omlish 0.0.0.dev447__py3-none-any.whl → 0.0.0.dev484__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.

Potentially problematic release.


This version of omlish might be problematic. Click here for more details.

Files changed (226) hide show
  1. omlish/.omlish-manifests.json +12 -0
  2. omlish/__about__.py +15 -15
  3. omlish/argparse/all.py +17 -9
  4. omlish/argparse/cli.py +16 -3
  5. omlish/argparse/utils.py +21 -0
  6. omlish/asyncs/asyncio/rlock.py +110 -0
  7. omlish/asyncs/asyncio/sync.py +43 -0
  8. omlish/asyncs/asyncio/utils.py +2 -0
  9. omlish/asyncs/sync.py +25 -0
  10. omlish/bootstrap/_marshal.py +1 -1
  11. omlish/bootstrap/diag.py +12 -21
  12. omlish/bootstrap/main.py +2 -5
  13. omlish/bootstrap/sys.py +27 -28
  14. omlish/cexts/__init__.py +0 -0
  15. omlish/cexts/include/omlish/omlish.hh +1 -0
  16. omlish/collections/__init__.py +13 -1
  17. omlish/collections/attrregistry.py +210 -0
  18. omlish/collections/cache/impl.py +1 -0
  19. omlish/collections/identity.py +1 -0
  20. omlish/collections/mappings.py +28 -0
  21. omlish/collections/trie.py +5 -1
  22. omlish/collections/utils.py +77 -0
  23. omlish/concurrent/all.py +2 -1
  24. omlish/concurrent/futures.py +25 -0
  25. omlish/concurrent/threadlets.py +1 -1
  26. omlish/daemons/reparent.py +2 -3
  27. omlish/daemons/spawning.py +2 -3
  28. omlish/dataclasses/__init__.py +2 -0
  29. omlish/dataclasses/impl/api/classes/decorator.py +3 -0
  30. omlish/dataclasses/impl/api/classes/make.py +3 -0
  31. omlish/dataclasses/impl/concerns/repr.py +15 -2
  32. omlish/dataclasses/impl/configs.py +97 -37
  33. omlish/dataclasses/impl/generation/compilation.py +21 -19
  34. omlish/dataclasses/impl/generation/globals.py +1 -0
  35. omlish/dataclasses/impl/generation/ops.py +1 -0
  36. omlish/dataclasses/impl/generation/processor.py +105 -24
  37. omlish/dataclasses/impl/processing/base.py +8 -0
  38. omlish/dataclasses/impl/processing/driving.py +8 -8
  39. omlish/dataclasses/specs.py +1 -0
  40. omlish/dataclasses/tools/modifiers.py +5 -0
  41. omlish/diag/cmds/__init__.py +0 -0
  42. omlish/diag/{lslocks.py → cmds/lslocks.py} +6 -6
  43. omlish/diag/{lsof.py → cmds/lsof.py} +6 -6
  44. omlish/diag/{ps.py → cmds/ps.py} +6 -6
  45. omlish/diag/pycharm.py +16 -2
  46. omlish/diag/pydevd.py +58 -40
  47. omlish/diag/replserver/console.py +1 -1
  48. omlish/dispatch/__init__.py +18 -12
  49. omlish/dispatch/methods.py +50 -140
  50. omlish/dom/rendering.py +1 -1
  51. omlish/formats/dotenv.py +1 -1
  52. omlish/formats/json/stream/__init__.py +13 -0
  53. omlish/funcs/guard.py +225 -0
  54. omlish/graphs/dot/rendering.py +1 -1
  55. omlish/http/all.py +44 -4
  56. omlish/http/clients/asyncs.py +33 -27
  57. omlish/http/clients/base.py +17 -1
  58. omlish/http/clients/coro/__init__.py +0 -0
  59. omlish/http/clients/coro/sync.py +171 -0
  60. omlish/http/clients/default.py +208 -29
  61. omlish/http/clients/executor.py +56 -0
  62. omlish/http/clients/httpx.py +72 -11
  63. omlish/http/clients/middleware.py +181 -0
  64. omlish/http/clients/sync.py +33 -27
  65. omlish/http/clients/syncasync.py +49 -0
  66. omlish/http/clients/urllib.py +6 -3
  67. omlish/http/coro/client/connection.py +15 -6
  68. omlish/http/coro/io.py +2 -0
  69. omlish/http/flasky/__init__.py +40 -0
  70. omlish/http/flasky/_compat.py +2 -0
  71. omlish/http/flasky/api.py +82 -0
  72. omlish/http/flasky/app.py +203 -0
  73. omlish/http/flasky/cvs.py +59 -0
  74. omlish/http/flasky/requests.py +20 -0
  75. omlish/http/flasky/responses.py +23 -0
  76. omlish/http/flasky/routes.py +23 -0
  77. omlish/http/flasky/types.py +15 -0
  78. omlish/http/urls.py +67 -0
  79. omlish/inject/__init__.py +38 -18
  80. omlish/inject/_dataclasses.py +4986 -0
  81. omlish/inject/binder.py +4 -48
  82. omlish/inject/elements.py +27 -0
  83. omlish/inject/helpers/__init__.py +0 -0
  84. omlish/inject/{utils.py → helpers/constfn.py} +3 -3
  85. omlish/inject/{tags.py → helpers/id.py} +2 -2
  86. omlish/inject/helpers/multis.py +143 -0
  87. omlish/inject/helpers/wrappers.py +54 -0
  88. omlish/inject/impl/elements.py +47 -17
  89. omlish/inject/impl/injector.py +20 -19
  90. omlish/inject/impl/inspect.py +10 -1
  91. omlish/inject/impl/maysync.py +3 -4
  92. omlish/inject/impl/multis.py +3 -0
  93. omlish/inject/impl/sync.py +3 -4
  94. omlish/inject/injector.py +31 -2
  95. omlish/inject/inspect.py +35 -0
  96. omlish/inject/maysync.py +2 -4
  97. omlish/inject/multis.py +8 -0
  98. omlish/inject/overrides.py +3 -3
  99. omlish/inject/privates.py +6 -0
  100. omlish/inject/providers.py +3 -2
  101. omlish/inject/sync.py +5 -4
  102. omlish/io/buffers.py +118 -0
  103. omlish/io/readers.py +29 -0
  104. omlish/iterators/transforms.py +2 -2
  105. omlish/lang/__init__.py +178 -97
  106. omlish/lang/_asyncs.cc +186 -0
  107. omlish/lang/asyncs.py +17 -0
  108. omlish/lang/casing.py +11 -0
  109. omlish/lang/contextmanagers.py +28 -4
  110. omlish/lang/functions.py +31 -22
  111. omlish/lang/imports/_capture.cc +11 -9
  112. omlish/lang/imports/capture.py +363 -170
  113. omlish/lang/imports/proxy.py +455 -152
  114. omlish/lang/lazyglobals.py +22 -9
  115. omlish/lang/params.py +17 -0
  116. omlish/lang/recursion.py +0 -1
  117. omlish/lang/sequences.py +124 -0
  118. omlish/lite/abstract.py +54 -24
  119. omlish/lite/asyncs.py +2 -2
  120. omlish/lite/attrops.py +2 -0
  121. omlish/lite/contextmanagers.py +4 -4
  122. omlish/lite/dataclasses.py +44 -0
  123. omlish/lite/maybes.py +8 -0
  124. omlish/lite/maysync.py +1 -5
  125. omlish/lite/pycharm.py +1 -1
  126. omlish/lite/typing.py +6 -0
  127. omlish/logs/all.py +1 -1
  128. omlish/logs/utils.py +1 -1
  129. omlish/manifests/loading.py +2 -1
  130. omlish/marshal/__init__.py +33 -13
  131. omlish/marshal/_dataclasses.py +2774 -0
  132. omlish/marshal/base/configs.py +12 -0
  133. omlish/marshal/base/contexts.py +36 -21
  134. omlish/marshal/base/funcs.py +8 -11
  135. omlish/marshal/base/options.py +8 -0
  136. omlish/marshal/base/registries.py +146 -44
  137. omlish/marshal/base/types.py +40 -16
  138. omlish/marshal/composite/iterables.py +33 -20
  139. omlish/marshal/composite/literals.py +20 -18
  140. omlish/marshal/composite/mappings.py +36 -23
  141. omlish/marshal/composite/maybes.py +29 -19
  142. omlish/marshal/composite/newtypes.py +16 -16
  143. omlish/marshal/composite/optionals.py +14 -14
  144. omlish/marshal/composite/special.py +15 -15
  145. omlish/marshal/composite/unions/__init__.py +0 -0
  146. omlish/marshal/composite/unions/literals.py +93 -0
  147. omlish/marshal/composite/unions/primitives.py +103 -0
  148. omlish/marshal/factories/invalidate.py +18 -68
  149. omlish/marshal/factories/method.py +26 -0
  150. omlish/marshal/factories/moduleimport/factories.py +22 -65
  151. omlish/marshal/factories/multi.py +13 -25
  152. omlish/marshal/factories/recursive.py +42 -56
  153. omlish/marshal/factories/typecache.py +29 -74
  154. omlish/marshal/factories/typemap.py +42 -43
  155. omlish/marshal/objects/dataclasses.py +129 -106
  156. omlish/marshal/objects/marshal.py +18 -14
  157. omlish/marshal/objects/namedtuples.py +48 -42
  158. omlish/marshal/objects/unmarshal.py +19 -15
  159. omlish/marshal/polymorphism/marshal.py +9 -11
  160. omlish/marshal/polymorphism/metadata.py +16 -5
  161. omlish/marshal/polymorphism/standard.py +13 -1
  162. omlish/marshal/polymorphism/unions.py +15 -105
  163. omlish/marshal/polymorphism/unmarshal.py +9 -10
  164. omlish/marshal/singular/enums.py +14 -18
  165. omlish/marshal/standard.py +10 -6
  166. omlish/marshal/trivial/any.py +1 -1
  167. omlish/marshal/trivial/forbidden.py +21 -26
  168. omlish/metadata.py +23 -1
  169. omlish/os/forkhooks.py +4 -0
  170. omlish/os/pidfiles/pinning.py +2 -2
  171. omlish/reflect/types.py +1 -0
  172. omlish/secrets/marshal.py +1 -1
  173. omlish/specs/jmespath/__init__.py +12 -3
  174. omlish/specs/jmespath/_dataclasses.py +2893 -0
  175. omlish/specs/jmespath/ast.py +1 -1
  176. omlish/specs/jsonrpc/__init__.py +13 -0
  177. omlish/specs/jsonrpc/_marshal.py +32 -23
  178. omlish/specs/jsonrpc/conns.py +10 -7
  179. omlish/specs/jsonschema/_marshal.py +1 -1
  180. omlish/specs/jsonschema/keywords/__init__.py +7 -0
  181. omlish/specs/jsonschema/keywords/_dataclasses.py +1644 -0
  182. omlish/specs/openapi/_marshal.py +31 -22
  183. omlish/sql/{tabledefs/alchemy.py → alchemy/tabledefs.py} +2 -2
  184. omlish/sql/queries/_marshal.py +2 -2
  185. omlish/sql/queries/rendering.py +1 -1
  186. omlish/sql/tabledefs/_marshal.py +1 -1
  187. omlish/subprocesses/base.py +4 -0
  188. omlish/subprocesses/editor.py +1 -1
  189. omlish/sync.py +155 -21
  190. omlish/term/alt.py +60 -0
  191. omlish/term/confirm.py +8 -8
  192. omlish/term/pager.py +235 -0
  193. omlish/term/terminfo.py +935 -0
  194. omlish/term/termstate.py +67 -0
  195. omlish/term/vt100/terminal.py +0 -3
  196. omlish/testing/pytest/plugins/asyncs/fixtures.py +4 -1
  197. omlish/testing/pytest/plugins/skips.py +2 -1
  198. omlish/testing/unittest/main.py +3 -3
  199. omlish/text/docwrap/__init__.py +3 -0
  200. omlish/text/docwrap/__main__.py +11 -0
  201. omlish/text/docwrap/api.py +83 -0
  202. omlish/text/docwrap/cli.py +86 -0
  203. omlish/text/docwrap/groups.py +84 -0
  204. omlish/text/docwrap/lists.py +167 -0
  205. omlish/text/docwrap/parts.py +146 -0
  206. omlish/text/docwrap/reflowing.py +106 -0
  207. omlish/text/docwrap/rendering.py +151 -0
  208. omlish/text/docwrap/utils.py +11 -0
  209. omlish/text/docwrap/wrapping.py +59 -0
  210. omlish/text/filecache.py +2 -2
  211. omlish/text/lorem.py +6 -0
  212. omlish/text/parts.py +2 -2
  213. omlish/text/textwrap.py +51 -0
  214. omlish/typedvalues/marshal.py +85 -59
  215. omlish/typedvalues/values.py +2 -1
  216. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/METADATA +29 -28
  217. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/RECORD +222 -171
  218. omlish/dataclasses/impl/generation/mangling.py +0 -18
  219. omlish/funcs/match.py +0 -227
  220. omlish/marshal/factories/match.py +0 -34
  221. omlish/marshal/factories/simple.py +0 -28
  222. /omlish/inject/impl/{providers2.py → providersmap.py} +0 -0
  223. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/WHEEL +0 -0
  224. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/entry_points.txt +0 -0
  225. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/licenses/LICENSE +0 -0
  226. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/top_level.txt +0 -0
@@ -3,6 +3,10 @@ import typing as ta
3
3
  from ... import lang
4
4
  from .registries import Registry
5
5
  from .registries import RegistryItem
6
+ from .registries import RegistryView
7
+
8
+
9
+ ConfigT = ta.TypeVar('ConfigT', bound='Config')
6
10
 
7
11
 
8
12
  ##
@@ -12,7 +16,15 @@ class Config(RegistryItem, lang.Abstract):
12
16
  pass
13
17
 
14
18
 
19
+ Configs: ta.TypeAlias = RegistryView[Config]
20
+
21
+
22
+ ##
23
+
24
+
15
25
  ConfigRegistry: ta.TypeAlias = Registry[Config]
16
26
 
27
+ lang.static_check_issubclass[Configs](ConfigRegistry)
28
+
17
29
 
18
30
  EMPTY_CONFIG_REGISTRY = ConfigRegistry().seal()
@@ -5,9 +5,8 @@ from ... import check
5
5
  from ... import collections as col
6
6
  from ... import lang
7
7
  from ... import reflect as rfl
8
- from ...funcs import match as mfs
9
8
  from .configs import EMPTY_CONFIG_REGISTRY
10
- from .configs import ConfigRegistry
9
+ from .configs import Configs
11
10
  from .errors import UnhandledTypeError
12
11
  from .options import Option
13
12
  from .overrides import ReflectOverride
@@ -28,44 +27,60 @@ T = ta.TypeVar('T')
28
27
 
29
28
 
30
29
  @dc.dataclass(frozen=True, kw_only=True)
31
- class BaseContext(lang.Abstract):
32
- config_registry: ConfigRegistry = EMPTY_CONFIG_REGISTRY
30
+ class BaseContext(lang.Abstract, lang.Sealed):
31
+ configs: Configs = EMPTY_CONFIG_REGISTRY
33
32
  options: col.TypeMap[Option] = col.TypeMap()
34
33
 
35
34
  def _reflect(self, o: ta.Any) -> rfl.Type:
36
35
  def override(o):
37
- if (ovr := self.config_registry.get_of(o, ReflectOverride)):
36
+ if (ovr := self.configs.get_of(o, ReflectOverride)):
38
37
  return ovr[-1].rty
39
38
  return None
40
39
 
41
40
  return rfl.Reflector(override=override).type(o)
42
41
 
43
42
 
43
+ #
44
+
45
+
44
46
  @dc.dataclass(frozen=True, kw_only=True)
45
- class MarshalContext(BaseContext, lang.Final):
46
- factory: ta.Optional['MarshalerFactory'] = None
47
+ class MarshalFactoryContext(BaseContext, lang.Final):
48
+ marshaler_factory: ta.Optional['MarshalerFactory'] = None
47
49
 
