omdev 0.0.0.dev416__py3-none-any.whl → 0.0.0.dev500__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 omdev might be problematic. Click here for more details.

Files changed (211) hide show
  1. omdev/{.manifests.json → .omlish-manifests.json} +23 -47
  2. omdev/README.md +51 -0
  3. omdev/__about__.py +12 -8
  4. omdev/amalg/cli/main.py +1 -2
  5. omdev/amalg/gen/gen.py +49 -6
  6. omdev/amalg/gen/imports.py +1 -1
  7. omdev/amalg/gen/manifests.py +1 -1
  8. omdev/amalg/gen/resources.py +1 -1
  9. omdev/amalg/gen/srcfiles.py +26 -3
  10. omdev/amalg/gen/strip.py +1 -1
  11. omdev/amalg/gen/types.py +1 -1
  12. omdev/amalg/gen/typing.py +1 -1
  13. omdev/amalg/info.py +32 -0
  14. omdev/cache/compute/storage.py +3 -1
  15. omdev/cache/data/actions.py +1 -1
  16. omdev/cache/data/cache.py +2 -2
  17. omdev/cache/data/specs.py +1 -1
  18. omdev/cexts/_boilerplate.cc +2 -3
  19. omdev/cexts/_distutils/build_ext.py +5 -2
  20. omdev/cexts/_distutils/compilers/ccompiler.py +5 -2
  21. omdev/cexts/_distutils/compilers/options.py +3 -0
  22. omdev/cexts/_distutils/compilers/unixccompiler.py +6 -2
  23. omdev/cexts/_distutils/dir_util.py +6 -2
  24. omdev/cexts/_distutils/errors.py +3 -0
  25. omdev/cexts/_distutils/extension.py +3 -0
  26. omdev/cexts/_distutils/file_util.py +6 -2
  27. omdev/cexts/_distutils/modified.py +3 -0
  28. omdev/cexts/_distutils/spawn.py +6 -2
  29. omdev/cexts/_distutils/sysconfig.py +3 -0
  30. omdev/cexts/_distutils/util.py +6 -2
  31. omdev/cexts/_distutils/version.py +3 -0
  32. omdev/cexts/cmake.py +5 -3
  33. omdev/cexts/scan.py +1 -2
  34. omdev/ci/cache.py +7 -3
  35. omdev/ci/cli.py +6 -4
  36. omdev/ci/docker/buildcaching.py +3 -1
  37. omdev/ci/docker/cache.py +2 -1
  38. omdev/ci/docker/cacheserved/cache.py +4 -1
  39. omdev/ci/docker/cacheserved/manifests.py +2 -2
  40. omdev/ci/docker/dataserver.py +2 -2
  41. omdev/ci/docker/imagepulling.py +2 -1
  42. omdev/ci/docker/packing.py +1 -1
  43. omdev/ci/docker/repositories.py +2 -1
  44. omdev/ci/github/api/clients.py +8 -4
  45. omdev/ci/github/api/v1/client.py +4 -1
  46. omdev/ci/github/api/v2/api.py +2 -0
  47. omdev/ci/github/api/v2/azure.py +4 -1
  48. omdev/ci/github/api/v2/client.py +4 -1
  49. omdev/cli/clicli.py +37 -7
  50. omdev/clipboard/clipboard.py +1 -1
  51. omdev/cmake.py +2 -1
  52. omdev/cmdlog/cli.py +1 -2
  53. omdev/dataclasses/_dumping.py +1960 -0
  54. omdev/dataclasses/_template.py +22 -0
  55. omdev/dataclasses/cli.py +7 -2
  56. omdev/dataclasses/codegen.py +342 -62
  57. omdev/dataclasses/dumping.py +200 -0
  58. omdev/dataserver/handlers.py +3 -2
  59. omdev/dataserver/targets.py +2 -2
  60. omdev/imgur.py +2 -2
  61. omdev/interp/cli.py +1 -1
  62. omdev/interp/inspect.py +2 -1
  63. omdev/interp/providers/base.py +3 -2
  64. omdev/interp/providers/standalone.py +4 -1
  65. omdev/interp/providers/system.py +2 -2
  66. omdev/interp/pyenv/install.py +2 -1
  67. omdev/interp/pyenv/provider.py +2 -2
  68. omdev/interp/types.py +3 -2
  69. omdev/interp/uv/provider.py +40 -2
  70. omdev/interp/uv/uv.py +2 -2
  71. omdev/interp/venvs.py +3 -2
  72. omdev/irc/messages/base.py +50 -0
  73. omdev/irc/messages/formats.py +92 -0
  74. omdev/irc/messages/messages.py +775 -0
  75. omdev/irc/messages/parsing.py +99 -0
  76. omdev/irc/numerics/formats.py +97 -0
  77. omdev/irc/numerics/numerics.py +865 -0
  78. omdev/irc/numerics/types.py +59 -0
  79. omdev/irc/protocol/LICENSE +11 -0
  80. omdev/irc/protocol/__init__.py +61 -0
  81. omdev/irc/protocol/consts.py +6 -0
  82. omdev/irc/protocol/errors.py +30 -0
  83. omdev/irc/protocol/message.py +21 -0
  84. omdev/irc/protocol/nuh.py +55 -0
  85. omdev/irc/protocol/parsing.py +158 -0
  86. omdev/irc/protocol/rendering.py +153 -0
  87. omdev/irc/protocol/tags.py +102 -0
  88. omdev/irc/protocol/utils.py +30 -0
  89. omdev/manifests/_dumping.py +529 -136
  90. omdev/manifests/building.py +6 -3
  91. omdev/manifests/main.py +1 -1
  92. omdev/markdown/__init__.py +0 -0
  93. omdev/markdown/incparse.py +116 -0
  94. omdev/markdown/tokens.py +51 -0
  95. omdev/oci/data.py +2 -2
  96. omdev/oci/datarefs.py +2 -2
  97. omdev/oci/media.py +2 -2
  98. omdev/oci/repositories.py +3 -2
  99. omdev/packaging/marshal.py +9 -9
  100. omdev/packaging/requires.py +6 -6
  101. omdev/packaging/revisions.py +5 -2
  102. omdev/packaging/specifiers.py +41 -42
  103. omdev/packaging/versions.py +10 -10
  104. omdev/packaging/wheelfile.py +4 -2
  105. omdev/precheck/blanklines.py +66 -0
  106. omdev/precheck/caches.py +1 -1
  107. omdev/precheck/imports.py +14 -1
  108. omdev/precheck/lite.py +2 -2
  109. omdev/precheck/main.py +5 -5
  110. omdev/precheck/unicode.py +39 -15
  111. omdev/py/asts/__init__.py +0 -0
  112. omdev/py/asts/parents.py +28 -0
  113. omdev/py/asts/toplevel.py +123 -0
  114. omdev/py/asts/visitors.py +18 -0
  115. omdev/py/attrdocs.py +6 -7
  116. omdev/py/bracepy.py +12 -4
  117. omdev/py/docstrings/numpydoc.py +4 -4
  118. omdev/py/reprs.py +32 -0
  119. omdev/py/scripts/execstat.py +31 -26
  120. omdev/py/srcheaders.py +1 -1
  121. omdev/py/tokens/__init__.py +0 -0
  122. omdev/{tokens → py/tokens}/utils.py +2 -1
  123. omdev/py/tools/importscan.py +2 -2
  124. omdev/py/tools/mkrelimp.py +3 -4
  125. omdev/py/tools/pipdepup.py +686 -0
  126. omdev/pyproject/cli.py +1 -1
  127. omdev/pyproject/pkg.py +197 -48
  128. omdev/pyproject/reqs.py +36 -10
  129. omdev/pyproject/tools/__init__.py +0 -0
  130. omdev/pyproject/tools/aboutdeps.py +60 -0
  131. omdev/pyproject/venvs.py +12 -2
  132. omdev/rs/__init__.py +0 -0
  133. omdev/scripts/ci.py +9551 -6982
  134. omdev/scripts/interp.py +1323 -892
  135. omdev/scripts/lib/__init__.py +0 -0
  136. omdev/scripts/lib/inject.py +2086 -0
  137. omdev/scripts/lib/logs.py +2175 -0
  138. omdev/scripts/lib/marshal.py +1731 -0
  139. omdev/scripts/pyproject.py +4979 -1874
  140. omdev/tools/docker.py +19 -7
  141. omdev/tools/git/cli.py +56 -16
  142. omdev/tools/git/messages.py +2 -2
  143. omdev/tools/json/cli.py +6 -6
  144. omdev/tools/json/formats.py +2 -0
  145. omdev/tools/json/parsing.py +5 -5
  146. omdev/tools/json/processing.py +6 -3
  147. omdev/tools/json/rendering.py +2 -2
  148. omdev/tools/jsonview/cli.py +49 -65
  149. omdev/tools/jsonview/resources/jsonview.html.j2 +43 -0
  150. omdev/tools/pawk/README.md +195 -0
  151. omdev/tools/pawk/pawk.py +2 -2
  152. omdev/tools/pip.py +8 -0
  153. omdev/tui/__init__.py +0 -0
  154. omdev/tui/apps/__init__.py +0 -0
  155. omdev/tui/apps/edit/__init__.py +0 -0
  156. omdev/tui/apps/edit/main.py +167 -0
  157. omdev/tui/apps/irc/__init__.py +0 -0
  158. omdev/tui/apps/irc/__main__.py +4 -0
  159. omdev/tui/apps/irc/app.py +286 -0
  160. omdev/tui/apps/irc/client.py +187 -0
  161. omdev/tui/apps/irc/commands.py +175 -0
  162. omdev/tui/apps/irc/main.py +26 -0
  163. omdev/tui/apps/markdown/__init__.py +0 -0
  164. omdev/tui/apps/markdown/__main__.py +11 -0
  165. omdev/{ptk → tui/apps}/markdown/cli.py +5 -7
  166. omdev/tui/rich/__init__.py +46 -0
  167. omdev/tui/rich/console2.py +20 -0
  168. omdev/tui/rich/markdown2.py +186 -0
  169. omdev/tui/textual/__init__.py +265 -0
  170. omdev/tui/textual/app2.py +16 -0
  171. omdev/tui/textual/autocomplete/LICENSE +21 -0
  172. omdev/tui/textual/autocomplete/__init__.py +33 -0
  173. omdev/tui/textual/autocomplete/matching.py +226 -0
  174. omdev/tui/textual/autocomplete/paths.py +202 -0
  175. omdev/tui/textual/autocomplete/widget.py +612 -0
  176. omdev/tui/textual/debug/__init__.py +10 -0
  177. omdev/tui/textual/debug/dominfo.py +151 -0
  178. omdev/tui/textual/debug/screen.py +24 -0
  179. omdev/tui/textual/devtools.py +187 -0
  180. omdev/tui/textual/drivers2.py +55 -0
  181. omdev/tui/textual/logging2.py +20 -0
  182. omdev/tui/textual/types.py +45 -0
  183. {omdev-0.0.0.dev416.dist-info → omdev-0.0.0.dev500.dist-info}/METADATA +18 -12
  184. omdev-0.0.0.dev500.dist-info/RECORD +386 -0
  185. omdev/ptk/__init__.py +0 -103
  186. omdev/ptk/apps/ncdu.py +0 -167
  187. omdev/ptk/confirm.py +0 -60
  188. omdev/ptk/markdown/LICENSE +0 -22
  189. omdev/ptk/markdown/__init__.py +0 -10
  190. omdev/ptk/markdown/__main__.py +0 -11
  191. omdev/ptk/markdown/border.py +0 -94
  192. omdev/ptk/markdown/markdown.py +0 -390
  193. omdev/ptk/markdown/parser.py +0 -42
  194. omdev/ptk/markdown/styles.py +0 -29
  195. omdev/ptk/markdown/tags.py +0 -299
  196. omdev/ptk/markdown/utils.py +0 -366
  197. omdev/pyproject/cexts.py +0 -110
  198. omdev/tools/antlr/__main__.py +0 -11
  199. omdev/tools/antlr/cli.py +0 -62
  200. omdev/tools/antlr/consts.py +0 -7
  201. omdev/tools/antlr/gen.py +0 -188
  202. omdev-0.0.0.dev416.dist-info/RECORD +0 -332
  203. /omdev/{ptk/apps → irc}/__init__.py +0 -0
  204. /omdev/{tokens → irc/messages}/__init__.py +0 -0
  205. /omdev/{tools/antlr → irc/numerics}/__init__.py +0 -0
  206. /omdev/{tokens → py/tokens}/all.py +0 -0
  207. /omdev/{tokens → py/tokens}/tokenizert.py +0 -0
  208. {omdev-0.0.0.dev416.dist-info → omdev-0.0.0.dev500.dist-info}/WHEEL +0 -0
  209. {omdev-0.0.0.dev416.dist-info → omdev-0.0.0.dev500.dist-info}/entry_points.txt +0 -0
  210. {omdev-0.0.0.dev416.dist-info → omdev-0.0.0.dev500.dist-info}/licenses/LICENSE +0 -0
  211. {omdev-0.0.0.dev416.dist-info → omdev-0.0.0.dev500.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1731 @@
1
+ #!/usr/bin/env python3
2
+ # noinspection DuplicatedCode
3
+ # @omlish-lite
4
+ # @omlish-script
5
+ # @omlish-generated
6
+ # @omlish-amalg-output ../../../omlish/lite/marshal.py
7
+ # @omlish-git-diff-omit
8
+ # ruff: noqa: UP006 UP007 UP036 UP045
9
+ """
10
+ TODO:
11
+ - pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
12
+ - Options.sequence_cls = list, mapping_cls = dict, ... - def with_mutable_containers() -> Options
13
+ """
14
+ import abc
15
+ import base64
16
+ import collections
17
+ import collections.abc
18
+ import dataclasses as dc # noqa
19
+ import datetime
20
+ import decimal
21
+ import enum
22
+ import fractions
23
+ import functools
24
+ import inspect
25
+ import sys
26
+ import threading
27
+ import types
28
+ import typing as ta
29
+ import uuid
30
+ import weakref
31
+
32
+
33
+ ########################################
34
+
35
+
36
+ if sys.version_info < (3, 8):
37
+ raise OSError(f'Requires python (3, 8), got {sys.version_info} from {sys.executable}') # noqa
38
+
39
+
40
+ def __omlish_amalg__(): # noqa
41
+ return dict(
42
+ src_files=[
43
+ dict(path='abstract.py', sha1='a2fc3f3697fa8de5247761e9d554e70176f37aac'),
44
+ dict(path='check.py', sha1='bb6b6b63333699b84462951a854d99ae83195b94'),
45
+ dict(path='objects.py', sha1='9566bbf3530fd71fcc56321485216b592fae21e9'),
46
+ dict(path='reflect.py', sha1='c4fec44bf144e9d93293c996af06f6c65fc5e63d'),
47
+ dict(path='strings.py', sha1='89831ecbc34ad80e118a865eceb390ed399dc4d6'),
48
+ dict(path='marshal.py', sha1='96348f5f2a26dc27d842d33cc3927e9da163436b'),
49
+ ],
50
+ )
51
+
52
+
53
+ ########################################
54
+
55
+
56
+ # abstract.py
57
+ T = ta.TypeVar('T')
58
+
59
+ # check.py
60
+ SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
61
+ CheckMessage = ta.Union[str, ta.Callable[..., ta.Optional[str]], None] # ta.TypeAlias
62
+ CheckLateConfigureFn = ta.Callable[['Checks'], None] # ta.TypeAlias
63
+ CheckOnRaiseFn = ta.Callable[[Exception], None] # ta.TypeAlias
64
+ CheckExceptionFactory = ta.Callable[..., Exception] # ta.TypeAlias
65
+ CheckArgsRenderer = ta.Callable[..., ta.Optional[str]] # ta.TypeAlias
66
+
67
+
68
+ ########################################
69
+ # ../abstract.py
70
+
71
+
72
+ ##
73
+
74
+
75
+ _ABSTRACT_METHODS_ATTR = '__abstractmethods__'
76
+ _IS_ABSTRACT_METHOD_ATTR = '__isabstractmethod__'
77
+
78
+
79
+ def is_abstract_method(obj: ta.Any) -> bool:
80
+ return bool(getattr(obj, _IS_ABSTRACT_METHOD_ATTR, False))
81
+
82
+
83
+ def compute_abstract_methods(cls: type) -> ta.FrozenSet[str]:
84
+ # ~> https://github.com/python/cpython/blob/f3476c6507381ca860eec0989f53647b13517423/Modules/_abc.c#L358
85
+
86
+ # Stage 1: direct abstract methods
87
+
88
+ abstracts = {
89
+ a
90
+ # Get items as a list to avoid mutation issues during iteration
91
+ for a, v in list(cls.__dict__.items())
92
+ if is_abstract_method(v)
93
+ }
94
+
95
+ # Stage 2: inherited abstract methods
96
+
97
+ for base in cls.__bases__:
98
+ # Get __abstractmethods__ from base if it exists
99
+ if (base_abstracts := getattr(base, _ABSTRACT_METHODS_ATTR, None)) is None:
100
+ continue
101
+
102
+ # Iterate over abstract methods in base
103
+ for key in base_abstracts:
104
+ # Check if this class has an attribute with this name
105
+ try:
106
+ value = getattr(cls, key)
107
+ except AttributeError:
108
+ # Attribute not found in this class, skip
109
+ continue
110
+
111
+ # Check if it's still abstract
112
+ if is_abstract_method(value):
113
+ abstracts.add(key)
114
+
115
+ return frozenset(abstracts)
116
+
117
+
118
+ def update_abstracts(cls: ta.Type[T], *, force: bool = False) -> ta.Type[T]:
119
+ if not force and not hasattr(cls, _ABSTRACT_METHODS_ATTR):
120
+ # Per stdlib: We check for __abstractmethods__ here because cls might by a C implementation or a python
121
+ # implementation (especially during testing), and we want to handle both cases.
122
+ return cls
123
+
124
+ abstracts = compute_abstract_methods(cls)
125
+ setattr(cls, _ABSTRACT_METHODS_ATTR, abstracts)
126
+ return cls
127
+
128
+
129
+ #
130
+
131
+
132
+ class AbstractTypeError(TypeError):
133
+ pass
134
+
135
+
136
+ _FORCE_ABSTRACT_ATTR = '__forceabstract__'
137
+
138
+
139
+ class Abstract:
140
+ """
141
+ Different from, but interoperable with, abc.ABC / abc.ABCMeta:
142
+
143
+ - This raises AbstractTypeError during class creation, not instance instantiation - unless Abstract or abc.ABC are
144
+ explicitly present in the class's direct bases.
145
+ - This will forbid instantiation of classes with Abstract in their direct bases even if there are no
146
+ abstractmethods left on the class.
147
+ - This is a mixin, not a metaclass.
148
+ - As it is not an ABCMeta, this does not support virtual base classes. As a result, operations like `isinstance`
149
+ and `issubclass` are ~7x faster.
150
+ - It additionally enforces a base class order of (Abstract, abc.ABC) to preemptively prevent common mro conflicts.
151
+
152
+ If not mixed-in with an ABCMeta, it will update __abstractmethods__ itself.
153
+ """
154
+
155
+ __slots__ = ()
156
+
157
+ __abstractmethods__: ta.ClassVar[ta.FrozenSet[str]] = frozenset()
158
+
159
+ #
160
+
161
+ def __forceabstract__(self):
162
+ raise TypeError
163
+
164
+ # This is done manually, rather than through @abc.abstractmethod, to mask it from static analysis.
165
+ setattr(__forceabstract__, _IS_ABSTRACT_METHOD_ATTR, True)
166
+
167
+ #
168
+
169
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
170
+ setattr(
171
+ cls,
172
+ _FORCE_ABSTRACT_ATTR,
173
+ getattr(Abstract, _FORCE_ABSTRACT_ATTR) if Abstract in cls.__bases__ else False,
174
+ )
175
+
176
+ super().__init_subclass__(**kwargs)
177
+
178
+ if not (Abstract in cls.__bases__ or abc.ABC in cls.__bases__):
179
+ if ams := compute_abstract_methods(cls):
180
+ amd = {
181
+ a: mcls
182
+ for mcls in cls.__mro__[::-1]
183
+ for a in ams
184
+ if a in mcls.__dict__
185
+ }
186
+
187
+ raise AbstractTypeError(
188
+ f'Cannot subclass abstract class {cls.__name__} with abstract methods: ' +
189
+ ', '.join(sorted([
190
+ '.'.join([
191
+ *([
192
+ *([m] if (m := getattr(c, '__module__')) else []),
193
+ getattr(c, '__qualname__', getattr(c, '__name__')),
194
+ ] if c is not None else '?'),
195
+ a,
196
+ ])
197
+ for a in ams
198
+ for c in [amd.get(a)]
199
+ ])),
200
+ )
201
+
202
+ xbi = (Abstract, abc.ABC) # , ta.Generic ?
203
+ bis = [(cls.__bases__.index(b), b) for b in xbi if b in cls.__bases__]
204
+ if bis != sorted(bis):
205
+ raise TypeError(
206
+ f'Abstract subclass {cls.__name__} must have proper base class order of '
207
+ f'({", ".join(getattr(b, "__name__") for b in xbi)}), got: '
208
+ f'({", ".join(getattr(b, "__name__") for _, b in sorted(bis))})',
209
+ )
210
+
211
+ if not isinstance(cls, abc.ABCMeta):
212
+ update_abstracts(cls, force=True)
213
+
214
+
215
+ ########################################
216
+ # ../check.py
217
+ """
218
+ TODO:
219
+ - def maybe(v: lang.Maybe[T])
220
+ - def not_ ?
221
+ - ** class @dataclass Raise - user message should be able to be an exception type or instance or factory
222
+ """
223
+
224
+
225
+ ##
226
+
227
+
228
+ class Checks:
229
+ def __init__(self) -> None:
230
+ super().__init__()
231
+
232
+ self._config_lock = threading.RLock()
233
+ self._on_raise_fns: ta.Sequence[CheckOnRaiseFn] = []
234
+ self._exception_factory: CheckExceptionFactory = Checks.default_exception_factory
235
+ self._args_renderer: ta.Optional[CheckArgsRenderer] = None
236
+ self._late_configure_fns: ta.Sequence[CheckLateConfigureFn] = []
237
+
238
+ @staticmethod
239
+ def default_exception_factory(exc_cls: ta.Type[Exception], *args, **kwargs) -> Exception:
240
+ return exc_cls(*args, **kwargs) # noqa
241
+
242
+ #
243
+
244
+ def register_on_raise(self, fn: CheckOnRaiseFn) -> None:
245
+ with self._config_lock:
246
+ self._on_raise_fns = [*self._on_raise_fns, fn]
247
+
248
+ def unregister_on_raise(self, fn: CheckOnRaiseFn) -> None:
249
+ with self._config_lock:
250
+ self._on_raise_fns = [e for e in self._on_raise_fns if e != fn]
251
+
252
+ #
253
+
254
+ def register_on_raise_breakpoint_if_env_var_set(self, key: str) -> None:
255
+ import os
256
+
257
+ def on_raise(exc: Exception) -> None: # noqa
258
+ if key in os.environ:
259
+ breakpoint() # noqa
260
+
261
+ self.register_on_raise(on_raise)
262
+
263
+ #
264
+
265
+ def set_exception_factory(self, factory: CheckExceptionFactory) -> None:
266
+ self._exception_factory = factory
267
+
268
+ def set_args_renderer(self, renderer: ta.Optional[CheckArgsRenderer]) -> None:
269
+ self._args_renderer = renderer
270
+
271
+ #
272
+
273
+ def register_late_configure(self, fn: CheckLateConfigureFn) -> None:
274
+ with self._config_lock:
275
+ self._late_configure_fns = [*self._late_configure_fns, fn]
276
+
277
+ def _late_configure(self) -> None:
278
+ if not self._late_configure_fns:
279
+ return
280
+
281
+ with self._config_lock:
282
+ if not (lc := self._late_configure_fns):
283
+ return
284
+
285
+ for fn in lc:
286
+ fn(self)
287
+
288
+ self._late_configure_fns = []
289
+
290
+ #
291
+
292
+ class _ArgsKwargs:
293
+ def __init__(self, *args, **kwargs):
294
+ self.args = args
295
+ self.kwargs = kwargs
296
+
297
+ def _raise(
298
+ self,
299
+ exception_type: ta.Type[Exception],
300
+ default_message: str,
301
+ message: CheckMessage,
302
+ ak: _ArgsKwargs = _ArgsKwargs(),
303
+ *,
304
+ render_fmt: ta.Optional[str] = None,
305
+ ) -> ta.NoReturn:
306
+ exc_args = ()
307
+ if callable(message):
308
+ message = ta.cast(ta.Callable, message)(*ak.args, **ak.kwargs)
309
+ if isinstance(message, tuple):
310
+ message, *exc_args = message # type: ignore
311
+
312
+ if message is None:
313
+ message = default_message
314
+
315
+ self._late_configure()
316
+
317
+ if render_fmt is not None and (af := self._args_renderer) is not None:
318
+ rendered_args = af(render_fmt, *ak.args)
319
+ if rendered_args is not None:
320
+ message = f'{message} : {rendered_args}'
321
+
322
+ exc = self._exception_factory(
323
+ exception_type,
324
+ message,
325
+ *exc_args,
326
+ *ak.args,
327
+ **ak.kwargs,
328
+ )
329
+
330
+ for fn in self._on_raise_fns:
331
+ fn(exc)
332
+
333
+ raise exc
334
+
335
+ #
336
+
337
+ def _unpack_isinstance_spec(self, spec: ta.Any) -> tuple:
338
+ if isinstance(spec, type):
339
+ return (spec,)
340
+ if not isinstance(spec, tuple):
341
+ spec = (spec,)
342
+ if None in spec:
343
+ spec = tuple(filter(None, spec)) + (None.__class__,) # noqa
344
+ if ta.Any in spec:
345
+ spec = (object,)
346
+ return spec
347
+
348
+ @ta.overload
349
+ def isinstance(self, v: ta.Any, spec: ta.Type[T], msg: CheckMessage = None) -> T:
350
+ ...
351
+
352
+ @ta.overload
353
+ def isinstance(self, v: ta.Any, spec: ta.Any, msg: CheckMessage = None) -> ta.Any:
354
+ ...
355
+
356
+ def isinstance(self, v, spec, msg=None):
357
+ if not isinstance(v, self._unpack_isinstance_spec(spec)):
358
+ self._raise(
359
+ TypeError,
360
+ 'Must be instance',
361
+ msg,
362
+ Checks._ArgsKwargs(v, spec),
363
+ render_fmt='not isinstance(%s, %s)',
364
+ )
365
+
366
+ return v
367
+
368
+ @ta.overload
369
+ def of_isinstance(self, spec: ta.Type[T], msg: CheckMessage = None) -> ta.Callable[[ta.Any], T]:
370
+ ...
371
+
372
+ @ta.overload
373
+ def of_isinstance(self, spec: ta.Any, msg: CheckMessage = None) -> ta.Callable[[ta.Any], ta.Any]:
374
+ ...
375
+
376
+ def of_isinstance(self, spec, msg=None):
377
+ def inner(v):
378
+ return self.isinstance(v, self._unpack_isinstance_spec(spec), msg)
379
+
380
+ return inner
381
+
382
+ def cast(self, v: ta.Any, cls: ta.Type[T], msg: CheckMessage = None) -> T:
383
+ if not isinstance(v, cls):
384
+ self._raise(
385
+ TypeError,
386
+ 'Must be instance',
387
+ msg,
388
+ Checks._ArgsKwargs(v, cls),
389
+ )
390
+
391
+ return v
392
+
393
+ def of_cast(self, cls: ta.Type[T], msg: CheckMessage = None) -> ta.Callable[[T], T]:
394
+ def inner(v):
395
+ return self.cast(v, cls, msg)
396
+
397
+ return inner
398
+
399
+ def not_isinstance(self, v: T, spec: ta.Any, msg: CheckMessage = None) -> T: # noqa
400
+ if isinstance(v, self._unpack_isinstance_spec(spec)):
401
+ self._raise(
402
+ TypeError,
403
+ 'Must not be instance',
404
+ msg,
405
+ Checks._ArgsKwargs(v, spec),
406
+ render_fmt='isinstance(%s, %s)',
407
+ )
408
+
409
+ return v
410
+
411
+ def of_not_isinstance(self, spec: ta.Any, msg: CheckMessage = None) -> ta.Callable[[T], T]:
412
+ def inner(v):
413
+ return self.not_isinstance(v, self._unpack_isinstance_spec(spec), msg)
414
+
415
+ return inner
416
+
417
+ ##
418
+
419
+ def issubclass(self, v: ta.Type[T], spec: ta.Any, msg: CheckMessage = None) -> ta.Type[T]: # noqa
420
+ if not issubclass(v, spec):
421
+ self._raise(
422
+ TypeError,
423
+ 'Must be subclass',
424
+ msg,
425
+ Checks._ArgsKwargs(v, spec),
426
+ render_fmt='not issubclass(%s, %s)',
427
+ )
428
+
429
+ return v
430
+
431
+ def not_issubclass(self, v: ta.Type[T], spec: ta.Any, msg: CheckMessage = None) -> ta.Type[T]:
432
+ if issubclass(v, spec):
433
+ self._raise(
434
+ TypeError,
435
+ 'Must not be subclass',
436
+ msg,
437
+ Checks._ArgsKwargs(v, spec),
438
+ render_fmt='issubclass(%s, %s)',
439
+ )
440
+
441
+ return v
442
+
443
+ #
444
+
445
+ def in_(self, v: T, c: ta.Container[T], msg: CheckMessage = None) -> T:
446
+ if v not in c:
447
+ self._raise(
448
+ ValueError,
449
+ 'Must be in',
450
+ msg,
451
+ Checks._ArgsKwargs(v, c),
452
+ render_fmt='%s not in %s',
453
+ )
454
+
455
+ return v
456
+
457
+ def not_in(self, v: T, c: ta.Container[T], msg: CheckMessage = None) -> T:
458
+ if v in c:
459
+ self._raise(
460
+ ValueError,
461
+ 'Must not be in',
462
+ msg,
463
+ Checks._ArgsKwargs(v, c),
464
+ render_fmt='%s in %s',
465
+ )
466
+
467
+ return v
468
+
469
+ def empty(self, v: SizedT, msg: CheckMessage = None) -> SizedT:
470
+ if len(v) != 0:
471
+ self._raise(
472
+ ValueError,
473
+ 'Must be empty',
474
+ msg,
475
+ Checks._ArgsKwargs(v),
476
+ render_fmt='%s',
477
+ )
478
+
479
+ return v
480
+
481
+ def iterempty(self, v: ta.Iterable[T], msg: CheckMessage = None) -> ta.Iterable[T]:
482
+ it = iter(v)
483
+ try:
484
+ next(it)
485
+ except StopIteration:
486
+ pass
487
+ else:
488
+ self._raise(
489
+ ValueError,
490
+ 'Must be empty',
491
+ msg,
492
+ Checks._ArgsKwargs(v),
493
+ render_fmt='%s',
494
+ )
495
+
496
+ return v
497
+
498
+ def not_empty(self, v: SizedT, msg: CheckMessage = None) -> SizedT:
499
+ if len(v) == 0:
500
+ self._raise(
501
+ ValueError,
502
+ 'Must not be empty',
503
+ msg,
504
+ Checks._ArgsKwargs(v),
505
+ render_fmt='%s',
506
+ )
507
+
508
+ return v
509
+
510
+ def unique(self, it: ta.Iterable[T], msg: CheckMessage = None) -> ta.Iterable[T]:
511
+ dupes = [e for e, c in collections.Counter(it).items() if c > 1]
512
+ if dupes:
513
+ self._raise(
514
+ ValueError,
515
+ 'Must be unique',
516
+ msg,
517
+ Checks._ArgsKwargs(it, dupes),
518
+ )
519
+
520
+ return it
521
+
522
+ def single(self, obj: ta.Iterable[T], msg: CheckMessage = None) -> T:
523
+ try:
524
+ [value] = obj
525
+ except ValueError:
526
+ self._raise(
527
+ ValueError,
528
+ 'Must be single',
529
+ msg,
530
+ Checks._ArgsKwargs(obj),
531
+ render_fmt='%s',
532
+ )
533
+
534
+ return value
535
+
536
+ def opt_single(self, obj: ta.Iterable[T], msg: CheckMessage = None) -> ta.Optional[T]:
537
+ it = iter(obj)
538
+ try:
539
+ value = next(it)
540
+ except StopIteration:
541
+ return None
542
+
543
+ try:
544
+ next(it)
545
+ except StopIteration:
546
+ return value # noqa
547
+
548
+ self._raise(
549
+ ValueError,
550
+ 'Must be empty or single',
551
+ msg,
552
+ Checks._ArgsKwargs(obj),
553
+ render_fmt='%s',
554
+ )
555
+
556
+ raise RuntimeError # noqa
557
+
558
+ #
559
+
560
+ def none(self, v: ta.Any, msg: CheckMessage = None) -> None:
561
+ if v is not None:
562
+ self._raise(
563
+ ValueError,
564
+ 'Must be None',
565
+ msg,
566
+ Checks._ArgsKwargs(v),
567
+ render_fmt='%s',
568
+ )
569
+
570
+ def not_none(self, v: ta.Optional[T], msg: CheckMessage = None) -> T:
571
+ if v is None:
572
+ self._raise(
573
+ ValueError,
574
+ 'Must not be None',
575
+ msg,
576
+ Checks._ArgsKwargs(v),
577
+ render_fmt='%s',
578
+ )
579
+
580
+ return v
581
+
582
+ #
583
+
584
+ def equal(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
585
+ if o != v:
586
+ self._raise(
587
+ ValueError,
588
+ 'Must be equal',
589
+ msg,
590
+ Checks._ArgsKwargs(v, o),
591
+ render_fmt='%s != %s',
592
+ )
593
+
594
+ return v
595
+
596
+ def not_equal(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
597
+ if o == v:
598
+ self._raise(
599
+ ValueError,
600
+ 'Must not be equal',
601
+ msg,
602
+ Checks._ArgsKwargs(v, o),
603
+ render_fmt='%s == %s',
604
+ )
605
+
606
+ return v
607
+
608
+ def is_(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
609
+ if o is not v:
610
+ self._raise(
611
+ ValueError,
612
+ 'Must be the same',
613
+ msg,
614
+ Checks._ArgsKwargs(v, o),
615
+ render_fmt='%s is not %s',
616
+ )
617
+
618
+ return v
619
+
620
+ def is_not(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
621
+ if o is v:
622
+ self._raise(
623
+ ValueError,
624
+ 'Must not be the same',
625
+ msg,
626
+ Checks._ArgsKwargs(v, o),
627
+ render_fmt='%s is %s',
628
+ )
629
+
630
+ return v
631
+
632
+ def callable(self, v: T, msg: CheckMessage = None) -> T: # noqa
633
+ if not callable(v):
634
+ self._raise(
635
+ TypeError,
636
+ 'Must be callable',
637
+ msg,
638
+ Checks._ArgsKwargs(v),
639
+ render_fmt='%s',
640
+ )
641
+
642
+ return v
643
+
644
+ def non_empty_str(self, v: ta.Optional[str], msg: CheckMessage = None) -> str:
645
+ if not isinstance(v, str) or not v:
646
+ self._raise(
647
+ ValueError,
648
+ 'Must be non-empty str',
649
+ msg,
650
+ Checks._ArgsKwargs(v),
651
+ render_fmt='%s',
652
+ )
653
+
654
+ return v
655
+
656
+ def replacing(self, expected: ta.Any, old: ta.Any, new: T, msg: CheckMessage = None) -> T:
657
+ if old != expected:
658
+ self._raise(
659
+ ValueError,
660
+ 'Must be replacing',
661
+ msg,
662
+ Checks._ArgsKwargs(expected, old, new),
663
+ render_fmt='%s -> %s -> %s',
664
+ )
665
+
666
+ return new
667
+
668
+ def replacing_none(self, old: ta.Any, new: T, msg: CheckMessage = None) -> T:
669
+ if old is not None:
670
+ self._raise(
671
+ ValueError,
672
+ 'Must be replacing None',
673
+ msg,
674
+ Checks._ArgsKwargs(old, new),
675
+ render_fmt='%s -> %s',
676
+ )
677
+
678
+ return new
679
+
680
+ #
681
+
682
+ def arg(self, v: bool, msg: CheckMessage = None) -> None:
683
+ if not v:
684
+ self._raise(
685
+ RuntimeError,
686
+ 'Argument condition not met',
687
+ msg,
688
+ Checks._ArgsKwargs(v),
689
+ render_fmt='%s',
690
+ )
691
+
692
+ def state(self, v: bool, msg: CheckMessage = None) -> None:
693
+ if not v:
694
+ self._raise(
695
+ RuntimeError,
696
+ 'State condition not met',
697
+ msg,
698
+ Checks._ArgsKwargs(v),
699
+ render_fmt='%s',
700
+ )
701
+
702
+
703
+ check = Checks()
704
+
705
+
706
+ ########################################
707
+ # ../objects.py
708
+
709
+
710
+ ##
711
+
712
+
713
+ def deep_subclasses(cls: ta.Type[T]) -> ta.Iterator[ta.Type[T]]:
714
+ seen = set()
715
+ todo = list(reversed(cls.__subclasses__()))
716
+ while todo:
717
+ cur = todo.pop()
718
+ if cur in seen:
719
+ continue
720
+ seen.add(cur)
721
+ yield cur
722
+ todo.extend(reversed(cur.__subclasses__()))
723
+
724
+
725
+ ##
726
+
727
+
728
+ def mro_owner_dict(
729
+ instance_cls: type,
730
+ owner_cls: ta.Optional[type] = None,
731
+ *,
732
+ bottom_up_key_order: bool = False,
733
+ sort_keys: bool = False,
734
+ ) -> ta.Mapping[str, ta.Tuple[type, ta.Any]]:
735
+ if owner_cls is None:
736
+ owner_cls = instance_cls
737
+
738
+ mro = instance_cls.__mro__[-2::-1]
739
+ try:
740
+ pos = mro.index(owner_cls)
741
+ except ValueError:
742
+ raise TypeError(f'Owner class {owner_cls} not in mro of instance class {instance_cls}') from None
743
+
744
+ dct: ta.Dict[str, ta.Tuple[type, ta.Any]] = {}
745
+ if not bottom_up_key_order:
746
+ for cur_cls in mro[:pos + 1][::-1]:
747
+ for k, v in cur_cls.__dict__.items():
748
+ if k not in dct:
749
+ dct[k] = (cur_cls, v)
750
+
751
+ else:
752
+ for cur_cls in mro[:pos + 1]:
753
+ dct.update({k: (cur_cls, v) for k, v in cur_cls.__dict__.items()})
754
+
755
+ if sort_keys:
756
+ dct = dict(sorted(dct.items(), key=lambda t: t[0]))
757
+
758
+ return dct
759
+
760
+
761
+ def mro_dict(
762
+ instance_cls: type,
763
+ owner_cls: ta.Optional[type] = None,
764
+ *,
765
+ bottom_up_key_order: bool = False,
766
+ sort_keys: bool = False,
767
+ ) -> ta.Mapping[str, ta.Any]:
768
+ return {
769
+ k: v
770
+ for k, (o, v) in mro_owner_dict(
771
+ instance_cls,
772
+ owner_cls,
773
+ bottom_up_key_order=bottom_up_key_order,
774
+ sort_keys=sort_keys,
775
+ ).items()
776
+ }
777
+
778
+
779
+ def dir_dict(o: ta.Any) -> ta.Dict[str, ta.Any]:
780
+ return {
781
+ a: getattr(o, a)
782
+ for a in dir(o)
783
+ }
784
+
785
+
786
+ ########################################
787
+ # ../reflect.py
788
+
789
+
790
+ ##
791
+
792
+
793
+ _GENERIC_ALIAS_TYPES = (
794
+ ta._GenericAlias, # type: ignore # noqa
795
+ *([ta._SpecialGenericAlias] if hasattr(ta, '_SpecialGenericAlias') else []), # noqa
796
+ )
797
+
798
+
799
+ def is_generic_alias(obj: ta.Any, *, origin: ta.Any = None) -> bool:
800
+ return (
801
+ isinstance(obj, _GENERIC_ALIAS_TYPES) and
802
+ (origin is None or ta.get_origin(obj) is origin)
803
+ )
804
+
805
+
806
+ is_callable_alias = functools.partial(is_generic_alias, origin=ta.Callable)
807
+
808
+
809
+ ##
810
+
811
+
812
+ _UNION_ALIAS_ORIGINS = frozenset([
813
+ ta.get_origin(ta.Optional[int]),
814
+ *(
815
+ [
816
+ ta.get_origin(int | None),
817
+ ta.get_origin(getattr(ta, 'TypeVar')('_T') | None),
818
+ ] if sys.version_info >= (3, 10) else ()
819
+ ),
820
+ ])
821
+
822
+
823
+ def is_union_alias(obj: ta.Any) -> bool:
824
+ return ta.get_origin(obj) in _UNION_ALIAS_ORIGINS
825
+
826
+
827
+ #
828
+
829
+
830
+ def is_optional_alias(spec: ta.Any) -> bool:
831
+ return (
832
+ is_union_alias(spec) and
833
+ len(ta.get_args(spec)) == 2 and
834
+ any(a in (None, type(None)) for a in ta.get_args(spec))
835
+ )
836
+
837
+
838
+ def get_optional_alias_arg(spec: ta.Any) -> ta.Any:
839
+ [it] = [it for it in ta.get_args(spec) if it not in (None, type(None))]
840
+ return it
841
+
842
+
843
+ ##
844
+
845
+
846
+ def is_new_type(spec: ta.Any) -> bool:
847
+ if isinstance(ta.NewType, type):
848
+ return isinstance(spec, ta.NewType)
849
+ else:
850
+ # Before https://github.com/python/cpython/commit/c2f33dfc83ab270412bf243fb21f724037effa1a
851
+ return isinstance(spec, types.FunctionType) and spec.__code__ is ta.NewType.__code__.co_consts[1] # type: ignore # noqa
852
+
853
+
854
+ def get_new_type_supertype(spec: ta.Any) -> ta.Any:
855
+ return spec.__supertype__
856
+
857
+
858
+ ##
859
+
860
+
861
+ def is_literal_type(spec: ta.Any) -> bool:
862
+ if hasattr(ta, '_LiteralGenericAlias'):
863
+ return isinstance(spec, ta._LiteralGenericAlias) # noqa
864
+ else:
865
+ return (
866
+ isinstance(spec, ta._GenericAlias) and # type: ignore # noqa
867
+ spec.__origin__ is ta.Literal
868
+ )
869
+
870
+
871
+ def get_literal_type_args(spec: ta.Any) -> ta.Iterable[ta.Any]:
872
+ return spec.__args__
873
+
874
+
875
+ ########################################
876
+ # ../strings.py
877
+
878
+
879
+ ##
880
+
881
+
882
+ def camel_case(name: str, *, lower: bool = False) -> str:
883
+ if not name:
884
+ return ''
885
+ s = ''.join(map(str.capitalize, name.split('_'))) # noqa
886
+ if lower:
887
+ s = s[0].lower() + s[1:]
888
+ return s
889
+
890
+
891
+ def snake_case(name: str) -> str:
892
+ uppers: list[int | None] = [i for i, c in enumerate(name) if c.isupper()]
893
+ return '_'.join([name[l:r].lower() for l, r in zip([None, *uppers], [*uppers, None])]).strip('_')
894
+
895
+
896
+ ##
897
+
898
+
899
+ def is_dunder(name: str) -> bool:
900
+ return (
901
+ name[:2] == name[-2:] == '__' and
902
+ name[2:3] != '_' and
903
+ name[-3:-2] != '_' and
904
+ len(name) > 4
905
+ )
906
+
907
+
908
+ def is_sunder(name: str) -> bool:
909
+ return (
910
+ name[0] == name[-1] == '_' and
911
+ name[1:2] != '_' and
912
+ name[-2:-1] != '_' and
913
+ len(name) > 2
914
+ )
915
+
916
+
917
+ ##
918
+
919
+
920
+ def strip_with_newline(s: str) -> str:
921
+ if not s:
922
+ return ''
923
+ return s.strip() + '\n'
924
+
925
+
926
+ @ta.overload
927
+ def split_keep_delimiter(s: str, d: str) -> str:
928
+ ...
929
+
930
+
931
+ @ta.overload
932
+ def split_keep_delimiter(s: bytes, d: bytes) -> bytes:
933
+ ...
934
+
935
+
936
+ def split_keep_delimiter(s, d):
937
+ ps = []
938
+ i = 0
939
+ while i < len(s):
940
+ if (n := s.find(d, i)) < i:
941
+ ps.append(s[i:])
942
+ break
943
+ ps.append(s[i:n + 1])
944
+ i = n + 1
945
+ return ps
946
+
947
+
948
+ ##
949
+
950
+
951
+ FORMAT_NUM_BYTES_SUFFIXES: ta.Sequence[str] = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB']
952
+
953
+
954
+ def format_num_bytes(num_bytes: int) -> str:
955
+ for i, suffix in enumerate(FORMAT_NUM_BYTES_SUFFIXES):
956
+ value = num_bytes / 1024 ** i
957
+ if num_bytes < 1024 ** (i + 1):
958
+ if value.is_integer():
959
+ return f'{int(value)}{suffix}'
960
+ else:
961
+ return f'{value:.2f}{suffix}'
962
+
963
+ return f'{num_bytes / 1024 ** (len(FORMAT_NUM_BYTES_SUFFIXES) - 1):.2f}{FORMAT_NUM_BYTES_SUFFIXES[-1]}'
964
+
965
+
966
+ ########################################
967
+ # marshal.py
968
+
969
+
970
+ ##
971
+
972
+
973
+ @dc.dataclass(frozen=True)
974
+ class ObjMarshalOptions:
975
+ raw_bytes: bool = False
976
+ non_strict_fields: bool = False
977
+
978
+
979
+ class ObjMarshaler(Abstract):
980
+ @abc.abstractmethod
981
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
982
+ raise NotImplementedError
983
+
984
+ @abc.abstractmethod
985
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
986
+ raise NotImplementedError
987
+
988
+
989
+ class NopObjMarshaler(ObjMarshaler):
990
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
991
+ return o
992
+
993
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
994
+ return o
995
+
996
+
997
+ class ProxyObjMarshaler(ObjMarshaler):
998
+ def __init__(self, m: ta.Optional[ObjMarshaler] = None) -> None:
999
+ super().__init__()
1000
+
1001
+ self._m = m
1002
+
1003
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1004
+ return check.not_none(self._m).marshal(o, ctx)
1005
+
1006
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1007
+ return check.not_none(self._m).unmarshal(o, ctx)
1008
+
1009
+
1010
+ class CastObjMarshaler(ObjMarshaler):
1011
+ def __init__(self, ty: type) -> None:
1012
+ super().__init__()
1013
+
1014
+ self._ty = ty
1015
+
1016
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1017
+ return o
1018
+
1019
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1020
+ return self._ty(o)
1021
+
1022
+
1023
+ class DynamicObjMarshaler(ObjMarshaler):
1024
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1025
+ return ctx.manager.marshal_obj(o, opts=ctx.options)
1026
+
1027
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1028
+ return o
1029
+
1030
+
1031
+ class Base64ObjMarshaler(ObjMarshaler):
1032
+ def __init__(self, ty: type) -> None:
1033
+ super().__init__()
1034
+
1035
+ self._ty = ty
1036
+
1037
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1038
+ return base64.b64encode(o).decode('ascii')
1039
+
1040
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1041
+ return self._ty(base64.b64decode(o))
1042
+
1043
+
1044
+ class BytesSwitchedObjMarshaler(ObjMarshaler):
1045
+ def __init__(self, m: ObjMarshaler) -> None:
1046
+ super().__init__()
1047
+
1048
+ self._m = m
1049
+
1050
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1051
+ if ctx.options.raw_bytes:
1052
+ return o
1053
+ return self._m.marshal(o, ctx)
1054
+
1055
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1056
+ if ctx.options.raw_bytes:
1057
+ return o
1058
+ return self._m.unmarshal(o, ctx)
1059
+
1060
+
1061
+ class EnumObjMarshaler(ObjMarshaler):
1062
+ def __init__(self, ty: type) -> None:
1063
+ super().__init__()
1064
+
1065
+ self._ty = ty
1066
+
1067
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1068
+ return o.name
1069
+
1070
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1071
+ return self._ty.__members__[o] # type: ignore
1072
+
1073
+
1074
+ class OptionalObjMarshaler(ObjMarshaler):
1075
+ def __init__(self, item: ObjMarshaler) -> None:
1076
+ super().__init__()
1077
+
1078
+ self._item = item
1079
+
1080
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1081
+ if o is None:
1082
+ return None
1083
+ return self._item.marshal(o, ctx)
1084
+
1085
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1086
+ if o is None:
1087
+ return None
1088
+ return self._item.unmarshal(o, ctx)
1089
+
1090
+
1091
+ class PrimitiveUnionObjMarshaler(ObjMarshaler):
1092
+ def __init__(
1093
+ self,
1094
+ pt: ta.Tuple[type, ...],
1095
+ x: ta.Optional[ObjMarshaler] = None,
1096
+ ) -> None:
1097
+ super().__init__()
1098
+
1099
+ self._pt = pt
1100
+ self._x = x
1101
+
1102
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1103
+ if isinstance(o, self._pt):
1104
+ return o
1105
+ elif self._x is not None:
1106
+ return self._x.marshal(o, ctx)
1107
+ else:
1108
+ raise TypeError(o)
1109
+
1110
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1111
+ if isinstance(o, self._pt):
1112
+ return o
1113
+ elif self._x is not None:
1114
+ return self._x.unmarshal(o, ctx)
1115
+ else:
1116
+ raise TypeError(o)
1117
+
1118
+
1119
+ class LiteralObjMarshaler(ObjMarshaler):
1120
+ def __init__(
1121
+ self,
1122
+ item: ObjMarshaler,
1123
+ vs: frozenset,
1124
+ ) -> None:
1125
+ super().__init__()
1126
+
1127
+ self._item = item
1128
+ self._vs = vs
1129
+
1130
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1131
+ return self._item.marshal(check.in_(o, self._vs), ctx)
1132
+
1133
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1134
+ return check.in_(self._item.unmarshal(o, ctx), self._vs)
1135
+
1136
+
1137
+ class MappingObjMarshaler(ObjMarshaler):
1138
+ def __init__(
1139
+ self,
1140
+ ty: type,
1141
+ km: ObjMarshaler,
1142
+ vm: ObjMarshaler,
1143
+ ) -> None:
1144
+ super().__init__()
1145
+
1146
+ self._ty = ty
1147
+ self._km = km
1148
+ self._vm = vm
1149
+
1150
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1151
+ return {self._km.marshal(k, ctx): self._vm.marshal(v, ctx) for k, v in o.items()}
1152
+
1153
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1154
+ return self._ty((self._km.unmarshal(k, ctx), self._vm.unmarshal(v, ctx)) for k, v in o.items())
1155
+
1156
+
1157
+ class IterableObjMarshaler(ObjMarshaler):
1158
+ def __init__(
1159
+ self,
1160
+ ty: type,
1161
+ item: ObjMarshaler,
1162
+ ) -> None:
1163
+ super().__init__()
1164
+
1165
+ self._ty = ty
1166
+ self._item = item
1167
+
1168
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1169
+ return [self._item.marshal(e, ctx) for e in o]
1170
+
1171
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1172
+ return self._ty(self._item.unmarshal(e, ctx) for e in o)
1173
+
1174
+
1175
+ class FieldsObjMarshaler(ObjMarshaler):
1176
+ @dc.dataclass(frozen=True)
1177
+ class Field:
1178
+ att: str
1179
+ key: str
1180
+ m: ObjMarshaler
1181
+
1182
+ omit_if_none: bool = False
1183
+
1184
+ def __init__(
1185
+ self,
1186
+ ty: type,
1187
+ fs: ta.Sequence[Field],
1188
+ *,
1189
+ non_strict: bool = False,
1190
+ ) -> None:
1191
+ super().__init__()
1192
+
1193
+ self._ty = ty
1194
+ self._fs = fs
1195
+ self._non_strict = non_strict
1196
+
1197
+ fs_by_att: dict = {}
1198
+ fs_by_key: dict = {}
1199
+ for f in self._fs:
1200
+ check.not_in(check.non_empty_str(f.att), fs_by_att)
1201
+ check.not_in(check.non_empty_str(f.key), fs_by_key)
1202
+ fs_by_att[f.att] = f
1203
+ fs_by_key[f.key] = f
1204
+
1205
+ self._fs_by_att: ta.Mapping[str, FieldsObjMarshaler.Field] = fs_by_att
1206
+ self._fs_by_key: ta.Mapping[str, FieldsObjMarshaler.Field] = fs_by_key
1207
+
1208
+ @property
1209
+ def ty(self) -> type:
1210
+ return self._ty
1211
+
1212
+ @property
1213
+ def fs(self) -> ta.Sequence[Field]:
1214
+ return self._fs
1215
+
1216
+ #
1217
+
1218
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1219
+ d = {}
1220
+ for f in self._fs:
1221
+ mv = f.m.marshal(getattr(o, f.att), ctx)
1222
+ if mv is None and f.omit_if_none:
1223
+ continue
1224
+ d[f.key] = mv
1225
+ return d
1226
+
1227
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1228
+ kw = {}
1229
+ for k, v in o.items():
1230
+ if (f := self._fs_by_key.get(k)) is None:
1231
+ if not (self._non_strict or ctx.options.non_strict_fields):
1232
+ raise KeyError(k)
1233
+ continue
1234
+ kw[f.att] = f.m.unmarshal(v, ctx)
1235
+ return self._ty(**kw)
1236
+
1237
+
1238
+ class SingleFieldObjMarshaler(ObjMarshaler):
1239
+ def __init__(
1240
+ self,
1241
+ ty: type,
1242
+ fld: str,
1243
+ ) -> None:
1244
+ super().__init__()
1245
+
1246
+ self._ty = ty
1247
+ self._fld = fld
1248
+
1249
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1250
+ return getattr(o, self._fld)
1251
+
1252
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1253
+ return self._ty(**{self._fld: o})
1254
+
1255
+
1256
+ class PolymorphicObjMarshaler(ObjMarshaler):
1257
+ class Impl(ta.NamedTuple):
1258
+ ty: type
1259
+ tag: str
1260
+ m: ObjMarshaler
1261
+
1262
+ def __init__(
1263
+ self,
1264
+ impls_by_ty: ta.Mapping[type, Impl],
1265
+ impls_by_tag: ta.Mapping[str, Impl],
1266
+ ) -> None:
1267
+ super().__init__()
1268
+
1269
+ self._impls_by_ty = impls_by_ty
1270
+ self._impls_by_tag = impls_by_tag
1271
+
1272
+ @classmethod
1273
+ def of(cls, impls: ta.Iterable[Impl]) -> 'PolymorphicObjMarshaler':
1274
+ return cls(
1275
+ {i.ty: i for i in impls},
1276
+ {i.tag: i for i in impls},
1277
+ )
1278
+
1279
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1280
+ impl = self._impls_by_ty[type(o)]
1281
+ return {impl.tag: impl.m.marshal(o, ctx)}
1282
+
1283
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1284
+ [(t, v)] = o.items()
1285
+ impl = self._impls_by_tag[t]
1286
+ return impl.m.unmarshal(v, ctx)
1287
+
1288
+
1289
+ class DatetimeObjMarshaler(ObjMarshaler):
1290
+ def __init__(
1291
+ self,
1292
+ ty: type,
1293
+ ) -> None:
1294
+ super().__init__()
1295
+
1296
+ self._ty = ty
1297
+
1298
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1299
+ return o.isoformat()
1300
+
1301
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1302
+ return self._ty.fromisoformat(o) # type: ignore
1303
+
1304
+
1305
+ class DecimalObjMarshaler(ObjMarshaler):
1306
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1307
+ return str(check.isinstance(o, decimal.Decimal))
1308
+
1309
+ def unmarshal(self, v: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1310
+ return decimal.Decimal(check.isinstance(v, str))
1311
+
1312
+
1313
+ class FractionObjMarshaler(ObjMarshaler):
1314
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1315
+ fr = check.isinstance(o, fractions.Fraction)
1316
+ return [fr.numerator, fr.denominator]
1317
+
1318
+ def unmarshal(self, v: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1319
+ num, denom = check.isinstance(v, list)
1320
+ return fractions.Fraction(num, denom)
1321
+
1322
+
1323
+ class UuidObjMarshaler(ObjMarshaler):
1324
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1325
+ return str(o)
1326
+
1327
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
1328
+ return uuid.UUID(o)
1329
+
1330
+
1331
+ ##
1332
+
1333
+
1334
+ _DEFAULT_OBJ_MARSHALERS: ta.Dict[ta.Any, ObjMarshaler] = {
1335
+ **{t: NopObjMarshaler() for t in (type(None),)},
1336
+ **{t: CastObjMarshaler(t) for t in (int, float, str, bool)},
1337
+ **{t: BytesSwitchedObjMarshaler(Base64ObjMarshaler(t)) for t in (bytes, bytearray)},
1338
+ **{t: IterableObjMarshaler(t, DynamicObjMarshaler()) for t in (list, tuple, set, frozenset)},
1339
+ **{t: MappingObjMarshaler(t, DynamicObjMarshaler(), DynamicObjMarshaler()) for t in (dict,)},
1340
+
1341
+ **{t: DynamicObjMarshaler() for t in (ta.Any, object)},
1342
+
1343
+ **{t: DatetimeObjMarshaler(t) for t in (datetime.date, datetime.time, datetime.datetime)},
1344
+ decimal.Decimal: DecimalObjMarshaler(),
1345
+ fractions.Fraction: FractionObjMarshaler(),
1346
+ uuid.UUID: UuidObjMarshaler(),
1347
+ }
1348
+
1349
+ _OBJ_MARSHALER_GENERIC_MAPPING_TYPES: ta.Dict[ta.Any, type] = {
1350
+ **{t: t for t in (dict,)},
1351
+ **{t: dict for t in (collections.abc.Mapping, collections.abc.MutableMapping)}, # noqa
1352
+ }
1353
+
1354
+ _OBJ_MARSHALER_GENERIC_ITERABLE_TYPES: ta.Dict[ta.Any, type] = {
1355
+ **{t: t for t in (list, tuple, set, frozenset)},
1356
+ collections.abc.Set: frozenset,
1357
+ collections.abc.MutableSet: set,
1358
+ collections.abc.Sequence: tuple,
1359
+ collections.abc.MutableSequence: list,
1360
+ }
1361
+
1362
+ _OBJ_MARSHALER_PRIMITIVE_TYPES: ta.Set[type] = {
1363
+ int,
1364
+ float,
1365
+ bool,
1366
+ str,
1367
+ }
1368
+
1369
+
1370
+ ##
1371
+
1372
+
1373
+ _REGISTERED_OBJ_MARSHALERS_BY_TYPE: ta.MutableMapping[type, ObjMarshaler] = weakref.WeakKeyDictionary()
1374
+
1375
+
1376
+ def register_type_obj_marshaler(ty: type, om: ObjMarshaler) -> None:
1377
+ _REGISTERED_OBJ_MARSHALERS_BY_TYPE[ty] = om
1378
+
1379
+
1380
+ def register_single_field_type_obj_marshaler(fld, ty=None):
1381
+ def inner(ty): # noqa
1382
+ register_type_obj_marshaler(ty, SingleFieldObjMarshaler(ty, fld))
1383
+ return ty
1384
+
1385
+ if ty is not None:
1386
+ return inner(ty)
1387
+ else:
1388
+ return inner
1389
+
1390
+
1391
+ ##
1392
+
1393
+
1394
+ class ObjMarshalerFieldMetadata:
1395
+ def __new__(cls, *args, **kwargs): # noqa
1396
+ raise TypeError
1397
+
1398
+
1399
+ class OBJ_MARSHALER_FIELD_KEY(ObjMarshalerFieldMetadata): # noqa
1400
+ pass
1401
+
1402
+
1403
+ class OBJ_MARSHALER_OMIT_IF_NONE(ObjMarshalerFieldMetadata): # noqa
1404
+ pass
1405
+
1406
+
1407
+ ##
1408
+
1409
+
1410
+ class ObjMarshalerManager(Abstract):
1411
+ @abc.abstractmethod
1412
+ def make_obj_marshaler(
1413
+ self,
1414
+ ty: ta.Any,
1415
+ rec: ta.Callable[[ta.Any], ObjMarshaler],
1416
+ *,
1417
+ non_strict_fields: bool = False,
1418
+ ) -> ObjMarshaler:
1419
+ raise NotImplementedError
1420
+
1421
+ @abc.abstractmethod
1422
+ def set_obj_marshaler(
1423
+ self,
1424
+ ty: ta.Any,
1425
+ m: ObjMarshaler,
1426
+ *,
1427
+ override: bool = False,
1428
+ ) -> None:
1429
+ raise NotImplementedError
1430
+
1431
+ @abc.abstractmethod
1432
+ def get_obj_marshaler(
1433
+ self,
1434
+ ty: ta.Any,
1435
+ *,
1436
+ no_cache: bool = False,
1437
+ **kwargs: ta.Any,
1438
+ ) -> ObjMarshaler:
1439
+ raise NotImplementedError
1440
+
1441
+ @abc.abstractmethod
1442
+ def make_context(self, opts: ta.Optional[ObjMarshalOptions]) -> 'ObjMarshalContext':
1443
+ raise NotImplementedError
1444
+
1445
+ #
1446
+
1447
+ def marshal_obj(
1448
+ self,
1449
+ o: ta.Any,
1450
+ ty: ta.Any = None,
1451
+ opts: ta.Optional[ObjMarshalOptions] = None,
1452
+ ) -> ta.Any:
1453
+ m = self.get_obj_marshaler(ty if ty is not None else type(o))
1454
+ return m.marshal(o, self.make_context(opts))
1455
+
1456
+ def unmarshal_obj(
1457
+ self,
1458
+ o: ta.Any,
1459
+ ty: ta.Union[ta.Type[T], ta.Any],
1460
+ opts: ta.Optional[ObjMarshalOptions] = None,
1461
+ ) -> T:
1462
+ m = self.get_obj_marshaler(ty)
1463
+ return m.unmarshal(o, self.make_context(opts))
1464
+
1465
+ def roundtrip_obj(
1466
+ self,
1467
+ o: ta.Any,
1468
+ ty: ta.Any = None,
1469
+ opts: ta.Optional[ObjMarshalOptions] = None,
1470
+ ) -> ta.Any:
1471
+ if ty is None:
1472
+ ty = type(o)
1473
+ m: ta.Any = self.marshal_obj(o, ty, opts)
1474
+ u: ta.Any = self.unmarshal_obj(m, ty, opts)
1475
+ return u
1476
+
1477
+
1478
+ #
1479
+
1480
+
1481
+ class ObjMarshalerManagerImpl(ObjMarshalerManager):
1482
+ def __init__(
1483
+ self,
1484
+ *,
1485
+ default_options: ObjMarshalOptions = ObjMarshalOptions(),
1486
+
1487
+ default_obj_marshalers: ta.Dict[ta.Any, ObjMarshaler] = _DEFAULT_OBJ_MARSHALERS, # noqa
1488
+ generic_mapping_types: ta.Dict[ta.Any, type] = _OBJ_MARSHALER_GENERIC_MAPPING_TYPES, # noqa
1489
+ generic_iterable_types: ta.Dict[ta.Any, type] = _OBJ_MARSHALER_GENERIC_ITERABLE_TYPES, # noqa
1490
+
1491
+ registered_obj_marshalers: ta.Mapping[type, ObjMarshaler] = _REGISTERED_OBJ_MARSHALERS_BY_TYPE,
1492
+ ) -> None:
1493
+ super().__init__()
1494
+
1495
+ self._default_options = default_options
1496
+
1497
+ self._obj_marshalers = dict(default_obj_marshalers)
1498
+ self._generic_mapping_types = generic_mapping_types
1499
+ self._generic_iterable_types = generic_iterable_types
1500
+ self._registered_obj_marshalers = registered_obj_marshalers
1501
+
1502
+ self._lock = threading.RLock()
1503
+ self._marshalers: ta.Dict[ta.Any, ObjMarshaler] = dict(_DEFAULT_OBJ_MARSHALERS)
1504
+ self._proxies: ta.Dict[ta.Any, ProxyObjMarshaler] = {}
1505
+
1506
+ #
1507
+
1508
+ @classmethod
1509
+ def _is_abstract(cls, ty: type) -> bool:
1510
+ return abc.ABC in ty.__bases__ or Abstract in ty.__bases__
1511
+
1512
+ #
1513
+
1514
+ def make_obj_marshaler(
1515
+ self,
1516
+ ty: ta.Any,
1517
+ rec: ta.Callable[[ta.Any], ObjMarshaler],
1518
+ *,
1519
+ non_strict_fields: bool = False,
1520
+ ) -> ObjMarshaler:
1521
+ if isinstance(ty, type):
1522
+ if (reg := self._registered_obj_marshalers.get(ty)) is not None:
1523
+ return reg
1524
+
1525
+ if self._is_abstract(ty):
1526
+ tn = ty.__name__
1527
+ impls: ta.List[ta.Tuple[type, str]] = [ # type: ignore[var-annotated]
1528
+ (ity, ity.__name__)
1529
+ for ity in deep_subclasses(ty)
1530
+ if not self._is_abstract(ity)
1531
+ ]
1532
+
1533
+ if all(itn.endswith(tn) for _, itn in impls):
1534
+ impls = [
1535
+ (ity, snake_case(itn[:-len(tn)]))
1536
+ for ity, itn in impls
1537
+ ]
1538
+
1539
+ dupe_tns = sorted(
1540
+ dn
1541
+ for dn, dc in collections.Counter(itn for _, itn in impls).items()
1542
+ if dc > 1
1543
+ )
1544
+ if dupe_tns:
1545
+ raise KeyError(f'Duplicate impl names for {ty}: {dupe_tns}')
1546
+
1547
+ return PolymorphicObjMarshaler.of([
1548
+ PolymorphicObjMarshaler.Impl(
1549
+ ity,
1550
+ itn,
1551
+ rec(ity),
1552
+ )
1553
+ for ity, itn in impls
1554
+ ])
1555
+
1556
+ if issubclass(ty, enum.Enum):
1557
+ return EnumObjMarshaler(ty)
1558
+
1559
+ if dc.is_dataclass(ty):
1560
+ return FieldsObjMarshaler(
1561
+ ty,
1562
+ [
1563
+ FieldsObjMarshaler.Field(
1564
+ att=f.name,
1565
+ key=check.non_empty_str(fk),
1566
+ m=rec(f.type),
1567
+ omit_if_none=check.isinstance(f.metadata.get(OBJ_MARSHALER_OMIT_IF_NONE, False), bool),
1568
+ )
1569
+ for f in dc.fields(ty)
1570
+ if (fk := f.metadata.get(OBJ_MARSHALER_FIELD_KEY, f.name)) is not None
1571
+ ],
1572
+ non_strict=non_strict_fields,
1573
+ )
1574
+
1575
+ if issubclass(ty, tuple) and hasattr(ty, '_fields'):
1576
+ return FieldsObjMarshaler(
1577
+ ty,
1578
+ [
1579
+ FieldsObjMarshaler.Field(
1580
+ att=p.name,
1581
+ key=p.name,
1582
+ m=rec(p.annotation),
1583
+ )
1584
+ for p in inspect.signature(ty).parameters.values()
1585
+ ],
1586
+ non_strict=non_strict_fields,
1587
+ )
1588
+
1589
+ if is_new_type(ty):
1590
+ return rec(get_new_type_supertype(ty))
1591
+
1592
+ if is_literal_type(ty):
1593
+ lvs = frozenset(get_literal_type_args(ty))
1594
+ if None in lvs:
1595
+ is_opt = True
1596
+ lvs -= frozenset([None])
1597
+ else:
1598
+ is_opt = False
1599
+ lty = check.single(set(map(type, lvs)))
1600
+ lm: ObjMarshaler = LiteralObjMarshaler(rec(lty), lvs)
1601
+ if is_opt:
1602
+ lm = OptionalObjMarshaler(lm)
1603
+ return lm
1604
+
1605
+ if is_generic_alias(ty):
1606
+ try:
1607
+ mt = self._generic_mapping_types[ta.get_origin(ty)]
1608
+ except KeyError:
1609
+ pass
1610
+ else:
1611
+ k, v = ta.get_args(ty)
1612
+ return MappingObjMarshaler(mt, rec(k), rec(v))
1613
+
1614
+ try:
1615
+ st = self._generic_iterable_types[ta.get_origin(ty)]
1616
+ except KeyError:
1617
+ pass
1618
+ else:
1619
+ [e] = ta.get_args(ty)
1620
+ return IterableObjMarshaler(st, rec(e))
1621
+
1622
+ if is_union_alias(ty):
1623
+ uts = frozenset(ta.get_args(ty))
1624
+ if None in uts or type(None) in uts:
1625
+ is_opt = True
1626
+ uts = frozenset(ut for ut in uts if ut not in (None, type(None)))
1627
+ else:
1628
+ is_opt = False
1629
+
1630
+ um: ObjMarshaler
1631
+ if not uts:
1632
+ raise TypeError(ty)
1633
+ elif len(uts) == 1:
1634
+ um = rec(check.single(uts))
1635
+ else:
1636
+ pt = tuple({ut for ut in uts if ut in _OBJ_MARSHALER_PRIMITIVE_TYPES})
1637
+ np_uts = {ut for ut in uts if ut not in _OBJ_MARSHALER_PRIMITIVE_TYPES}
1638
+ if not np_uts:
1639
+ um = PrimitiveUnionObjMarshaler(pt)
1640
+ elif len(np_uts) == 1:
1641
+ um = PrimitiveUnionObjMarshaler(pt, x=rec(check.single(np_uts)))
1642
+ else:
1643
+ raise TypeError(ty)
1644
+
1645
+ if is_opt:
1646
+ um = OptionalObjMarshaler(um)
1647
+ return um
1648
+
1649
+ raise TypeError(ty)
1650
+
1651
+ #
1652
+
1653
+ def set_obj_marshaler(
1654
+ self,
1655
+ ty: ta.Any,
1656
+ m: ObjMarshaler,
1657
+ *,
1658
+ override: bool = False,
1659
+ ) -> None:
1660
+ with self._lock:
1661
+ if not override and ty in self._obj_marshalers:
1662
+ raise KeyError(ty)
1663
+ self._obj_marshalers[ty] = m
1664
+
1665
+ def get_obj_marshaler(
1666
+ self,
1667
+ ty: ta.Any,
1668
+ *,
1669
+ no_cache: bool = False,
1670
+ **kwargs: ta.Any,
1671
+ ) -> ObjMarshaler:
1672
+ with self._lock:
1673
+ if not no_cache:
1674
+ try:
1675
+ return self._obj_marshalers[ty]
1676
+ except KeyError:
1677
+ pass
1678
+
1679
+ try:
1680
+ return self._proxies[ty]
1681
+ except KeyError:
1682
+ pass
1683
+
1684
+ rec = functools.partial(
1685
+ self.get_obj_marshaler,
1686
+ no_cache=no_cache,
1687
+ **kwargs,
1688
+ )
1689
+
1690
+ p = ProxyObjMarshaler()
1691
+ self._proxies[ty] = p
1692
+ try:
1693
+ m = self.make_obj_marshaler(ty, rec, **kwargs)
1694
+ finally:
1695
+ del self._proxies[ty]
1696
+ p._m = m # noqa
1697
+
1698
+ if not no_cache:
1699
+ self._obj_marshalers[ty] = m
1700
+ return m
1701
+
1702
+ def make_context(self, opts: ta.Optional[ObjMarshalOptions]) -> 'ObjMarshalContext':
1703
+ return ObjMarshalContext(
1704
+ options=opts or self._default_options,
1705
+ manager=self,
1706
+ )
1707
+
1708
+
1709
+ def new_obj_marshaler_manager(**kwargs: ta.Any) -> ObjMarshalerManager:
1710
+ return ObjMarshalerManagerImpl(**kwargs)
1711
+
1712
+
1713
+ ##
1714
+
1715
+
1716
+ @dc.dataclass(frozen=True)
1717
+ class ObjMarshalContext:
1718
+ options: ObjMarshalOptions
1719
+ manager: ObjMarshalerManager
1720
+
1721
+
1722
+ ##
1723
+
1724
+
1725
+ OBJ_MARSHALER_MANAGER = new_obj_marshaler_manager()
1726
+
1727
+ set_obj_marshaler = OBJ_MARSHALER_MANAGER.set_obj_marshaler
1728
+ get_obj_marshaler = OBJ_MARSHALER_MANAGER.get_obj_marshaler
1729
+
1730
+ marshal_obj = OBJ_MARSHALER_MANAGER.marshal_obj
1731
+ unmarshal_obj = OBJ_MARSHALER_MANAGER.unmarshal_obj