omlish 0.0.0.dev338__py3-none-any.whl → 0.0.0.dev340__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/asyncs/bluelet/LICENSE +1 -1
- omlish/asyncs/bluelet/api.py +1 -1
- omlish/asyncs/bluelet/core.py +1 -1
- omlish/asyncs/bluelet/events.py +1 -1
- omlish/asyncs/bluelet/files.py +1 -1
- omlish/asyncs/bluelet/runner.py +1 -1
- omlish/asyncs/bluelet/sockets.py +1 -1
- omlish/collections/__init__.py +19 -0
- omlish/collections/multimaps.py +151 -0
- omlish/formats/json/__init__.py +13 -13
- omlish/formats/json/backends/__init__.py +2 -2
- omlish/formats/json/backends/default.py +56 -12
- omlish/formats/json/backends/orjson.py +6 -5
- omlish/formats/json/backends/std.py +4 -1
- omlish/formats/json/backends/ujson.py +6 -5
- omlish/formats/json/codecs.py +4 -4
- omlish/graphs/dags.py +112 -48
- omlish/graphs/domination.py +5 -1
- omlish/graphs/dot/items.py +3 -0
- omlish/graphs/dot/make.py +3 -0
- omlish/graphs/dot/rendering.py +3 -0
- omlish/graphs/dot/utils.py +3 -0
- omlish/graphs/trees.py +5 -4
- omlish/lang/__init__.py +9 -0
- omlish/lang/classes/bindable.py +43 -0
- omlish/lang/classes/protocols.py +26 -0
- omlish/math/__init__.py +15 -15
- omlish/math/fixed.py +25 -15
- omlish/os/forkhooks.py +4 -4
- omlish/testing/pytest/plugins/switches/plugin.py +2 -0
- omlish/testing/pytest/skip.py +7 -8
- omlish/typedvalues/__init__.py +4 -0
- omlish/typedvalues/accessor.py +5 -1
- omlish/typedvalues/collection.py +1 -0
- omlish/typedvalues/of_.py +51 -0
- {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/RECORD +42 -39
- omlish/formats/json/json.py +0 -17
- {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/top_level.txt +0 -0
omlish/graphs/dot/items.py
CHANGED
omlish/graphs/dot/make.py
CHANGED
omlish/graphs/dot/rendering.py
CHANGED
omlish/graphs/dot/utils.py
CHANGED
omlish/graphs/trees.py
CHANGED
@@ -10,13 +10,14 @@ from .. import cached
|
|
10
10
|
from .. import check
|
11
11
|
from .. import collections as col
|
12
12
|
from .. import lang
|
13
|
-
from ..algorithm import
|
13
|
+
from ..algorithm.toposort import mut_toposort
|
14
14
|
|
15
15
|
|
16
16
|
T = ta.TypeVar('T')
|
17
17
|
NodeT = ta.TypeVar('NodeT')
|
18
|
-
|
19
|
-
|
18
|
+
|
19
|
+
NodeWalker: ta.TypeAlias = ta.Callable[[NodeT], ta.Iterable[NodeT]]
|
20
|
+
NodeGenerator: ta.TypeAlias = ta.Generator[NodeT, None, None]
|
20
21
|
|
21
22
|
|
22
23
|
##
|
@@ -200,7 +201,7 @@ class BasicTreeAnalysis(ta.Generic[NodeT]):
|
|
200
201
|
else:
|
201
202
|
e, d = lang.identity, lang.identity
|
202
203
|
tsd = {e(n): {e(p)} for n, p in parents_by_node.items()}
|
203
|
-
ts = list(
|
204
|
+
ts = list(mut_toposort(tsd))
|
204
205
|
root = d(check.single(ts[0]))
|
205
206
|
|
206
207
|
return cls(
|
omlish/lang/__init__.py
CHANGED
@@ -61,12 +61,21 @@ from .classes.abstract import ( # noqa
|
|
61
61
|
unabstract_class,
|
62
62
|
)
|
63
63
|
|
64
|
+
from .classes.bindable import ( # noqa
|
65
|
+
BindableClass,
|
66
|
+
)
|
67
|
+
|
64
68
|
from .classes.namespaces import ( # noqa
|
65
69
|
GenericNamespaceMeta,
|
66
70
|
Namespace,
|
67
71
|
NamespaceMeta,
|
68
72
|
)
|
69
73
|
|
74
|
+
from .classes.protocols import ( # noqa
|
75
|
+
ProtocolForbiddenAsBaseClass,
|
76
|
+
ProtocolForbiddenAsBaseClassTypeError,
|
77
|
+
)
|
78
|
+
|
70
79
|
from .classes.restrict import ( # noqa
|
71
80
|
AnySensitive,
|
72
81
|
Final,
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import typing as ta
|
2
|
+
|
3
|
+
|
4
|
+
T = ta.TypeVar('T')
|
5
|
+
|
6
|
+
|
7
|
+
##
|
8
|
+
|
9
|
+
|
10
|
+
class _ClassOrInstanceMethod:
|
11
|
+
def __init__(self, func):
|
12
|
+
super().__init__()
|
13
|
+
|
14
|
+
self.__func__ = func
|
15
|
+
|
16
|
+
def __get__(self, instance, owner=None):
|
17
|
+
return self.__func__.__get__(instance if instance is not None else owner)
|
18
|
+
|
19
|
+
|
20
|
+
class BindableClass(ta.Generic[T]):
|
21
|
+
# FIXME: apparently can't have TypeVars in ClassVars, but could stick in a @classmethod (which gets transformed)...
|
22
|
+
_bound: ta.ClassVar[type[T] | None] = None # type: ignore[misc]
|
23
|
+
|
24
|
+
def __init__(self, *, _bound):
|
25
|
+
super().__init__()
|
26
|
+
|
27
|
+
setattr(self, '_bound', _bound)
|
28
|
+
|
29
|
+
def __class_getitem__(cls, *args, **kwargs):
|
30
|
+
# FIXME: this could handle __mro_items__ to be subclassable, but it's not currently really intended to be
|
31
|
+
# subclassed
|
32
|
+
if cls is BindableClass:
|
33
|
+
return super().__class_getitem__(*args, **kwargs) # type: ignore[misc]
|
34
|
+
|
35
|
+
[bind_cls] = args
|
36
|
+
return cls(_bound=bind_cls)
|
37
|
+
|
38
|
+
def __init_subclass__(cls, **kwargs):
|
39
|
+
super().__init_subclass__(**kwargs)
|
40
|
+
|
41
|
+
for k, v in cls.__dict__.items():
|
42
|
+
if isinstance(v, classmethod):
|
43
|
+
setattr(cls, k, _ClassOrInstanceMethod(v.__func__))
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import typing as ta
|
2
|
+
|
3
|
+
|
4
|
+
##
|
5
|
+
|
6
|
+
|
7
|
+
class ProtocolForbiddenAsBaseClass(ta.Protocol):
|
8
|
+
pass
|
9
|
+
|
10
|
+
|
11
|
+
class ProtocolForbiddenAsBaseClassTypeError(TypeError):
|
12
|
+
pass
|
13
|
+
|
14
|
+
|
15
|
+
def _ProtocolForbiddenAsBaseClass__init_subclass__(cls: ta.Any, **kwargs: ta.Any) -> None: # noqa
|
16
|
+
if ta.Protocol not in cls.__mro__:
|
17
|
+
raise TypeError(f'Class {cls} must be a Protocol')
|
18
|
+
|
19
|
+
super(ProtocolForbiddenAsBaseClass, cls).__init_subclass__(**kwargs) # noqa
|
20
|
+
|
21
|
+
# TODO: ta.Protocol not in cls.__bases__ ?
|
22
|
+
if not cls.__dict__['_is_protocol']:
|
23
|
+
raise ProtocolForbiddenAsBaseClassTypeError(cls)
|
24
|
+
|
25
|
+
|
26
|
+
setattr(ProtocolForbiddenAsBaseClass, '__init_subclass__', classmethod(_ProtocolForbiddenAsBaseClass__init_subclass__))
|
omlish/math/__init__.py
CHANGED
@@ -35,11 +35,11 @@ from .fixed import ( # noqa
|
|
35
35
|
CheckedInt64,
|
36
36
|
CheckedInt128,
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
CheckedUint8,
|
39
|
+
CheckedUint16,
|
40
|
+
CheckedUint32,
|
41
|
+
CheckedUint64,
|
42
|
+
CheckedUint128,
|
43
43
|
|
44
44
|
ClampedInt8,
|
45
45
|
ClampedInt16,
|
@@ -47,11 +47,11 @@ from .fixed import ( # noqa
|
|
47
47
|
ClampedInt64,
|
48
48
|
ClampedInt128,
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
50
|
+
ClampedUint8,
|
51
|
+
ClampedUint16,
|
52
|
+
ClampedUint32,
|
53
|
+
ClampedUint64,
|
54
|
+
ClampedUint128,
|
55
55
|
|
56
56
|
WrappedInt8,
|
57
57
|
WrappedInt16,
|
@@ -59,11 +59,11 @@ from .fixed import ( # noqa
|
|
59
59
|
WrappedInt64,
|
60
60
|
WrappedInt128,
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
62
|
+
WrappedUint8,
|
63
|
+
WrappedUint16,
|
64
|
+
WrappedUint32,
|
65
|
+
WrappedUint64,
|
66
|
+
WrappedUint128,
|
67
67
|
)
|
68
68
|
|
69
69
|
from .floats import ( # noqa
|
omlish/math/fixed.py
CHANGED
@@ -183,6 +183,16 @@ class FixedWidthInt(int, lang.Abstract):
|
|
183
183
|
locals()[_proxy_name] = _gen_tuple_proxy_method(_proxy_name)
|
184
184
|
del _proxy_name
|
185
185
|
|
186
|
+
#
|
187
|
+
|
188
|
+
def __invert__(self) -> ta.Self:
|
189
|
+
if not self.SIGNED:
|
190
|
+
return self.__class__(~int(self) & self.MASK)
|
191
|
+
else:
|
192
|
+
return self.__class__(super().__invert__())
|
193
|
+
|
194
|
+
#
|
195
|
+
|
186
196
|
def __repr__(self) -> str:
|
187
197
|
return f'{self.__class__.__name__}({int(self)})'
|
188
198
|
|
@@ -278,23 +288,23 @@ class CheckedInt128(CheckedInt, SignedInt, AnyInt128):
|
|
278
288
|
#
|
279
289
|
|
280
290
|
|
281
|
-
class
|
291
|
+
class CheckedUint8(CheckedInt, UnsignedInt, AnyInt8):
|
282
292
|
pass
|
283
293
|
|
284
294
|
|
285
|
-
class
|
295
|
+
class CheckedUint16(CheckedInt, UnsignedInt, AnyInt16):
|
286
296
|
pass
|
287
297
|
|
288
298
|
|
289
|
-
class
|
299
|
+
class CheckedUint32(CheckedInt, UnsignedInt, AnyInt32):
|
290
300
|
pass
|
291
301
|
|
292
302
|
|
293
|
-
class
|
303
|
+
class CheckedUint64(CheckedInt, UnsignedInt, AnyInt64):
|
294
304
|
pass
|
295
305
|
|
296
306
|
|
297
|
-
class
|
307
|
+
class CheckedUint128(CheckedInt, UnsignedInt, AnyInt128):
|
298
308
|
pass
|
299
309
|
|
300
310
|
|
@@ -324,23 +334,23 @@ class ClampedInt128(ClampedInt, SignedInt, AnyInt128):
|
|
324
334
|
#
|
325
335
|
|
326
336
|
|
327
|
-
class
|
337
|
+
class ClampedUint8(ClampedInt, UnsignedInt, AnyInt8):
|
328
338
|
pass
|
329
339
|
|
330
340
|
|
331
|
-
class
|
341
|
+
class ClampedUint16(ClampedInt, UnsignedInt, AnyInt16):
|
332
342
|
pass
|
333
343
|
|
334
344
|
|
335
|
-
class
|
345
|
+
class ClampedUint32(ClampedInt, UnsignedInt, AnyInt32):
|
336
346
|
pass
|
337
347
|
|
338
348
|
|
339
|
-
class
|
349
|
+
class ClampedUint64(ClampedInt, UnsignedInt, AnyInt64):
|
340
350
|
pass
|
341
351
|
|
342
352
|
|
343
|
-
class
|
353
|
+
class ClampedUint128(ClampedInt, UnsignedInt, AnyInt128):
|
344
354
|
pass
|
345
355
|
|
346
356
|
|
@@ -370,21 +380,21 @@ class WrappedInt128(WrappedInt, SignedInt, AnyInt128):
|
|
370
380
|
#
|
371
381
|
|
372
382
|
|
373
|
-
class
|
383
|
+
class WrappedUint8(WrappedInt, UnsignedInt, AnyInt8):
|
374
384
|
pass
|
375
385
|
|
376
386
|
|
377
|
-
class
|
387
|
+
class WrappedUint16(WrappedInt, UnsignedInt, AnyInt16):
|
378
388
|
pass
|
379
389
|
|
380
390
|
|
381
|
-
class
|
391
|
+
class WrappedUint32(WrappedInt, UnsignedInt, AnyInt32):
|
382
392
|
pass
|
383
393
|
|
384
394
|
|
385
|
-
class
|
395
|
+
class WrappedUint64(WrappedInt, UnsignedInt, AnyInt64):
|
386
396
|
pass
|
387
397
|
|
388
398
|
|
389
|
-
class
|
399
|
+
class WrappedUint128(WrappedInt, UnsignedInt, AnyInt128):
|
390
400
|
pass
|
omlish/os/forkhooks.py
CHANGED
@@ -36,9 +36,9 @@ class _ForkHookManager:
|
|
36
36
|
|
37
37
|
#
|
38
38
|
|
39
|
-
_hooks_by_key: ta.ClassVar[ta.Dict[
|
39
|
+
_hooks_by_key: ta.ClassVar[ta.Dict[ta.Any, Hook]] = {}
|
40
40
|
|
41
|
-
_hook_keys: ta.ClassVar[ta.FrozenSet[
|
41
|
+
_hook_keys: ta.ClassVar[ta.FrozenSet[ta.Any]] = frozenset()
|
42
42
|
_priority_ordered_hooks: ta.ClassVar[ta.List[Hook]] = []
|
43
43
|
|
44
44
|
@classmethod
|
@@ -191,7 +191,7 @@ class ForkHook(abc.ABC): # noqa
|
|
191
191
|
##
|
192
192
|
|
193
193
|
|
194
|
-
class
|
194
|
+
class ForkDepthTracker(ForkHook):
|
195
195
|
_hook_priority = -1000
|
196
196
|
|
197
197
|
_fork_depth: ta.ClassVar[int] = 0
|
@@ -208,7 +208,7 @@ class _ForkDepthTracker(ForkHook):
|
|
208
208
|
|
209
209
|
|
210
210
|
def get_fork_depth() -> int:
|
211
|
-
return
|
211
|
+
return ForkDepthTracker.get_fork_depth()
|
212
212
|
|
213
213
|
|
214
214
|
##
|
@@ -3,6 +3,7 @@ TODO:
|
|
3
3
|
- inheritance
|
4
4
|
- dynamic registration
|
5
5
|
- dynamic switching (skip docker if not running, skip online if not online, ...)
|
6
|
+
- probably make IF_SINGLE understand parametErization
|
6
7
|
"""
|
7
8
|
import dataclasses as dc
|
8
9
|
import typing as ta
|
@@ -121,6 +122,7 @@ class SwitchesPlugin:
|
|
121
122
|
def pytest_sessionstart(self, session):
|
122
123
|
session.stash[self._states_key] = self._States(session)
|
123
124
|
|
125
|
+
@pytest.hookimpl(tryfirst=True)
|
124
126
|
def pytest_collection_modifyitems(self, config, items):
|
125
127
|
def process_item(item):
|
126
128
|
state: SwitchesPlugin._States = item.session.stash[self._states_key]
|
omlish/testing/pytest/skip.py
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
"""
|
2
|
+
TODO:
|
3
|
+
- if_not_single? switches does this
|
4
|
+
"""
|
1
5
|
import shutil
|
2
6
|
import sys
|
3
7
|
import sysconfig
|
@@ -11,8 +15,9 @@ from ... import lang
|
|
11
15
|
##
|
12
16
|
|
13
17
|
|
14
|
-
def if_cant_import(
|
15
|
-
|
18
|
+
def if_cant_import(*modules: str, **kwargs):
|
19
|
+
missing = [m for m in modules if not lang.can_import(m, **kwargs)]
|
20
|
+
return pytest.mark.skipif(bool(missing), reason=f'requires import {", ".join(missing)}')
|
16
21
|
|
17
22
|
|
18
23
|
def if_not_on_path(exe: str):
|
@@ -27,11 +32,5 @@ def if_not_platform(*platforms: str):
|
|
27
32
|
return pytest.mark.skipif(sys.platform not in platforms, reason=f'requires platform in {platforms}')
|
28
33
|
|
29
34
|
|
30
|
-
def if_not_single():
|
31
|
-
# FIXME
|
32
|
-
# [resolve_collection_argument(a) for a in session.config.args]
|
33
|
-
raise NotImplementedError
|
34
|
-
|
35
|
-
|
36
35
|
def if_nogil():
|
37
36
|
return pytest.mark.skipif(sysconfig.get_config_var('Py_GIL_DISABLED'), reason='requires gil build')
|
omlish/typedvalues/__init__.py
CHANGED
omlish/typedvalues/accessor.py
CHANGED
@@ -27,7 +27,11 @@ class _NOT_SET(lang.Marker): # noqa
|
|
27
27
|
pass
|
28
28
|
|
29
29
|
|
30
|
-
class TypedValuesAccessor(
|
30
|
+
class TypedValuesAccessor(
|
31
|
+
lang.Abstract,
|
32
|
+
ta.Sequence[TypedValueT0],
|
33
|
+
ta.Generic[TypedValueT0],
|
34
|
+
):
|
31
35
|
def __iter__(self):
|
32
36
|
raise TypeError(
|
33
37
|
'TypedValuesAccessor does not implement __iter__ - it is reserved for implementation by subclasses.',
|
omlish/typedvalues/collection.py
CHANGED
@@ -0,0 +1,51 @@
|
|
1
|
+
import typing as ta
|
2
|
+
|
3
|
+
from .. import lang
|
4
|
+
from .collection import TypedValues
|
5
|
+
from .consumer import TypedValuesConsumer
|
6
|
+
from .values import TypedValue
|
7
|
+
|
8
|
+
|
9
|
+
TypedValueT = ta.TypeVar('TypedValueT', bound=TypedValue)
|
10
|
+
|
11
|
+
|
12
|
+
##
|
13
|
+
|
14
|
+
|
15
|
+
class _TypedValuesOf(lang.BindableClass[TypedValueT]): # noqa
|
16
|
+
@classmethod
|
17
|
+
def collect(
|
18
|
+
cls,
|
19
|
+
*tvs: TypedValueT,
|
20
|
+
override: bool = False,
|
21
|
+
check_type: bool | type | tuple[type, ...] | None = None,
|
22
|
+
) -> TypedValues[TypedValueT]: # noqa
|
23
|
+
if isinstance(check_type, bool):
|
24
|
+
if check_type:
|
25
|
+
if cls._bound is None:
|
26
|
+
raise TypeError('TypedValues.of unbound, cannot use check_type=True')
|
27
|
+
check_type = cls._bound
|
28
|
+
else:
|
29
|
+
check_type = None
|
30
|
+
|
31
|
+
return TypedValues(
|
32
|
+
*tvs,
|
33
|
+
override=override,
|
34
|
+
check_type=check_type,
|
35
|
+
)
|
36
|
+
|
37
|
+
@classmethod
|
38
|
+
def consume(
|
39
|
+
cls,
|
40
|
+
*tvs: TypedValueT,
|
41
|
+
override: bool = False,
|
42
|
+
check_type: bool | type | tuple[type, ...] | None = None,
|
43
|
+
) -> TypedValuesConsumer[TypedValueT]:
|
44
|
+
return cls.collect(
|
45
|
+
*tvs,
|
46
|
+
override=override,
|
47
|
+
check_type=check_type,
|
48
|
+
).consume()
|
49
|
+
|
50
|
+
|
51
|
+
of = _TypedValuesOf
|