omlish 0.0.0.dev316__py3-none-any.whl → 0.0.0.dev317__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- omlish/__about__.py +2 -2
- omlish/collections/utils.py +99 -5
- omlish/formats/json/literals.py +26 -10
- omlish/formats/json/rendering.py +1 -0
- omlish/formats/json5/rendering.py +44 -0
- omlish/lang/cached/function.py +66 -9
- omlish/lite/maybes.py +3 -1
- omlish/sockets/ports.py +24 -7
- omlish/testing/pytest/plugins/switches.py +103 -44
- {omlish-0.0.0.dev316.dist-info → omlish-0.0.0.dev317.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev316.dist-info → omlish-0.0.0.dev317.dist-info}/RECORD +15 -14
- {omlish-0.0.0.dev316.dist-info → omlish-0.0.0.dev317.dist-info}/WHEEL +1 -1
- {omlish-0.0.0.dev316.dist-info → omlish-0.0.0.dev317.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev316.dist-info → omlish-0.0.0.dev317.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev316.dist-info → omlish-0.0.0.dev317.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
omlish/collections/utils.py
CHANGED
@@ -58,13 +58,33 @@ def unique(
|
|
58
58
|
##
|
59
59
|
|
60
60
|
|
61
|
+
@ta.overload
|
62
|
+
def make_map(
|
63
|
+
kvs: ta.Iterable[tuple[K, V]],
|
64
|
+
*,
|
65
|
+
identity: ta.Literal[False] = False,
|
66
|
+
strict: bool = False,
|
67
|
+
) -> dict[K, V]:
|
68
|
+
...
|
69
|
+
|
70
|
+
|
71
|
+
@ta.overload
|
61
72
|
def make_map(
|
62
73
|
kvs: ta.Iterable[tuple[K, V]],
|
63
74
|
*,
|
64
75
|
identity: bool = False,
|
65
76
|
strict: bool = False,
|
66
77
|
) -> ta.MutableMapping[K, V]:
|
67
|
-
|
78
|
+
...
|
79
|
+
|
80
|
+
|
81
|
+
def make_map(
|
82
|
+
kvs,
|
83
|
+
*,
|
84
|
+
identity=False,
|
85
|
+
strict=False,
|
86
|
+
):
|
87
|
+
d: ta.MutableMapping = IdentityKeyDict() if identity else {}
|
68
88
|
for k, v in kvs:
|
69
89
|
if k in d:
|
70
90
|
if strict:
|
@@ -74,6 +94,21 @@ def make_map(
|
|
74
94
|
return d
|
75
95
|
|
76
96
|
|
97
|
+
#
|
98
|
+
|
99
|
+
|
100
|
+
@ta.overload
|
101
|
+
def make_map_by(
|
102
|
+
fn: ta.Callable[[V], K],
|
103
|
+
vs: ta.Iterable[V],
|
104
|
+
*,
|
105
|
+
identity: ta.Literal[False] = False,
|
106
|
+
strict: bool = False,
|
107
|
+
) -> dict[K, V]:
|
108
|
+
...
|
109
|
+
|
110
|
+
|
111
|
+
@ta.overload
|
77
112
|
def make_map_by(
|
78
113
|
fn: ta.Callable[[V], K],
|
79
114
|
vs: ta.Iterable[V],
|
@@ -81,6 +116,16 @@ def make_map_by(
|
|
81
116
|
identity: bool = False,
|
82
117
|
strict: bool = False,
|
83
118
|
) -> ta.MutableMapping[K, V]:
|
119
|
+
...
|
120
|
+
|
121
|
+
|
122
|
+
def make_map_by(
|
123
|
+
fn,
|
124
|
+
vs,
|
125
|
+
*,
|
126
|
+
identity=False,
|
127
|
+
strict=False,
|
128
|
+
):
|
84
129
|
return make_map(
|
85
130
|
((fn(v), v) for v in vs),
|
86
131
|
identity=identity,
|
@@ -91,9 +136,30 @@ def make_map_by(
|
|
91
136
|
##
|
92
137
|
|
93
138
|
|
94
|
-
|
95
|
-
|
96
|
-
|
139
|
+
@ta.overload
|
140
|
+
def multi_map(
|
141
|
+
kvs: ta.Iterable[tuple[K, V]],
|
142
|
+
*,
|
143
|
+
identity: ta.Literal[False] = False,
|
144
|
+
) -> dict[K, list[V]]:
|
145
|
+
...
|
146
|
+
|
147
|
+
|
148
|
+
@ta.overload
|
149
|
+
def multi_map(
|
150
|
+
kvs: ta.Iterable[tuple[K, V]],
|
151
|
+
*,
|
152
|
+
identity: bool = False,
|
153
|
+
) -> ta.MutableMapping[K, list[V]]:
|
154
|
+
...
|
155
|
+
|
156
|
+
|
157
|
+
def multi_map(
|
158
|
+
kvs,
|
159
|
+
*,
|
160
|
+
identity=False,
|
161
|
+
):
|
162
|
+
d: ta.MutableMapping = IdentityKeyDict() if identity else {}
|
97
163
|
for k, v in kvs:
|
98
164
|
try:
|
99
165
|
l = d[k]
|
@@ -103,7 +169,35 @@ def multi_map(kvs: ta.Iterable[tuple[K, V]], *, identity: bool = False) -> ta.Mu
|
|
103
169
|
return d
|
104
170
|
|
105
171
|
|
106
|
-
|
172
|
+
#
|
173
|
+
|
174
|
+
|
175
|
+
@ta.overload
|
176
|
+
def multi_map_by(
|
177
|
+
fn: ta.Callable[[V], K],
|
178
|
+
vs: ta.Iterable[V],
|
179
|
+
*,
|
180
|
+
identity: ta.Literal[False] = False,
|
181
|
+
) -> dict[K, list[V]]: # noqa
|
182
|
+
...
|
183
|
+
|
184
|
+
|
185
|
+
@ta.overload
|
186
|
+
def multi_map_by(
|
187
|
+
fn: ta.Callable[[V], K],
|
188
|
+
vs: ta.Iterable[V],
|
189
|
+
*,
|
190
|
+
identity: bool = False,
|
191
|
+
) -> ta.MutableMapping[K, list[V]]: # noqa
|
192
|
+
...
|
193
|
+
|
194
|
+
|
195
|
+
def multi_map_by(
|
196
|
+
fn,
|
197
|
+
vs,
|
198
|
+
*,
|
199
|
+
identity=False,
|
200
|
+
):
|
107
201
|
return multi_map(((fn(v), v) for v in vs), identity=identity)
|
108
202
|
|
109
203
|
|
omlish/formats/json/literals.py
CHANGED
@@ -29,7 +29,7 @@ import typing as ta
|
|
29
29
|
_ESCAPE_PAT = re.compile(r'[\x00-\x1f\\"]')
|
30
30
|
_ESCAPE_ASCII_PAT = re.compile(r'([\\"]|[^\ -~])')
|
31
31
|
|
32
|
-
|
32
|
+
ESCAPE_MAP: ta.Mapping[str, str] = {
|
33
33
|
**{
|
34
34
|
chr(i): f'\\u{i:04x}'
|
35
35
|
for i in range(0x20)
|
@@ -56,19 +56,35 @@ def _convert_to_string(s: str | bytes) -> str:
|
|
56
56
|
return s
|
57
57
|
|
58
58
|
|
59
|
-
def encode_string(
|
60
|
-
|
59
|
+
def encode_string(
|
60
|
+
s: str | bytes,
|
61
|
+
q: str = '"',
|
62
|
+
*,
|
63
|
+
escape_map: ta.Mapping[str, str] | None = None,
|
64
|
+
) -> str:
|
65
|
+
"""Return a JSON representation of a Python string."""
|
66
|
+
|
67
|
+
if escape_map is None:
|
68
|
+
escape_map = ESCAPE_MAP
|
61
69
|
|
62
70
|
s = _convert_to_string(s)
|
63
71
|
|
64
72
|
def replace(m):
|
65
|
-
return
|
73
|
+
return escape_map[m.group(0)]
|
66
74
|
|
67
75
|
return q + _ESCAPE_PAT.sub(replace, s) + q
|
68
76
|
|
69
77
|
|
70
|
-
def encode_string_ascii(
|
71
|
-
|
78
|
+
def encode_string_ascii(
|
79
|
+
s: str | bytes,
|
80
|
+
q: str = '"',
|
81
|
+
*,
|
82
|
+
escape_map: ta.Mapping[str, str] | None = None,
|
83
|
+
) -> str:
|
84
|
+
"""Return an ASCII-only JSON representation of a Python string."""
|
85
|
+
|
86
|
+
if escape_map is None:
|
87
|
+
escape_map = ESCAPE_MAP
|
72
88
|
|
73
89
|
s = _convert_to_string(s)
|
74
90
|
|
@@ -76,7 +92,7 @@ def encode_string_ascii(s: str | bytes, q: str = '"') -> str:
|
|
76
92
|
s = m.group(0)
|
77
93
|
|
78
94
|
try:
|
79
|
-
return
|
95
|
+
return escape_map[s]
|
80
96
|
|
81
97
|
except KeyError:
|
82
98
|
n = ord(s)
|
@@ -102,7 +118,7 @@ def _scan_four_digit_hex(
|
|
102
118
|
s: str,
|
103
119
|
end: int,
|
104
120
|
):
|
105
|
-
"""Scan a four digit hex number from s[end:end + 4]"""
|
121
|
+
"""Scan a four digit hex number from s[end:end + 4]."""
|
106
122
|
|
107
123
|
msg = 'Invalid \\uXXXX escape sequence'
|
108
124
|
esc = s[end: end + 4]
|
@@ -117,7 +133,7 @@ def _scan_four_digit_hex(
|
|
117
133
|
|
118
134
|
_STRING_CHUNK_PAT = re.compile(r'(.*?)(["\\\x00-\x1f])', re.VERBOSE | re.MULTILINE | re.DOTALL)
|
119
135
|
|
120
|
-
|
136
|
+
BACKSLASH_MAP: ta.Mapping[str, str] = {
|
121
137
|
'"': '"',
|
122
138
|
'\\': '\\',
|
123
139
|
'/': '/',
|
@@ -180,7 +196,7 @@ def parse_string(
|
|
180
196
|
# If not a unicode escape sequence, must be in the lookup table
|
181
197
|
if esc != 'u':
|
182
198
|
try:
|
183
|
-
char =
|
199
|
+
char = BACKSLASH_MAP[esc]
|
184
200
|
except KeyError:
|
185
201
|
msg = 'Invalid \\X escape sequence %r'
|
186
202
|
raise json.JSONDecodeError(msg, s, end) from None
|
omlish/formats/json/rendering.py
CHANGED
@@ -0,0 +1,44 @@
|
|
1
|
+
import typing as ta
|
2
|
+
|
3
|
+
from ..json import Scalar
|
4
|
+
from ..json.literals import ESCAPE_MAP
|
5
|
+
from ..json.literals import encode_string
|
6
|
+
from ..json.literals import encode_string_ascii
|
7
|
+
from ..json.rendering import JsonRenderer
|
8
|
+
from ..json.rendering import JsonRendererOut
|
9
|
+
|
10
|
+
|
11
|
+
##
|
12
|
+
|
13
|
+
|
14
|
+
MULTILINE_STRINGS_ESCAPE_MAP = {
|
15
|
+
**ESCAPE_MAP,
|
16
|
+
'\n': '\\n\\\n',
|
17
|
+
}
|
18
|
+
|
19
|
+
|
20
|
+
class Json5Renderer(JsonRenderer):
|
21
|
+
def __init__(
|
22
|
+
self,
|
23
|
+
out: JsonRendererOut,
|
24
|
+
*,
|
25
|
+
multiline_strings: bool = False,
|
26
|
+
**kwargs: ta.Any,
|
27
|
+
) -> None:
|
28
|
+
super().__init__(out, **kwargs)
|
29
|
+
|
30
|
+
self._multiline_strings = multiline_strings
|
31
|
+
|
32
|
+
def _format_scalar(self, o: Scalar) -> str:
|
33
|
+
if (
|
34
|
+
self._multiline_strings and
|
35
|
+
isinstance(o, str) and
|
36
|
+
'\n' in o
|
37
|
+
):
|
38
|
+
if self._ensure_ascii:
|
39
|
+
return encode_string_ascii(o, escape_map=MULTILINE_STRINGS_ESCAPE_MAP)
|
40
|
+
else:
|
41
|
+
return encode_string(o, escape_map=MULTILINE_STRINGS_ESCAPE_MAP)
|
42
|
+
|
43
|
+
else:
|
44
|
+
return super()._format_scalar(o)
|
omlish/lang/cached/function.py
CHANGED
@@ -22,6 +22,7 @@ TODO:
|
|
22
22
|
import dataclasses as dc
|
23
23
|
import functools
|
24
24
|
import inspect
|
25
|
+
import types
|
25
26
|
import typing as ta
|
26
27
|
|
27
28
|
from ..classes.abstract import Abstract
|
@@ -153,6 +154,10 @@ def _make_cache_key_maker(
|
|
153
154
|
##
|
154
155
|
|
155
156
|
|
157
|
+
class _CachedException(ta.NamedTuple):
|
158
|
+
ex: BaseException
|
159
|
+
|
160
|
+
|
156
161
|
class _CachedFunction(ta.Generic[T], Abstract):
|
157
162
|
@dc.dataclass(frozen=True, kw_only=True)
|
158
163
|
class Opts:
|
@@ -160,6 +165,19 @@ class _CachedFunction(ta.Generic[T], Abstract):
|
|
160
165
|
lock: DefaultLockable = None
|
161
166
|
transient: bool = False
|
162
167
|
no_wrapper_update: bool = False
|
168
|
+
cache_exceptions: type[BaseException] | tuple[type[BaseException], ...] | None = None
|
169
|
+
|
170
|
+
def __post_init__(self) -> None:
|
171
|
+
if (ce := self.cache_exceptions) is not None:
|
172
|
+
if isinstance(ce, type):
|
173
|
+
if not issubclass(ce, BaseException):
|
174
|
+
raise TypeError(ce)
|
175
|
+
elif isinstance(ce, tuple):
|
176
|
+
for e in ce:
|
177
|
+
if not issubclass(e, BaseException):
|
178
|
+
raise TypeError(e)
|
179
|
+
else:
|
180
|
+
raise TypeError(ce)
|
163
181
|
|
164
182
|
def __init__(
|
165
183
|
self,
|
@@ -199,13 +217,33 @@ class _CachedFunction(ta.Generic[T], Abstract):
|
|
199
217
|
def __call__(self, *args, **kwargs) -> T:
|
200
218
|
k = self._key_maker(*args, **kwargs)
|
201
219
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
220
|
+
if (ce := self._opts.cache_exceptions) is None:
|
221
|
+
try:
|
222
|
+
return self._values[k]
|
223
|
+
except KeyError:
|
224
|
+
pass
|
206
225
|
|
207
|
-
|
208
|
-
|
226
|
+
else:
|
227
|
+
try:
|
228
|
+
hit = self._values[k]
|
229
|
+
except KeyError:
|
230
|
+
pass
|
231
|
+
else:
|
232
|
+
if isinstance(hit, _CachedException):
|
233
|
+
raise hit.ex
|
234
|
+
else:
|
235
|
+
return hit
|
236
|
+
|
237
|
+
if ce is None:
|
238
|
+
def call_value_fn():
|
239
|
+
return self._value_fn(*args, **kwargs)
|
240
|
+
|
241
|
+
else:
|
242
|
+
def call_value_fn():
|
243
|
+
try:
|
244
|
+
return self._value_fn(*args, **kwargs)
|
245
|
+
except ce as ex: # type: ignore[misc]
|
246
|
+
return _CachedException(ex)
|
209
247
|
|
210
248
|
if self._lock is not None:
|
211
249
|
with self._lock:
|
@@ -220,7 +258,11 @@ class _CachedFunction(ta.Generic[T], Abstract):
|
|
220
258
|
value = call_value_fn()
|
221
259
|
|
222
260
|
self._values[k] = value
|
223
|
-
|
261
|
+
|
262
|
+
if isinstance(value, _CachedException):
|
263
|
+
raise value.ex
|
264
|
+
else:
|
265
|
+
return value
|
224
266
|
|
225
267
|
|
226
268
|
#
|
@@ -369,12 +411,27 @@ def cached_function(fn=None, **kwargs): # noqa
|
|
369
411
|
|
370
412
|
opts = _CachedFunction.Opts(**kwargs)
|
371
413
|
|
414
|
+
if isinstance(fn, types.MethodType):
|
415
|
+
return _FreeCachedFunction(
|
416
|
+
fn,
|
417
|
+
opts=opts,
|
418
|
+
key_maker=_make_cache_key_maker(fn, bound=True),
|
419
|
+
)
|
420
|
+
|
372
421
|
if isinstance(fn, staticmethod):
|
373
|
-
return _FreeCachedFunction(
|
422
|
+
return _FreeCachedFunction(
|
423
|
+
fn,
|
424
|
+
opts=opts,
|
425
|
+
value_fn=unwrap_func(fn),
|
426
|
+
)
|
374
427
|
|
375
428
|
scope = classmethod if isinstance(fn, classmethod) else None
|
376
429
|
|
377
|
-
return _DescriptorCachedFunction(
|
430
|
+
return _DescriptorCachedFunction(
|
431
|
+
fn,
|
432
|
+
scope,
|
433
|
+
opts=opts,
|
434
|
+
)
|
378
435
|
|
379
436
|
|
380
437
|
def static_init(fn: CallableT) -> CallableT:
|
omlish/lite/maybes.py
CHANGED
@@ -166,9 +166,11 @@ class _JustMaybe(_Maybe[T]):
|
|
166
166
|
def __repr__(self) -> str:
|
167
167
|
return f'just({self._v!r})'
|
168
168
|
|
169
|
+
_hash: int
|
170
|
+
|
169
171
|
def __hash__(self) -> int:
|
170
172
|
try:
|
171
|
-
return self._hash
|
173
|
+
return self._hash
|
172
174
|
except AttributeError:
|
173
175
|
pass
|
174
176
|
h = self._hash = hash((_JustMaybe, self._v))
|
omlish/sockets/ports.py
CHANGED
@@ -16,21 +16,38 @@ DEFAULT_AVAILABLE_PORT_HOST: str = '127.0.0.1'
|
|
16
16
|
|
17
17
|
|
18
18
|
@contextlib.contextmanager
|
19
|
-
def get_available_port_context(
|
19
|
+
def get_available_port_context(
|
20
|
+
host: ta.Optional[str] = None,
|
21
|
+
family: int = socket.AF_INET,
|
22
|
+
type: int = socket.SOCK_STREAM, # noqa
|
23
|
+
*,
|
24
|
+
listen: ta.Union[bool, int, None] = False,
|
25
|
+
) -> ta.Iterator[int]:
|
20
26
|
if host is None:
|
21
27
|
host = DEFAULT_AVAILABLE_PORT_HOST
|
22
28
|
|
23
|
-
|
29
|
+
if listen is not None:
|
30
|
+
if listen is True:
|
31
|
+
listen = 1
|
32
|
+
elif listen is False:
|
33
|
+
listen = None
|
34
|
+
else:
|
35
|
+
listen = check.isinstance(listen, int)
|
36
|
+
|
37
|
+
with socket.socket(family, type) as sock:
|
24
38
|
sock.bind((host, 0))
|
25
|
-
|
39
|
+
|
40
|
+
if listen is not None:
|
41
|
+
sock.listen(listen)
|
42
|
+
|
26
43
|
port = sock.getsockname()[1]
|
44
|
+
|
27
45
|
yield port
|
28
46
|
|
29
47
|
|
30
|
-
def get_available_port(
|
31
|
-
with get_available_port_context(
|
32
|
-
|
33
|
-
return port
|
48
|
+
def get_available_port(*args: ta.Any, **kwargs: ta.Any) -> int:
|
49
|
+
with get_available_port_context(*args, **kwargs) as port:
|
50
|
+
return port
|
34
51
|
|
35
52
|
|
36
53
|
def get_available_ports(
|
@@ -4,29 +4,87 @@ TODO:
|
|
4
4
|
- dynamic registration
|
5
5
|
- dynamic switching (skip docker if not running, skip online if not online, ...)
|
6
6
|
"""
|
7
|
+
import dataclasses as dc
|
7
8
|
import typing as ta
|
8
9
|
|
9
10
|
import pytest
|
10
11
|
|
11
12
|
from .... import check
|
12
13
|
from .... import collections as col
|
13
|
-
from .... import lang
|
14
14
|
from ....docker import all as docker
|
15
15
|
from ._registry import register
|
16
16
|
|
17
17
|
|
18
|
-
Configable = pytest.FixtureRequest | pytest.Config
|
18
|
+
Configable: ta.TypeAlias = pytest.FixtureRequest | pytest.Config
|
19
19
|
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
21
|
+
##
|
22
|
+
|
23
|
+
|
24
|
+
@dc.dataclass(frozen=True, eq=False)
|
25
|
+
class Switch:
|
26
|
+
name: str
|
27
|
+
_default_enabled: bool | ta.Callable[[], bool]
|
28
|
+
|
29
|
+
_: dc.KW_ONLY
|
30
|
+
|
31
|
+
add_marks: ta.Sequence[ta.Any] | None = None
|
32
|
+
|
33
|
+
def default_enabled(self) -> bool:
|
34
|
+
if isinstance(e := self._default_enabled, bool):
|
35
|
+
return e
|
36
|
+
elif callable(e):
|
37
|
+
return check.isinstance(e(), bool)
|
38
|
+
else:
|
39
|
+
raise TypeError(e)
|
40
|
+
|
41
|
+
@property
|
42
|
+
def attr(self) -> str:
|
43
|
+
return self.name.replace('-', '_')
|
44
|
+
|
45
|
+
|
46
|
+
SWITCHES: ta.Sequence[Switch] = [
|
47
|
+
Switch(
|
48
|
+
'name',
|
49
|
+
docker.has_cli,
|
50
|
+
),
|
51
|
+
|
52
|
+
Switch(
|
53
|
+
'docker-guest',
|
54
|
+
docker.is_likely_in_docker,
|
55
|
+
),
|
56
|
+
|
57
|
+
Switch(
|
58
|
+
'online',
|
59
|
+
True,
|
60
|
+
),
|
28
61
|
|
29
|
-
|
62
|
+
Switch(
|
63
|
+
'integration',
|
64
|
+
True,
|
65
|
+
),
|
66
|
+
|
67
|
+
Switch(
|
68
|
+
'high-mem',
|
69
|
+
True,
|
70
|
+
add_marks=[
|
71
|
+
# https://pytest-xdist.readthedocs.io/en/latest/distribution.html
|
72
|
+
pytest.mark.xdist_group('high-mem'),
|
73
|
+
],
|
74
|
+
),
|
75
|
+
|
76
|
+
Switch(
|
77
|
+
'slow',
|
78
|
+
False,
|
79
|
+
),
|
80
|
+
]
|
81
|
+
|
82
|
+
|
83
|
+
SWITCHES_BY_NAME: ta.Mapping[str, Switch] = col.make_map_by(lambda sw: sw.name, SWITCHES, strict=True)
|
84
|
+
SWITCHES_BY_ATTR: ta.Mapping[str, Switch] = col.make_map_by(lambda sw: sw.attr, SWITCHES, strict=True)
|
85
|
+
|
86
|
+
|
87
|
+
##
|
30
88
|
|
31
89
|
|
32
90
|
SwitchState: ta.TypeAlias = bool | ta.Literal['only']
|
@@ -49,7 +107,7 @@ def _get_obj_config(obj: Configable) -> pytest.Config:
|
|
49
107
|
|
50
108
|
def is_disabled(obj: Configable | None, name: str) -> bool:
|
51
109
|
check.isinstance(name, str)
|
52
|
-
check.in_(name,
|
110
|
+
check.in_(name, SWITCHES_BY_NAME)
|
53
111
|
return obj is not None and _get_obj_config(obj).getoption(f'--no-{name}')
|
54
112
|
|
55
113
|
|
@@ -58,17 +116,17 @@ def skip_if_disabled(obj: Configable | None, name: str) -> None:
|
|
58
116
|
pytest.skip(f'{name} disabled')
|
59
117
|
|
60
118
|
|
61
|
-
def get_specified_switches(obj: Configable) ->
|
62
|
-
ret: dict[
|
119
|
+
def get_specified_switches(obj: Configable) -> dict[Switch, SwitchState]:
|
120
|
+
ret: dict[Switch, SwitchState] = {}
|
63
121
|
for sw in SWITCHES:
|
64
122
|
sts = {
|
65
123
|
st
|
66
124
|
for st, pfx in SWITCH_STATE_OPT_PREFIXES.items()
|
67
|
-
if _get_obj_config(obj).getoption(pfx + sw)
|
125
|
+
if _get_obj_config(obj).getoption(pfx + sw.name)
|
68
126
|
}
|
69
127
|
if sts:
|
70
128
|
if len(sts) > 1:
|
71
|
-
raise Exception(f'Multiple switches specified for {sw}')
|
129
|
+
raise Exception(f'Multiple switches specified for {sw.name}')
|
72
130
|
ret[sw] = check.single(sts)
|
73
131
|
return ret
|
74
132
|
|
@@ -76,49 +134,50 @@ def get_specified_switches(obj: Configable) -> ta.Mapping[str, SwitchState]:
|
|
76
134
|
@register
|
77
135
|
class SwitchesPlugin:
|
78
136
|
def pytest_configure(self, config):
|
79
|
-
for sw in
|
80
|
-
config.addinivalue_line('markers', f'{sw}: mark test as {sw}')
|
81
|
-
config.addinivalue_line('markers', f'not_{sw}: mark test as not {sw}')
|
137
|
+
for sw in SWITCHES:
|
138
|
+
config.addinivalue_line('markers', f'{sw.attr}: mark test as {sw.attr}')
|
139
|
+
config.addinivalue_line('markers', f'not_{sw.attr}: mark test as not {sw.attr}')
|
82
140
|
|
83
141
|
def pytest_addoption(self, parser):
|
84
142
|
for sw in SWITCHES:
|
85
|
-
parser.addoption(f'--no-{sw}', action='store_true', default=False, help=f'disable {sw} tests')
|
86
|
-
parser.addoption(f'--{sw}', action='store_true', default=False, help=f'enables {sw} tests')
|
87
|
-
parser.addoption(f'--only-{sw}', action='store_true', default=False, help=f'enables only {sw} tests')
|
88
|
-
|
89
|
-
@lang.cached_function
|
90
|
-
def get_switches(self) -> ta.Mapping[str, SwitchState]:
|
91
|
-
return {
|
92
|
-
k: v() if callable(v) else v
|
93
|
-
for k, v in SWITCHES.items()
|
94
|
-
}
|
143
|
+
parser.addoption(f'--no-{sw.name}', action='store_true', default=False, help=f'disable {sw.name} tests')
|
144
|
+
parser.addoption(f'--{sw.name}', action='store_true', default=False, help=f'enables {sw.name} tests')
|
145
|
+
parser.addoption(f'--only-{sw.name}', action='store_true', default=False, help=f'enables only {sw.name} tests') # noqa
|
95
146
|
|
96
147
|
def pytest_collection_modifyitems(self, config, items):
|
97
|
-
|
98
|
-
**
|
148
|
+
switch_states: dict[Switch, SwitchState] = {
|
149
|
+
**{
|
150
|
+
sw: sw.default_enabled()
|
151
|
+
for sw in SWITCHES
|
152
|
+
},
|
99
153
|
**get_specified_switches(config),
|
100
154
|
}
|
101
155
|
|
102
|
-
|
103
|
-
|
156
|
+
inv_switch_states: dict[SwitchState, list[Switch]] = col.multi_map((st, sw) for sw, st in switch_states.items())
|
157
|
+
true_switches = frozenset(inv_switch_states.get(True, ()))
|
158
|
+
false_switches = frozenset(inv_switch_states.get(False, ()))
|
159
|
+
only_switches = frozenset(inv_switch_states.get('only', ()))
|
104
160
|
|
105
161
|
def process(item):
|
106
|
-
|
107
|
-
|
162
|
+
item_switches = {sw for sw in SWITCHES if sw.attr in item.keywords}
|
163
|
+
item_not_switches = {sw for sw in SWITCHES if ('not_' + sw.attr) in item.keywords}
|
164
|
+
|
165
|
+
for sw in item_switches:
|
166
|
+
for mk in sw.add_marks or []:
|
167
|
+
item.add_marker(mk)
|
108
168
|
|
109
|
-
if
|
110
|
-
if not any(sw in
|
111
|
-
item.add_marker(pytest.mark.skip(reason=f'skipping switches {
|
169
|
+
if only_switches:
|
170
|
+
if not any(sw in only_switches for sw in item_switches):
|
171
|
+
item.add_marker(pytest.mark.skip(reason=f'skipping switches {item_switches}'))
|
112
172
|
return
|
113
173
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
item.add_marker(pytest.mark.skip(reason=f'skipping switches {sw}'))
|
174
|
+
for sw in item_switches:
|
175
|
+
if sw in false_switches:
|
176
|
+
item.add_marker(pytest.mark.skip(reason=f'skipping switches {sw}'))
|
118
177
|
|
119
|
-
|
120
|
-
|
121
|
-
|
178
|
+
for sw in item_not_switches:
|
179
|
+
if sw in true_switches:
|
180
|
+
item.add_marker(pytest.mark.skip(reason=f'skipping switches {sw}'))
|
122
181
|
|
123
182
|
for item in items:
|
124
183
|
process(item)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
omlish/.manifests.json,sha256=orgsRvtpHu8tdhaCvlP9v3P495OJopYYiHKjK68WtWg,8587
|
2
|
-
omlish/__about__.py,sha256=
|
2
|
+
omlish/__about__.py,sha256=XvJduE7LyT8sT8wf4Tcj6mGwYJh0_bub6Rmk7aliJpA,3478
|
3
3
|
omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
|
4
4
|
omlish/c3.py,sha256=rer-TPOFDU6fYq_AWio_AmA-ckZ8JDY5shIzQ_yXfzA,8414
|
5
5
|
omlish/cached.py,sha256=MLap_p0rdGoDIMVhXVHm1tsbcWobJF0OanoodV03Ju8,542
|
@@ -87,7 +87,7 @@ omlish/collections/mappings.py,sha256=iXb7oq1rCQak0KgzblgrzWCJLrkfJAYHFvl9lprOVU
|
|
87
87
|
omlish/collections/ordered.py,sha256=7zTbrAt12rf6i33XHkQERKar258fJacaw_WbtGEBgWo,2338
|
88
88
|
omlish/collections/ranked.py,sha256=McB8C2UQfUvrbmxGTpBz1-EZuyCLkBFtktzncMdt8_Y,2287
|
89
89
|
omlish/collections/unmodifiable.py,sha256=X7kKhPFdZF4m28SfLDxZL-riWlhbZffdPv35aTP30YM,4753
|
90
|
-
omlish/collections/utils.py,sha256=
|
90
|
+
omlish/collections/utils.py,sha256=1LED_KLXsEb39Byhql7BD5ObHoSkFn2OyUdnHsKXKNo,4091
|
91
91
|
omlish/collections/cache/__init__.py,sha256=D1gO71VcwxFTZP9gAc9isHfg_TEdalwhsJcgGLvS9hg,233
|
92
92
|
omlish/collections/cache/descriptor.py,sha256=F9aRsF-xOUcNMzTwIWrUpKTjop1Z-oItQqJDwVaR0FU,5026
|
93
93
|
omlish/collections/cache/impl.py,sha256=Y18OcAsNL6dIWuk89UlZBpqq0iBU-P4VDio6eis43Us,14760
|
@@ -264,8 +264,8 @@ omlish/formats/json/codecs.py,sha256=E5KErfqsgGZq763ixXLT3qysbk5MIsypT92xG5aSaIs
|
|
264
264
|
omlish/formats/json/consts.py,sha256=A0cTAGGLyjo-gcYIQrL4JIaardI0yPMhQoNmh42BaRg,387
|
265
265
|
omlish/formats/json/encoding.py,sha256=iwoYyJePibgvRDZ6e9b2OlXmOBJEczquRNoiffVf3hE,502
|
266
266
|
omlish/formats/json/json.py,sha256=Mdqv2vdMi7gp96eV0BIYH5UdWpjWfsh-tSMZeywG-08,331
|
267
|
-
omlish/formats/json/literals.py,sha256=
|
268
|
-
omlish/formats/json/rendering.py,sha256=
|
267
|
+
omlish/formats/json/literals.py,sha256=MMou9UIoztL9EyX2Zhv3Kzfq8eaKMfGNCFhL16n-fTk,7532
|
268
|
+
omlish/formats/json/rendering.py,sha256=0S_ppr5ihkUW7PP0mipZXHr72otqwUCNfanwopcboag,4571
|
269
269
|
omlish/formats/json/types.py,sha256=ueO9-uOU2eVWowJf0LH1fHFLjZ6fTIZyq9qybcLQaiQ,147
|
270
270
|
omlish/formats/json/backends/__init__.py,sha256=gnaNDCxy_KmmPUPDnjxO5_WjuWxLGbI9FYWx8ZJuQUU,97
|
271
271
|
omlish/formats/json/backends/base.py,sha256=WqtyoM82pyM0NyqpPwndrebr1bUVU1QlpmVQNrcAO8c,1114
|
@@ -287,6 +287,7 @@ omlish/formats/json5/codec.py,sha256=ldnxCRo0JP1fkGLt0mMxJlLvNxqIF_1KUCcSp1HtI-M
|
|
287
287
|
omlish/formats/json5/errors.py,sha256=AHkR9ySjAoQdUrizpqgL8fg0M5oe5mHZkml3KZHEvC4,38
|
288
288
|
omlish/formats/json5/literals.py,sha256=rj4-9KFXfgdq5oK_eUkp57cgoMQ8T0gRaG9ga430he4,2429
|
289
289
|
omlish/formats/json5/parsing.py,sha256=CWJHfe_FXCQuyDk00a7PI5wOdROq7Tc3fbWrNuwaKCw,2346
|
290
|
+
omlish/formats/json5/rendering.py,sha256=Kf-yA4AB3BqyjellBOFTGrMxxpIUsmgHIFzVRSJkQnE,1112
|
290
291
|
omlish/formats/json5/_antlr/Json5Lexer.py,sha256=LnURBD0tL35VHNpxqM4DOPrTkE_9oONHtbftRM26tak,23239
|
291
292
|
omlish/formats/json5/_antlr/Json5Listener.py,sha256=37Jhzu7tQEw6gLIxtcZtV5Yjn08kS-GkxF7d0KjXyo8,2097
|
292
293
|
omlish/formats/json5/_antlr/Json5Parser.py,sha256=YU9bP89nq97A8ZCrjIcbgoBg4z76-N-6IMFGQe_ws54,19679
|
@@ -426,7 +427,7 @@ omlish/lang/strings.py,sha256=kJmRFd1D36xXcjd9MdB12BCwF_-MVhNr-TpWj7hMi_4,4252
|
|
426
427
|
omlish/lang/sys.py,sha256=b4qOPiJZQru_mbb04FNfOjYWUxlV2becZOoc-yya_rQ,411
|
427
428
|
omlish/lang/typing.py,sha256=Zdad9Zv0sa-hIaUXPrzPidT7sDVpRcussAI7D-j-I1c,3296
|
428
429
|
omlish/lang/cached/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
429
|
-
omlish/lang/cached/function.py,sha256=
|
430
|
+
omlish/lang/cached/function.py,sha256=tch6VlDljfUUFMP61RY0LYXYyNwj1Sy8CAQQQMHCypA,11344
|
430
431
|
omlish/lang/cached/property.py,sha256=WHYyg4-6EA86TcNMfbXTjVhjEZPc0kngt9hfY3WN5w8,2768
|
431
432
|
omlish/lang/classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
432
433
|
omlish/lang/classes/abstract.py,sha256=n4rDlDraUKxPF0GtOWEFZ6mEzEDmP7Z8LSI6Jww_thw,3715
|
@@ -452,7 +453,7 @@ omlish/lite/inject.py,sha256=-tTsOqqef-Ix5Tgl2DP_JAsNWJQDFUptERl3lk14Uzs,29007
|
|
452
453
|
omlish/lite/json.py,sha256=7-02Ny4fq-6YAu5ynvqoijhuYXWpLmfCI19GUeZnb1c,740
|
453
454
|
omlish/lite/logs.py,sha256=CWFG0NKGhqNeEgryF5atN2gkPYbUdTINEw_s1phbINM,51
|
454
455
|
omlish/lite/marshal.py,sha256=4DCbLoimLwJakihpvMjJ_kpc7v9aZQY8P3-gkoqEGUE,18471
|
455
|
-
omlish/lite/maybes.py,sha256=
|
456
|
+
omlish/lite/maybes.py,sha256=hAIzp2pmPccnULwKiA8vaxiTJj6av3dvrqhhDfMLte0,4353
|
456
457
|
omlish/lite/pycharm.py,sha256=pUOJevrPClSqTCEOkQBO11LKX2003tfDcp18a03QFrc,1163
|
457
458
|
omlish/lite/reflect.py,sha256=pzOY2PPuHH0omdtglkN6DheXDrGopdL3PtTJnejyLFU,2189
|
458
459
|
omlish/lite/reprs.py,sha256=QI5VBtvq_TW1TojWL25c04QfOABLi8Smt5jc5J-bArc,2008
|
@@ -580,7 +581,7 @@ omlish/sockets/addresses.py,sha256=vbVeQBkzI513H4vRv-JS89QtRbr9U8v5zqkm3oODl_s,1
|
|
580
581
|
omlish/sockets/bind.py,sha256=J1SfFFFnVf3H5nqESDX2NGEY8DmjyIMUXZciZM33zQY,8003
|
581
582
|
omlish/sockets/handlers.py,sha256=Gj6xZoo4vommge8XvkehYw3B7O4aql2P4qzZIIa0p24,462
|
582
583
|
omlish/sockets/io.py,sha256=lfhTkB7NnAIx9kuQhAkwgsEUXY78Mp1_WtYrIQNS_k8,1408
|
583
|
-
omlish/sockets/ports.py,sha256=
|
584
|
+
omlish/sockets/ports.py,sha256=6sp1jVTYuhuAGQ9NB12sFklPkygLhhrNMO9Wa9PcFeU,1772
|
584
585
|
omlish/sockets/wait.py,sha256=OtLbcoLYzksl4GU0TNbQpzzVWGxp2iRLgo2cGZZfFFM,1559
|
585
586
|
omlish/sockets/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
586
587
|
omlish/sockets/server/handlers.py,sha256=PPsb1X5oU9dN8jfztaMGsRiqWTyEANT-1aSLbS6bUVg,3867
|
@@ -748,7 +749,7 @@ omlish/testing/pytest/plugins/pydevd.py,sha256=5moE64LrNRV6gKRaeCuONDiwuh-4UFxJP
|
|
748
749
|
omlish/testing/pytest/plugins/repeat.py,sha256=jiZCI-17042jBvUEbZCxNwqWtr7s3EhTBSeSHh_Uz4E,497
|
749
750
|
omlish/testing/pytest/plugins/skips.py,sha256=eMir_n777Kk2BvOwjQzRROKL4iAMYKSHFwWCHJ-bdPI,1040
|
750
751
|
omlish/testing/pytest/plugins/spacing.py,sha256=tzq-L-exegHe2BImumkYIsVcUwpUUhLJJOuSrsKDuCU,706
|
751
|
-
omlish/testing/pytest/plugins/switches.py,sha256=
|
752
|
+
omlish/testing/pytest/plugins/switches.py,sha256=azw-pqSQfZYs7YlKsPK1bzerg20pX55YXejZgIxJu9U,5166
|
752
753
|
omlish/testing/pytest/plugins/utils.py,sha256=L5C622UXcA_AUKDcvyh5IMiRfqSGGz0McdhwZWvfMlU,261
|
753
754
|
omlish/testing/pytest/plugins/asyncs/__init__.py,sha256=TTNhFmP_krug1973sq_bpWBTIvg68-1nbuVLSs92Z6k,41
|
754
755
|
omlish/testing/pytest/plugins/asyncs/consts.py,sha256=0NOCkzV43dOu3u97BqYMQ4mPG8JuFncpWibkOZpCqX4,55
|
@@ -852,9 +853,9 @@ omlish/typedvalues/holder.py,sha256=ZTnHiw-K38ciOBLEdwgrltr7Xp8jjEs_0Lp69DH-G-o,
|
|
852
853
|
omlish/typedvalues/marshal.py,sha256=hWHRLcrGav7lvXJDtb9bNI0ickl4SKPQ6F4BbTpqw3A,4219
|
853
854
|
omlish/typedvalues/reflect.py,sha256=Ih1YgU-srUjsvBn_P7C66f73_VCvcwqE3ffeBnZBgt4,674
|
854
855
|
omlish/typedvalues/values.py,sha256=ym46I-q2QJ_6l4UlERqv3yj87R-kp8nCKMRph0xQ3UA,1307
|
855
|
-
omlish-0.0.0.
|
856
|
-
omlish-0.0.0.
|
857
|
-
omlish-0.0.0.
|
858
|
-
omlish-0.0.0.
|
859
|
-
omlish-0.0.0.
|
860
|
-
omlish-0.0.0.
|
856
|
+
omlish-0.0.0.dev317.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
857
|
+
omlish-0.0.0.dev317.dist-info/METADATA,sha256=4Z0c7koet4xBZgji9URV0S85ozghSxQShBteed3cg4s,4416
|
858
|
+
omlish-0.0.0.dev317.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
859
|
+
omlish-0.0.0.dev317.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
|
860
|
+
omlish-0.0.0.dev317.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
|
861
|
+
omlish-0.0.0.dev317.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|