48
- def make(self, o: ta.Any) -> 'Marshaler':
50
+ def make_marshaler(self, o: ta.Any) -> 'Marshaler':
49
51
  rty = self._reflect(o)
50
- try:
51
- return check.not_none(self.factory).make_marshaler(self, rty)
52
- except mfs.MatchGuardError:
52
+ fac = check.not_none(self.marshaler_factory)
53
+ if (m := fac.make_marshaler(self, rty)) is None:
53
54
  raise UnhandledTypeError(rty) # noqa
54
-
55
- def marshal(self, obj: ta.Any, ty: ta.Any | None = None) -> 'Value':
56
- return self.make(ty if ty is not None else type(obj)).marshal(self, obj)
55
+ return m()
57
56
 
58
57
 
59
58
  @dc.dataclass(frozen=True, kw_only=True)
60
- class UnmarshalContext(BaseContext, lang.Final):
61
- factory: ta.Optional['UnmarshalerFactory'] = None
59
+ class UnmarshalFactoryContext(BaseContext, lang.Final):
60
+ unmarshaler_factory: ta.Optional['UnmarshalerFactory'] = None
62
61
 
63
- def make(self, o: ta.Any) -> 'Unmarshaler':
62
+ def make_unmarshaler(self, o: ta.Any) -> 'Unmarshaler':
64
63
  rty = self._reflect(o)
65
- try:
66
- return check.not_none(self.factory).make_unmarshaler(self, rty)
67
- except mfs.MatchGuardError:
64
+ fac = check.not_none(self.unmarshaler_factory)
65
+ if (m := fac.make_unmarshaler(self, rty)) is None:
68
66
  raise UnhandledTypeError(rty) # noqa
67
+ return m()
68
+
69
+
70
+ #
71
+
72
+
73
+ @dc.dataclass(frozen=True, kw_only=True)
74
+ class MarshalContext(BaseContext, lang.Final):
75
+ marshal_factory_context: MarshalFactoryContext
76
+
77
+ def marshal(self, obj: ta.Any, ty: ta.Any | None = None) -> 'Value':
78
+ return self.marshal_factory_context.make_marshaler(ty if ty is not None else type(obj)).marshal(self, obj)
79
+
80
+
81
+ @dc.dataclass(frozen=True, kw_only=True)
82
+ class UnmarshalContext(BaseContext, lang.Final):
83
+ unmarshal_factory_context: UnmarshalFactoryContext
69
84
 
70
85
  @ta.overload
71
86
  def unmarshal(self, v: 'Value', ty: type[T]) -> T:
@@ -76,4 +91,4 @@ class UnmarshalContext(BaseContext, lang.Final):
76
91
  ...
77
92
 
78
93
  def unmarshal(self, v, ty):
79
- return self.make(ty).unmarshal(self, v)
94
+ return self.unmarshal_factory_context.make_unmarshaler(ty).unmarshal(self, v)
@@ -3,9 +3,10 @@ import typing as ta
3
3
 
4
4
  from ... import lang
5
5
  from ... import reflect as rfl
6
- from ...funcs import match as mfs
7
6
  from .contexts import MarshalContext
7
+ from .contexts import MarshalFactoryContext
8
8
  from .contexts import UnmarshalContext
9
+ from .contexts import UnmarshalFactoryContext
9
10
  from .types import Marshaler
10
11
  from .types import MarshalerFactory
11
12
  from .types import MarshalerMaker
@@ -39,19 +40,15 @@ class FuncUnmarshaler(Unmarshaler, lang.Final):
39
40
 
40
41
  @dc.dataclass(frozen=True)
41
42
  class FuncMarshalerFactory(MarshalerFactory): # noqa
42
- guard: ta.Callable[[MarshalContext, rfl.Type], bool]
43
- fn: ta.Callable[[MarshalContext, rfl.Type], Marshaler]
43
+ gf: MarshalerMaker
44
44
 
45
- @lang.cached_property
46
- def make_marshaler(self) -> MarshalerMaker:
47
- return mfs.simple(self.guard, self.fn)
45
+ def make_marshaler(self, ctx: MarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Marshaler] | None:
46
+ return self.gf(ctx, rty)
48
47
 
49
48
 
50
49
  @dc.dataclass(frozen=True)
51
50
  class FuncUnmarshalerFactory(UnmarshalerFactory): # noqa
52
- guard: ta.Callable[[UnmarshalContext, rfl.Type], bool]
53
- fn: ta.Callable[[UnmarshalContext, rfl.Type], Unmarshaler]
51
+ gf: UnmarshalerMaker
54
52
 
55
- @lang.cached_property
56
- def make_unmarshaler(self) -> UnmarshalerMaker:
57
- return mfs.simple(self.guard, self.fn)
53
+ def make_unmarshaler(self, ctx: UnmarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Unmarshaler] | None:
54
+ return self.gf(ctx, rty)
@@ -1,2 +1,10 @@
1
+ """
2
+ TODO:
3
+ - MarshalOption, UnmarshalOption, check if a context got something unexpected
4
+ - Options only apply to Factories?
5
+ - MarshalFactoryOption, MarshalOption, UnmarshalFactoryOption, MarshalOption ?
6
+ """
7
+
8
+
1
9
  class Option:
2
10
  pass
@@ -3,11 +3,11 @@ TODO:
3
3
  - col.TypeMap?
4
4
  - at least get_any
5
5
  """
6
+ import abc
6
7
  import dataclasses as dc
7
8
  import threading
8
9
  import typing as ta
9
10
 
10
- from ... import collections as col
11
11
  from ... import lang
12
12
 
13
13
 
@@ -22,23 +22,38 @@ RegistryItemT = ta.TypeVar('RegistryItemT', bound=RegistryItem)
22
22
  RegistryItemU = ta.TypeVar('RegistryItemU', bound=RegistryItem)
23
23
 
24
24
 
25
- @dc.dataclass(frozen=True)
26
- class _KeyRegistryItems(ta.Generic[RegistryItemT]):
27
- key: ta.Any
28
- items: list[RegistryItemT] = dc.field(default_factory=list)
29
- item_lists_by_ty: dict[type[RegistryItemT], list[RegistryItemT]] = dc.field(default_factory=dict)
25
+ ##
26
+
27
+
28
+ class RegistryView(lang.Abstract, ta.Generic[RegistryItemT]):
29
+ @abc.abstractmethod
30
+ def get(
31
+ self,
32
+ key: ta.Any,
33
+ *,
34
+ identity: bool | None = None,
35
+ ) -> ta.Sequence[RegistryItemT]:
36
+ ...
30
37
 
31
- def add(self, *items: RegistryItemT) -> None:
32
- for i in items:
33
- self.items.append(i)
34
- self.item_lists_by_ty.setdefault(type(i), []).append(i)
38
+ @abc.abstractmethod
39
+ def get_of(
40
+ self,
41
+ key: ta.Any,
42
+ item_ty: type[RegistryItemU],
43
+ *,
44
+ identity: bool | None = None,
45
+ ) -> ta.Sequence[RegistryItemU]:
46
+ ...
47
+
48
+
49
+ ##
35
50
 
36
51
 
37
52
  class RegistrySealedError(Exception):
38
53
  pass
39
54
 
40
55
 
41
- class Registry(ta.Generic[RegistryItemT]):
56
+ class Registry(RegistryView[RegistryItemT]):
42
57
  def __init__(
43
58
  self,
44
59
  *,
@@ -50,14 +65,123 @@ class Registry(ta.Generic[RegistryItemT]):
50
65
  lock = threading.RLock()
51
66
  self._lock = lock
52
67
 
53
- self._dct: dict[ta.Any, _KeyRegistryItems[RegistryItemT]] = {}
54
- self._id_dct: ta.MutableMapping[ta.Any, _KeyRegistryItems[RegistryItemT]] = col.IdentityKeyDict()
68
+ self._state: Registry._State[RegistryItemT] = Registry._State(
69
+ dct={},
70
+ id_dct={},
71
+ version=0,
72
+ )
55
73
 
56
- self._version = 0
57
74
  self._sealed = False
58
75
 
59
76
  #
60
77
 
78
+ @dc.dataclass(frozen=True)
79
+ class _KeyItems(ta.Generic[RegistryItemU]):
80
+ key: ta.Any
81
+ items: ta.Sequence[RegistryItemU] = ()
82
+ item_lists_by_ty: ta.Mapping[type[RegistryItemU], ta.Sequence[RegistryItemU]] = dc.field(default_factory=dict)
83
+
84
+ def add(self, *items: RegistryItemU) -> 'Registry._KeyItems[RegistryItemU]':
85
+ item_lists_by_ty: dict[type[RegistryItemU], list[RegistryItemU]] = {}
86
+
87
+ for i in items:
88
+ try:
89
+ l = item_lists_by_ty[type(i)]
90
+ except KeyError:
91
+ l = item_lists_by_ty[type(i)] = list(self.item_lists_by_ty.get(type(i), ()))
92
+ l.append(i)
93
+
94
+ return Registry._KeyItems(
95
+ self.key,
96
+ (*self.items, *items),
97
+ {**self.item_lists_by_ty, **item_lists_by_ty},
98
+ )
99
+
100
+ @dc.dataclass(frozen=True, kw_only=True)
101
+ class _State(ta.Generic[RegistryItemU]):
102
+ dct: ta.Mapping[ta.Any, 'Registry._KeyItems[RegistryItemU]']
103
+ id_dct: ta.Mapping[ta.Any, 'Registry._KeyItems[RegistryItemU]']
104
+ version: int
105
+
106
+ #
107
+
108
+ def register(
109
+ self,
110
+ key: ta.Any,
111
+ *items: RegistryItemT,
112
+ identity: bool = False,
113
+ ) -> 'Registry._State[RegistryItemU]':
114
+ if not items:
115
+ return self
116
+
117
+ sr_dct: ta.Any = self.dct if not identity else self.id_dct
118
+ if (sr := sr_dct.get(key)) is None:
119
+ sr = Registry._KeyItems(key)
120
+ sr = sr.add(*items)
121
+ new = {key: sr}
122
+
123
+ return Registry._State(
124
+ dct={**self.dct, **new} if not identity else self.dct,
125
+ id_dct={**self.id_dct, **new} if identity else self.id_dct,
126
+ version=self.version + 1,
127
+ )
128
+
129
+ #
130
+
131
+ _get_cache: dict[ta.Any, ta.Sequence[RegistryItem]] = dc.field(default_factory=dict)
132
+
133
+ def get(
134
+ self,
135
+ key: ta.Any,
136
+ *,
137
+ identity: bool | None = None,
138
+ ) -> ta.Sequence[RegistryItem]:
139
+ if identity is None:
140
+ try:
141
+ return self._get_cache[key]
142
+ except KeyError:
143
+ pass
144
+
145
+ ret = self._get_cache[key] = (
146
+ *self.get(key, identity=True),
147
+ *self.get(key, identity=False),
148
+ )
149
+ return ret
150
+
151
+ dct: ta.Any = self.dct if not identity else self.id_dct
152
+ try:
153
+ return dct[key].items
154
+ except KeyError:
155
+ return ()
156
+
157
+ _get_of_cache: dict[ta.Any, dict[type, ta.Sequence[RegistryItem]]] = dc.field(default_factory=dict)
158
+
159
+ def get_of(
160
+ self,
161
+ key: ta.Any,
162
+ item_ty: type[RegistryItem],
163
+ *,
164
+ identity: bool | None = None,
165
+ ) -> ta.Sequence[RegistryItem]:
166
+ if identity is None:
167
+ try:
168
+ return self._get_of_cache[key][item_ty]
169
+ except KeyError:
170
+ pass
171
+
172
+ ret = self._get_of_cache.setdefault(key, {})[item_ty] = (
173
+ *self.get_of(key, item_ty, identity=True),
174
+ *self.get_of(key, item_ty, identity=False),
175
+ )
176
+ return ret
177
+
178
+ dct: ta.Any = self.dct if not identity else self.id_dct
179
+ try:
180
+ sr = dct[key]
181
+ except KeyError:
182
+ return ()
183
+ return sr.item_lists_by_ty.get(item_ty, ())
184
+
61
185
  def is_sealed(self) -> bool:
62
186
  if self._sealed:
63
187
  return True
@@ -92,12 +216,11 @@ class Registry(ta.Generic[RegistryItemT]):
92
216
  if self._sealed:
93
217
  raise RegistrySealedError(self)
94
218
 
95
- dct: ta.Any = self._id_dct if identity else self._dct
96
- if (sr := dct.get(key)) is None:
97
- sr = dct[key] = _KeyRegistryItems(key)
98
- sr.add(*items)
99
-
100
- self._version += 1
219
+ self._state = self._state.register(
220
+ key,
221
+ *items,
222
+ identity=identity,
223
+ )
101
224
 
102
225
  return self
103
226
 
@@ -108,18 +231,8 @@ class Registry(ta.Generic[RegistryItemT]):
108
231
  key: ta.Any,
109
232
  *,
110
233
  identity: bool | None = None,
111
- ) -> ta.Sequence[RegistryItem]:
112
- if identity is None:
113
- return (
114
- *self.get(key, identity=True),
115
- *self.get(key, identity=False),
116
- )
117
-
118
- dct: ta.Any = self._id_dct if identity else self._dct
119
- try:
120
- return dct[key].items
121
- except KeyError:
122
- return ()
234
+ ) -> ta.Sequence[RegistryItemT]:
235
+ return self._state.get(key, identity=identity) # type: ignore [return-value]
123
236
 
124
237
  def get_of(
125
238
  self,
@@ -128,15 +241,4 @@ class Registry(ta.Generic[RegistryItemT]):
128
241
  *,
129
242
  identity: bool | None = None,
130
243
  ) -> ta.Sequence[RegistryItemU]:
131
- if identity is None:
132
- return (
133
- *self.get_of(key, item_ty, identity=True),
134
- *self.get_of(key, item_ty, identity=False),
135
- )
136
-
137
- dct: ta.Any = self._id_dct if identity else self._dct
138
- try:
139
- sr = dct[key]
140
- except KeyError:
141
- return ()
142
- return sr.item_lists_by_ty.get(item_ty, ())
244
+ return self._state.get_of(key, item_ty, identity=identity) # type: ignore [return-value]
@@ -3,10 +3,12 @@ import typing as ta
3
3
 
4
4
  from ... import lang
5
5
  from ... import reflect as rfl
6
- from ...funcs import match as mfs
6
+ from ...funcs import guard as gfs
7
7
  from .configs import ConfigRegistry
8
8
  from .contexts import MarshalContext
9
+ from .contexts import MarshalFactoryContext
9
10
  from .contexts import UnmarshalContext
11
+ from .contexts import UnmarshalFactoryContext
10
12
  from .values import Value
11
13
 
12
14
 
@@ -31,21 +33,19 @@ class Unmarshaler(lang.Abstract):
31
33
  ##
32
34
 
33
35
 
34
- MarshalerMaker: ta.TypeAlias = mfs.MatchFn[[MarshalContext, rfl.Type], Marshaler]
35
- UnmarshalerMaker: ta.TypeAlias = mfs.MatchFn[[UnmarshalContext, rfl.Type], Unmarshaler]
36
+ MarshalerMaker: ta.TypeAlias = gfs.GuardFn[[MarshalFactoryContext, rfl.Type], Marshaler]
37
+ UnmarshalerMaker: ta.TypeAlias = gfs.GuardFn[[UnmarshalFactoryContext, rfl.Type], Unmarshaler]
36
38
 
37
39
 
38
40
  class MarshalerFactory(lang.Abstract):
39
- @property
40
41
  @abc.abstractmethod
41
- def make_marshaler(self) -> MarshalerMaker:
42
+ def make_marshaler(self, ctx: MarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Marshaler] | None:
42
43
  raise NotImplementedError
43
44
 
44
45
 
45
46
  class UnmarshalerFactory(lang.Abstract):
46
- @property
47
47
  @abc.abstractmethod
48
- def make_unmarshaler(self) -> UnmarshalerMaker:
48
+ def make_unmarshaler(self, ctx: UnmarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Unmarshaler] | None:
49
49
  raise NotImplementedError
50
50
 
51
51
 
@@ -65,20 +65,32 @@ class Marshaling(lang.Abstract):
65
65
  def unmarshaler_factory(self) -> UnmarshalerFactory:
66
66
  raise NotImplementedError
67
67
 
68
- #
68
+ ##
69
+
70
+ def new_marshal_factory_context(self) -> MarshalFactoryContext:
71
+ return MarshalFactoryContext(
72
+ configs=self.config_registry(),
73
+ marshaler_factory=self.marshaler_factory(),
74
+ )
75
+
76
+ def new_unmarshal_factory_context(self) -> UnmarshalFactoryContext:
77
+ return UnmarshalFactoryContext(
78
+ configs=self.config_registry(),
79
+ unmarshaler_factory=self.unmarshaler_factory(),
80
+ )
81
+
82
+ ##
69
83
 
70
- def new_marshal_context(self, **kwargs: ta.Any) -> MarshalContext:
84
+ def new_marshal_context(self) -> MarshalContext:
71
85
  return MarshalContext(
72
- config_registry=self.config_registry(),
73
- factory=self.marshaler_factory(),
74
- **kwargs,
86
+ configs=self.config_registry(),
87
+ marshal_factory_context=self.new_marshal_factory_context(),
75
88
  )
76
89
 
77
- def new_unmarshal_context(self, **kwargs: ta.Any) -> UnmarshalContext:
90
+ def new_unmarshal_context(self) -> UnmarshalContext:
78
91
  return UnmarshalContext(
79
- config_registry=self.config_registry(),
80
- factory=self.unmarshaler_factory(),
81
- **kwargs,
92
+ configs=self.config_registry(),
93
+ unmarshal_factory_context=self.new_unmarshal_factory_context(),
82
94
  )
83
95
 
84
96
  #
@@ -98,3 +110,15 @@ class Marshaling(lang.Abstract):
98
110
  @ta.final
99
111
  def unmarshal(self, v, ty, **kwargs):
100
112
  return self.new_unmarshal_context(**kwargs).unmarshal(v, ty)
113
+
114
+
115
+ ##
116
+
117
+
118
+ # MarshalerOrUnmarshaler: ta.TypeAlias = Marshaler | Unmarshaler
119
+ # MarshalerOrUnmarshalerT = ta.TypeVar('MarshalerOrUnmarshalerT', bound=MarshalerOrUnmarshaler)
120
+ #
121
+ # MarshalContextOrUnmarshalContext: ta.TypeAlias = MarshalContext | UnmarshalContext
122
+ # MarshalContextOrUnmarshalContextT = ta.TypeVar('MarshalContextOrUnmarshalContextT', bound=MarshalContextOrUnmarshalContext) # noqa
123
+ #
124
+ # MarshalerMakerOrUnmarshalerMaker: ta.TypeAlias = MarshalerMaker | UnmarshalerMaker
@@ -9,14 +9,15 @@ import typing as ta
9
9
 
10
10
  from ... import check
11
11
  from ... import reflect as rfl
12
- from ...funcs import match as mfs
13
12
  from ..base.contexts import MarshalContext
13
+ from ..base.contexts import MarshalFactoryContext
14
14
  from ..base.contexts import UnmarshalContext
15
+ from ..base.contexts import UnmarshalFactoryContext
15
16
  from ..base.types import Marshaler
16
17
  from ..base.types import Unmarshaler
17
18
  from ..base.values import Value
18
- from ..factories.match import MarshalerFactoryMatchClass
19
- from ..factories.match import UnmarshalerFactoryMatchClass
19
+ from ..factories.method import MarshalerFactoryMethodClass
20
+ from ..factories.method import UnmarshalerFactoryMethodClass
20
21
 
21
22
 
22
23
  ##
@@ -29,6 +30,9 @@ DEFAULT_ITERABLE_CONCRETE_TYPES: dict[type[collections.abc.Iterable], type[colle
29
30
  }
30
31
 
31
32
 
33
+ #
34
+
35
+
32
36
  @dc.dataclass(frozen=True)
33
37
  class IterableMarshaler(Marshaler):
34
38
  e: Marshaler
@@ -37,15 +41,21 @@ class IterableMarshaler(Marshaler):
37
41
  return list(map(functools.partial(self.e.marshal, ctx), o))
38
42
 
39
43
 
40
- class IterableMarshalerFactory(MarshalerFactoryMatchClass):
41
- @mfs.simple(lambda _, ctx, rty: isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Iterable))
42
- def _build_generic(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
43
- gty = check.isinstance(rty, rfl.Generic)
44
- return IterableMarshaler(ctx.make(check.single(gty.args)))
44
+ class IterableMarshalerFactory(MarshalerFactoryMethodClass):
45
+ @MarshalerFactoryMethodClass.make_marshaler.register
46
+ def _make_generic(self, ctx: MarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Marshaler] | None:
47
+ if not (isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Iterable)):
48
+ return None
49
+ return lambda: IterableMarshaler(ctx.make_marshaler(check.single(rty.args)))
50
+
51
+ @MarshalerFactoryMethodClass.make_marshaler.register
52
+ def _make_concrete(self, ctx: MarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Marshaler] | None:
53
+ if not (isinstance(rty, type) and issubclass(rty, collections.abc.Iterable)):
54
+ return None
55
+ return lambda: IterableMarshaler(ctx.make_marshaler(ta.Any))
56
+
45
57
 
46
- @mfs.simple(lambda _, ctx, rty: isinstance(rty, type) and issubclass(rty, collections.abc.Iterable))
47
- def _build_concrete(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
48
- return IterableMarshaler(ctx.make(ta.Any))
58
+ #
49
59
 
50
60
 
51
61
  @dc.dataclass(frozen=True)
@@ -57,13 +67,16 @@ class IterableUnmarshaler(Unmarshaler):
57
67
  return self.ctor(map(functools.partial(self.e.unmarshal, ctx), check.isinstance(v, collections.abc.Iterable)))
58
68
 
59
69
 
60
- class IterableUnmarshalerFactory(UnmarshalerFactoryMatchClass):
61
- @mfs.simple(lambda _, ctx, rty: isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Iterable))
62
- def _build_generic(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
63
- gty = check.isinstance(rty, rfl.Generic)
64
- cty = DEFAULT_ITERABLE_CONCRETE_TYPES.get(gty.cls, gty.cls) # noqa
65
- return IterableUnmarshaler(cty, ctx.make(check.single(gty.args)))
70
+ class IterableUnmarshalerFactory(UnmarshalerFactoryMethodClass):
71
+ @UnmarshalerFactoryMethodClass.make_unmarshaler.register
72
+ def _make_generic(self, ctx: UnmarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Unmarshaler] | None:
73
+ if not (isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Iterable)):
74
+ return None
75
+ cty = DEFAULT_ITERABLE_CONCRETE_TYPES.get(rty.cls, rty.cls) # noqa
76
+ return lambda: IterableUnmarshaler(cty, ctx.make_unmarshaler(check.single(rty.args))) # noqa
66
77
 
67
- @mfs.simple(lambda _, ctx, rty: isinstance(rty, type) and issubclass(rty, collections.abc.Iterable))
68
- def _build_concrete(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
69
- return IterableUnmarshaler(check.isinstance(rty, type), ctx.make(ta.Any))
78
+ @UnmarshalerFactoryMethodClass.make_unmarshaler.register
79
+ def _make_concrete(self, ctx: UnmarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Unmarshaler] | None:
80
+ if not (isinstance(rty, type) and issubclass(rty, collections.abc.Iterable)):
81
+ return None
82
+ return lambda: IterableUnmarshaler(check.isinstance(rty, type), ctx.make_unmarshaler(ta.Any))
@@ -1,15 +1,21 @@
1
+ """
2
+ TODO:
3
+ - squash literal unions - typing machinery doesn't
4
+ """
1
5
  import dataclasses as dc
2
6
  import typing as ta
3
7
 
4
8
  from ... import check
5
9
  from ... import reflect as rfl
6
10
  from ..base.contexts import MarshalContext
11
+ from ..base.contexts import MarshalFactoryContext
7
12
  from ..base.contexts import UnmarshalContext
13
+ from ..base.contexts import UnmarshalFactoryContext
8
14
  from ..base.types import Marshaler
15
+ from ..base.types import MarshalerFactory
9
16
  from ..base.types import Unmarshaler
17
+ from ..base.types import UnmarshalerFactory
10
18
  from ..base.values import Value
11
- from ..factories.simple import SimpleMarshalerFactory
12
- from ..factories.simple import SimpleUnmarshalerFactory
13
19
 
14
20
 
15
21
  ##
@@ -24,14 +30,12 @@ class LiteralMarshaler(Marshaler):
24
30
  return self.e.marshal(ctx, check.in_(o, self.vs))
25
31
 
26
32
 
27
- class LiteralMarshalerFactory(SimpleMarshalerFactory):
28
- def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
29
- return isinstance(rty, rfl.Literal)
30
-
31
- def fn(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
32
- lty = check.isinstance(rty, rfl.Literal)
33
- ety = check.single(set(map(type, lty.args)))
34
- return LiteralMarshaler(ctx.make(ety), frozenset(lty.args))
33
+ class LiteralMarshalerFactory(MarshalerFactory):
34
+ def make_marshaler(self, ctx: MarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Marshaler] | None:
35
+ if not (isinstance(rty, rfl.Literal) and len(set(map(type, rty.args))) == 1):
36
+ return None
37
+ ety = check.single(set(map(type, rty.args)))
38
+ return lambda: LiteralMarshaler(ctx.make_marshaler(ety), frozenset(rty.args))
35
39
 
36
40
 
37
41
  @dc.dataclass(frozen=True)
@@ -43,11 +47,9 @@ class LiteralUnmarshaler(Unmarshaler):
43
47
  return check.in_(self.e.unmarshal(ctx, v), self.vs)
44
48
 
45
49
 
46
- class LiteralUnmarshalerFactory(SimpleUnmarshalerFactory):
47
- def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
48
- return isinstance(rty, rfl.Literal)
49
-
50
- def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
51
- lty = check.isinstance(rty, rfl.Literal)
52
- ety = check.single(set(map(type, lty.args)))
53
- return LiteralUnmarshaler(ctx.make(ety), frozenset(lty.args))
50
+ class LiteralUnmarshalerFactory(UnmarshalerFactory):
51
+ def make_unmarshaler(self, ctx: UnmarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Unmarshaler] | None:
52
+ if not (isinstance(rty, rfl.Literal) and len(set(map(type, rty.args))) == 1):
53
+ return None
54
+ ety = check.single(set(map(type, rty.args)))
55
+ return lambda: LiteralUnmarshaler(ctx.make_unmarshaler(ety), frozenset(rty.args))