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.
Files changed (43) hide show
  1. omlish/__about__.py +2 -2
  2. omlish/asyncs/bluelet/LICENSE +1 -1
  3. omlish/asyncs/bluelet/api.py +1 -1
  4. omlish/asyncs/bluelet/core.py +1 -1
  5. omlish/asyncs/bluelet/events.py +1 -1
  6. omlish/asyncs/bluelet/files.py +1 -1
  7. omlish/asyncs/bluelet/runner.py +1 -1
  8. omlish/asyncs/bluelet/sockets.py +1 -1
  9. omlish/collections/__init__.py +19 -0
  10. omlish/collections/multimaps.py +151 -0
  11. omlish/formats/json/__init__.py +13 -13
  12. omlish/formats/json/backends/__init__.py +2 -2
  13. omlish/formats/json/backends/default.py +56 -12
  14. omlish/formats/json/backends/orjson.py +6 -5
  15. omlish/formats/json/backends/std.py +4 -1
  16. omlish/formats/json/backends/ujson.py +6 -5
  17. omlish/formats/json/codecs.py +4 -4
  18. omlish/graphs/dags.py +112 -48
  19. omlish/graphs/domination.py +5 -1
  20. omlish/graphs/dot/items.py +3 -0
  21. omlish/graphs/dot/make.py +3 -0
  22. omlish/graphs/dot/rendering.py +3 -0
  23. omlish/graphs/dot/utils.py +3 -0
  24. omlish/graphs/trees.py +5 -4
  25. omlish/lang/__init__.py +9 -0
  26. omlish/lang/classes/bindable.py +43 -0
  27. omlish/lang/classes/protocols.py +26 -0
  28. omlish/math/__init__.py +15 -15
  29. omlish/math/fixed.py +25 -15
  30. omlish/os/forkhooks.py +4 -4
  31. omlish/testing/pytest/plugins/switches/plugin.py +2 -0
  32. omlish/testing/pytest/skip.py +7 -8
  33. omlish/typedvalues/__init__.py +4 -0
  34. omlish/typedvalues/accessor.py +5 -1
  35. omlish/typedvalues/collection.py +1 -0
  36. omlish/typedvalues/of_.py +51 -0
  37. {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/METADATA +1 -1
  38. {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/RECORD +42 -39
  39. omlish/formats/json/json.py +0 -17
  40. {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/WHEEL +0 -0
  41. {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/entry_points.txt +0 -0
  42. {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/licenses/LICENSE +0 -0
  43. {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/top_level.txt +0 -0
@@ -12,6 +12,9 @@ from ... import dataclasses as dc
12
12
  from ... import lang
13
13
 
14
14
 
15
+ ##
16
+
17
+
15
18
  class Item(dc.Frozen, lang.Abstract, lang.Sealed):
16
19
  pass
17
20
 
omlish/graphs/dot/make.py CHANGED
@@ -9,6 +9,9 @@ from .items import Node
9
9
  T = ta.TypeVar('T')
10
10
 
11
11
 
12
+ ##
13
+
14
+
12
15
  def make_simple(graph: ta.Mapping[T, ta.Iterable[T]]) -> Graph:
13
16
  return Graph([
14
17
  *[Node(n) for n in {*graph, *lang.flatten(graph.values())}],
@@ -20,6 +20,9 @@ from .items import Table
20
20
  from .items import Text
21
21
 
22
22
 
23
+ ##
24
+
25
+
23
26
  class Renderer:
24
27
  def __init__(self, out: ta.TextIO) -> None:
25
28
  super().__init__()
@@ -2,6 +2,9 @@ import html
2
2
  import typing as ta
3
3
 
4
4
 
5
+ ##
6
+
7
+
5
8
  def escape(s: str) -> str:
6
9
  return html.escape(s).replace('@', '@')
7
10
 
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 all as alg
13
+ from ..algorithm.toposort import mut_toposort
14
14
 
15
15
 
16
16
  T = ta.TypeVar('T')
17
17
  NodeT = ta.TypeVar('NodeT')
18
- NodeWalker = ta.Callable[[NodeT], ta.Iterable[NodeT]]
19
- NodeGenerator = ta.Generator[NodeT, None, None]
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(alg.mut_toposort(tsd))
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
- CheckedUInt8,
39
- CheckedUInt16,
40
- CheckedUInt32,
41
- CheckedUInt64,
42
- CheckedUInt128,
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
- ClampedUInt8,
51
- ClampedUInt16,
52
- ClampedUInt32,
53
- ClampedUInt64,
54
- ClampedUInt128,
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
- WrappedUInt8,
63
- WrappedUInt16,
64
- WrappedUInt32,
65
- WrappedUInt64,
66
- WrappedUInt128,
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 CheckedUInt8(CheckedInt, UnsignedInt, AnyInt8):
291
+ class CheckedUint8(CheckedInt, UnsignedInt, AnyInt8):
282
292
  pass
283
293
 
284
294
 
285
- class CheckedUInt16(CheckedInt, UnsignedInt, AnyInt16):
295
+ class CheckedUint16(CheckedInt, UnsignedInt, AnyInt16):
286
296
  pass
287
297
 
288
298
 
289
- class CheckedUInt32(CheckedInt, UnsignedInt, AnyInt32):
299
+ class CheckedUint32(CheckedInt, UnsignedInt, AnyInt32):
290
300
  pass
291
301
 
292
302
 
293
- class CheckedUInt64(CheckedInt, UnsignedInt, AnyInt64):
303
+ class CheckedUint64(CheckedInt, UnsignedInt, AnyInt64):
294
304
  pass
295
305
 
296
306
 
297
- class CheckedUInt128(CheckedInt, UnsignedInt, AnyInt128):
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 ClampedUInt8(ClampedInt, UnsignedInt, AnyInt8):
337
+ class ClampedUint8(ClampedInt, UnsignedInt, AnyInt8):
328
338
  pass
329
339
 
330
340
 
331
- class ClampedUInt16(ClampedInt, UnsignedInt, AnyInt16):
341
+ class ClampedUint16(ClampedInt, UnsignedInt, AnyInt16):
332
342
  pass
333
343
 
334
344
 
335
- class ClampedUInt32(ClampedInt, UnsignedInt, AnyInt32):
345
+ class ClampedUint32(ClampedInt, UnsignedInt, AnyInt32):
336
346
  pass
337
347
 
338
348
 
339
- class ClampedUInt64(ClampedInt, UnsignedInt, AnyInt64):
349
+ class ClampedUint64(ClampedInt, UnsignedInt, AnyInt64):
340
350
  pass
341
351
 
342
352
 
343
- class ClampedUInt128(ClampedInt, UnsignedInt, AnyInt128):
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 WrappedUInt8(WrappedInt, UnsignedInt, AnyInt8):
383
+ class WrappedUint8(WrappedInt, UnsignedInt, AnyInt8):
374
384
  pass
375
385
 
376
386
 
377
- class WrappedUInt16(WrappedInt, UnsignedInt, AnyInt16):
387
+ class WrappedUint16(WrappedInt, UnsignedInt, AnyInt16):
378
388
  pass
379
389
 
380
390
 
381
- class WrappedUInt32(WrappedInt, UnsignedInt, AnyInt32):
391
+ class WrappedUint32(WrappedInt, UnsignedInt, AnyInt32):
382
392
  pass
383
393
 
384
394
 
385
- class WrappedUInt64(WrappedInt, UnsignedInt, AnyInt64):
395
+ class WrappedUint64(WrappedInt, UnsignedInt, AnyInt64):
386
396
  pass
387
397
 
388
398
 
389
- class WrappedUInt128(WrappedInt, UnsignedInt, AnyInt128):
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[str, Hook]] = {}
39
+ _hooks_by_key: ta.ClassVar[ta.Dict[ta.Any, Hook]] = {}
40
40
 
41
- _hook_keys: ta.ClassVar[ta.FrozenSet[str]] = 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 _ForkDepthTracker(ForkHook):
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 _ForkDepthTracker.get_fork_depth()
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]
@@ -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(module: str, *args, **kwargs):
15
- return pytest.mark.skipif(not lang.can_import(module, *args, **kwargs), reason=f'requires import {module}')
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')
@@ -22,6 +22,10 @@ from .holder import ( # noqa
22
22
  TypedValueHolder,
23
23
  )
24
24
 
25
+ from .of_ import ( # noqa
26
+ of,
27
+ )
28
+
25
29
  from .reflect import ( # noqa
26
30
  reflect_typed_values_impls,
27
31
  )
@@ -27,7 +27,11 @@ class _NOT_SET(lang.Marker): # noqa
27
27
  pass
28
28
 
29
29
 
30
- class TypedValuesAccessor(lang.Abstract, ta.Sequence[TypedValueT0]):
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.',
@@ -10,6 +10,7 @@ from .values import UniqueTypedValue
10
10
 
11
11
 
12
12
  TypedValueT = ta.TypeVar('TypedValueT', bound='TypedValue')
13
+ TypedValueU = ta.TypeVar('TypedValueU', bound='TypedValue')
13
14
 
14
15
  UniqueTypedValueT = ta.TypeVar('UniqueTypedValueT', bound='UniqueTypedValue')
15
16
 
@@ -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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omlish
3
- Version: 0.0.0.dev338
3
+ Version: 0.0.0.dev340
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause