omdev 0.0.0.dev393__py3-none-any.whl → 0.0.0.dev394__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.
- omdev/cli/main.py +5 -2
- omdev/manifests/_dumping.py +1586 -0
- omdev/manifests/building.py +15 -17
- omdev/manifests/dumping.py +98 -75
- omdev/manifests/main.py +0 -2
- omdev/precheck/manifests.py +1 -1
- omdev/tools/git/messages.py +2 -2
- {omdev-0.0.0.dev393.dist-info → omdev-0.0.0.dev394.dist-info}/METADATA +2 -2
- {omdev-0.0.0.dev393.dist-info → omdev-0.0.0.dev394.dist-info}/RECORD +13 -12
- {omdev-0.0.0.dev393.dist-info → omdev-0.0.0.dev394.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev393.dist-info → omdev-0.0.0.dev394.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev393.dist-info → omdev-0.0.0.dev394.dist-info}/licenses/LICENSE +0 -0
- {omdev-0.0.0.dev393.dist-info → omdev-0.0.0.dev394.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1586 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
# noinspection DuplicatedCode
|
3
|
+
# @omlish-lite
|
4
|
+
# @omlish-script
|
5
|
+
# @omlish-generated
|
6
|
+
# @omlish-amalg-output dumping.py
|
7
|
+
# @omlish-git-diff-omit
|
8
|
+
# ruff: noqa: UP006 UP007 UP036 UP037 UP045
|
9
|
+
import abc
|
10
|
+
import base64
|
11
|
+
import collections
|
12
|
+
import collections.abc
|
13
|
+
import dataclasses as dc
|
14
|
+
import datetime
|
15
|
+
import decimal
|
16
|
+
import enum
|
17
|
+
import fractions
|
18
|
+
import functools
|
19
|
+
import importlib
|
20
|
+
import inspect
|
21
|
+
import json
|
22
|
+
import sys
|
23
|
+
import threading
|
24
|
+
import types
|
25
|
+
import typing as ta
|
26
|
+
import uuid
|
27
|
+
import weakref
|
28
|
+
|
29
|
+
|
30
|
+
########################################
|
31
|
+
|
32
|
+
|
33
|
+
if sys.version_info < (3, 8):
|
34
|
+
raise OSError(f'Requires python (3, 8), got {sys.version_info} from {sys.executable}') # noqa
|
35
|
+
|
36
|
+
|
37
|
+
########################################
|
38
|
+
|
39
|
+
|
40
|
+
# ../../omlish/lite/cached.py
|
41
|
+
T = ta.TypeVar('T')
|
42
|
+
CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
|
43
|
+
|
44
|
+
# ../../omlish/lite/check.py
|
45
|
+
SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
|
46
|
+
CheckMessage = ta.Union[str, ta.Callable[..., ta.Optional[str]], None] # ta.TypeAlias
|
47
|
+
CheckLateConfigureFn = ta.Callable[['Checks'], None] # ta.TypeAlias
|
48
|
+
CheckOnRaiseFn = ta.Callable[[Exception], None] # ta.TypeAlias
|
49
|
+
CheckExceptionFactory = ta.Callable[..., Exception] # ta.TypeAlias
|
50
|
+
CheckArgsRenderer = ta.Callable[..., ta.Optional[str]] # ta.TypeAlias
|
51
|
+
|
52
|
+
|
53
|
+
########################################
|
54
|
+
# ../../../omlish/lite/cached.py
|
55
|
+
|
56
|
+
|
57
|
+
##
|
58
|
+
|
59
|
+
|
60
|
+
class _AbstractCachedNullary:
|
61
|
+
def __init__(self, fn):
|
62
|
+
super().__init__()
|
63
|
+
self._fn = fn
|
64
|
+
self._value = self._missing = object()
|
65
|
+
functools.update_wrapper(self, fn)
|
66
|
+
|
67
|
+
def __call__(self, *args, **kwargs): # noqa
|
68
|
+
raise TypeError
|
69
|
+
|
70
|
+
def __get__(self, instance, owner): # noqa
|
71
|
+
bound = instance.__dict__[self._fn.__name__] = self.__class__(self._fn.__get__(instance, owner))
|
72
|
+
return bound
|
73
|
+
|
74
|
+
|
75
|
+
##
|
76
|
+
|
77
|
+
|
78
|
+
class _CachedNullary(_AbstractCachedNullary):
|
79
|
+
def __call__(self, *args, **kwargs): # noqa
|
80
|
+
if self._value is self._missing:
|
81
|
+
self._value = self._fn()
|
82
|
+
return self._value
|
83
|
+
|
84
|
+
|
85
|
+
def cached_nullary(fn: CallableT) -> CallableT:
|
86
|
+
return _CachedNullary(fn) # type: ignore
|
87
|
+
|
88
|
+
|
89
|
+
def static_init(fn: CallableT) -> CallableT:
|
90
|
+
fn = cached_nullary(fn)
|
91
|
+
fn()
|
92
|
+
return fn
|
93
|
+
|
94
|
+
|
95
|
+
##
|
96
|
+
|
97
|
+
|
98
|
+
class _AsyncCachedNullary(_AbstractCachedNullary):
|
99
|
+
async def __call__(self, *args, **kwargs):
|
100
|
+
if self._value is self._missing:
|
101
|
+
self._value = await self._fn()
|
102
|
+
return self._value
|
103
|
+
|
104
|
+
|
105
|
+
def async_cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
|
106
|
+
return _AsyncCachedNullary(fn)
|
107
|
+
|
108
|
+
|
109
|
+
########################################
|
110
|
+
# ../../../omlish/lite/check.py
|
111
|
+
"""
|
112
|
+
TODO:
|
113
|
+
- def maybe(v: lang.Maybe[T])
|
114
|
+
- def not_ ?
|
115
|
+
- ** class @dataclass Raise - user message should be able to be an exception type or instance or factory
|
116
|
+
"""
|
117
|
+
|
118
|
+
|
119
|
+
##
|
120
|
+
|
121
|
+
|
122
|
+
class Checks:
|
123
|
+
def __init__(self) -> None:
|
124
|
+
super().__init__()
|
125
|
+
|
126
|
+
self._config_lock = threading.RLock()
|
127
|
+
self._on_raise_fns: ta.Sequence[CheckOnRaiseFn] = []
|
128
|
+
self._exception_factory: CheckExceptionFactory = Checks.default_exception_factory
|
129
|
+
self._args_renderer: ta.Optional[CheckArgsRenderer] = None
|
130
|
+
self._late_configure_fns: ta.Sequence[CheckLateConfigureFn] = []
|
131
|
+
|
132
|
+
@staticmethod
|
133
|
+
def default_exception_factory(exc_cls: ta.Type[Exception], *args, **kwargs) -> Exception:
|
134
|
+
return exc_cls(*args, **kwargs) # noqa
|
135
|
+
|
136
|
+
#
|
137
|
+
|
138
|
+
def register_on_raise(self, fn: CheckOnRaiseFn) -> None:
|
139
|
+
with self._config_lock:
|
140
|
+
self._on_raise_fns = [*self._on_raise_fns, fn]
|
141
|
+
|
142
|
+
def unregister_on_raise(self, fn: CheckOnRaiseFn) -> None:
|
143
|
+
with self._config_lock:
|
144
|
+
self._on_raise_fns = [e for e in self._on_raise_fns if e != fn]
|
145
|
+
|
146
|
+
#
|
147
|
+
|
148
|
+
def register_on_raise_breakpoint_if_env_var_set(self, key: str) -> None:
|
149
|
+
import os
|
150
|
+
|
151
|
+
def on_raise(exc: Exception) -> None: # noqa
|
152
|
+
if key in os.environ:
|
153
|
+
breakpoint() # noqa
|
154
|
+
|
155
|
+
self.register_on_raise(on_raise)
|
156
|
+
|
157
|
+
#
|
158
|
+
|
159
|
+
def set_exception_factory(self, factory: CheckExceptionFactory) -> None:
|
160
|
+
self._exception_factory = factory
|
161
|
+
|
162
|
+
def set_args_renderer(self, renderer: ta.Optional[CheckArgsRenderer]) -> None:
|
163
|
+
self._args_renderer = renderer
|
164
|
+
|
165
|
+
#
|
166
|
+
|
167
|
+
def register_late_configure(self, fn: CheckLateConfigureFn) -> None:
|
168
|
+
with self._config_lock:
|
169
|
+
self._late_configure_fns = [*self._late_configure_fns, fn]
|
170
|
+
|
171
|
+
def _late_configure(self) -> None:
|
172
|
+
if not self._late_configure_fns:
|
173
|
+
return
|
174
|
+
|
175
|
+
with self._config_lock:
|
176
|
+
if not (lc := self._late_configure_fns):
|
177
|
+
return
|
178
|
+
|
179
|
+
for fn in lc:
|
180
|
+
fn(self)
|
181
|
+
|
182
|
+
self._late_configure_fns = []
|
183
|
+
|
184
|
+
#
|
185
|
+
|
186
|
+
class _ArgsKwargs:
|
187
|
+
def __init__(self, *args, **kwargs):
|
188
|
+
self.args = args
|
189
|
+
self.kwargs = kwargs
|
190
|
+
|
191
|
+
def _raise(
|
192
|
+
self,
|
193
|
+
exception_type: ta.Type[Exception],
|
194
|
+
default_message: str,
|
195
|
+
message: CheckMessage,
|
196
|
+
ak: _ArgsKwargs = _ArgsKwargs(),
|
197
|
+
*,
|
198
|
+
render_fmt: ta.Optional[str] = None,
|
199
|
+
) -> ta.NoReturn:
|
200
|
+
exc_args = ()
|
201
|
+
if callable(message):
|
202
|
+
message = ta.cast(ta.Callable, message)(*ak.args, **ak.kwargs)
|
203
|
+
if isinstance(message, tuple):
|
204
|
+
message, *exc_args = message # type: ignore
|
205
|
+
|
206
|
+
if message is None:
|
207
|
+
message = default_message
|
208
|
+
|
209
|
+
self._late_configure()
|
210
|
+
|
211
|
+
if render_fmt is not None and (af := self._args_renderer) is not None:
|
212
|
+
rendered_args = af(render_fmt, *ak.args)
|
213
|
+
if rendered_args is not None:
|
214
|
+
message = f'{message} : {rendered_args}'
|
215
|
+
|
216
|
+
exc = self._exception_factory(
|
217
|
+
exception_type,
|
218
|
+
message,
|
219
|
+
*exc_args,
|
220
|
+
*ak.args,
|
221
|
+
**ak.kwargs,
|
222
|
+
)
|
223
|
+
|
224
|
+
for fn in self._on_raise_fns:
|
225
|
+
fn(exc)
|
226
|
+
|
227
|
+
raise exc
|
228
|
+
|
229
|
+
#
|
230
|
+
|
231
|
+
def _unpack_isinstance_spec(self, spec: ta.Any) -> tuple:
|
232
|
+
if isinstance(spec, type):
|
233
|
+
return (spec,)
|
234
|
+
if not isinstance(spec, tuple):
|
235
|
+
spec = (spec,)
|
236
|
+
if None in spec:
|
237
|
+
spec = tuple(filter(None, spec)) + (None.__class__,) # noqa
|
238
|
+
if ta.Any in spec:
|
239
|
+
spec = (object,)
|
240
|
+
return spec
|
241
|
+
|
242
|
+
@ta.overload
|
243
|
+
def isinstance(self, v: ta.Any, spec: ta.Type[T], msg: CheckMessage = None) -> T:
|
244
|
+
...
|
245
|
+
|
246
|
+
@ta.overload
|
247
|
+
def isinstance(self, v: ta.Any, spec: ta.Any, msg: CheckMessage = None) -> ta.Any:
|
248
|
+
...
|
249
|
+
|
250
|
+
def isinstance(self, v, spec, msg=None):
|
251
|
+
if not isinstance(v, self._unpack_isinstance_spec(spec)):
|
252
|
+
self._raise(
|
253
|
+
TypeError,
|
254
|
+
'Must be instance',
|
255
|
+
msg,
|
256
|
+
Checks._ArgsKwargs(v, spec),
|
257
|
+
render_fmt='not isinstance(%s, %s)',
|
258
|
+
)
|
259
|
+
|
260
|
+
return v
|
261
|
+
|
262
|
+
@ta.overload
|
263
|
+
def of_isinstance(self, spec: ta.Type[T], msg: CheckMessage = None) -> ta.Callable[[ta.Any], T]:
|
264
|
+
...
|
265
|
+
|
266
|
+
@ta.overload
|
267
|
+
def of_isinstance(self, spec: ta.Any, msg: CheckMessage = None) -> ta.Callable[[ta.Any], ta.Any]:
|
268
|
+
...
|
269
|
+
|
270
|
+
def of_isinstance(self, spec, msg=None):
|
271
|
+
def inner(v):
|
272
|
+
return self.isinstance(v, self._unpack_isinstance_spec(spec), msg)
|
273
|
+
|
274
|
+
return inner
|
275
|
+
|
276
|
+
def cast(self, v: ta.Any, cls: ta.Type[T], msg: CheckMessage = None) -> T:
|
277
|
+
if not isinstance(v, cls):
|
278
|
+
self._raise(
|
279
|
+
TypeError,
|
280
|
+
'Must be instance',
|
281
|
+
msg,
|
282
|
+
Checks._ArgsKwargs(v, cls),
|
283
|
+
)
|
284
|
+
|
285
|
+
return v
|
286
|
+
|
287
|
+
def of_cast(self, cls: ta.Type[T], msg: CheckMessage = None) -> ta.Callable[[T], T]:
|
288
|
+
def inner(v):
|
289
|
+
return self.cast(v, cls, msg)
|
290
|
+
|
291
|
+
return inner
|
292
|
+
|
293
|
+
def not_isinstance(self, v: T, spec: ta.Any, msg: CheckMessage = None) -> T: # noqa
|
294
|
+
if isinstance(v, self._unpack_isinstance_spec(spec)):
|
295
|
+
self._raise(
|
296
|
+
TypeError,
|
297
|
+
'Must not be instance',
|
298
|
+
msg,
|
299
|
+
Checks._ArgsKwargs(v, spec),
|
300
|
+
render_fmt='isinstance(%s, %s)',
|
301
|
+
)
|
302
|
+
|
303
|
+
return v
|
304
|
+
|
305
|
+
def of_not_isinstance(self, spec: ta.Any, msg: CheckMessage = None) -> ta.Callable[[T], T]:
|
306
|
+
def inner(v):
|
307
|
+
return self.not_isinstance(v, self._unpack_isinstance_spec(spec), msg)
|
308
|
+
|
309
|
+
return inner
|
310
|
+
|
311
|
+
##
|
312
|
+
|
313
|
+
def issubclass(self, v: ta.Type[T], spec: ta.Any, msg: CheckMessage = None) -> ta.Type[T]: # noqa
|
314
|
+
if not issubclass(v, spec):
|
315
|
+
self._raise(
|
316
|
+
TypeError,
|
317
|
+
'Must be subclass',
|
318
|
+
msg,
|
319
|
+
Checks._ArgsKwargs(v, spec),
|
320
|
+
render_fmt='not issubclass(%s, %s)',
|
321
|
+
)
|
322
|
+
|
323
|
+
return v
|
324
|
+
|
325
|
+
def not_issubclass(self, v: ta.Type[T], spec: ta.Any, msg: CheckMessage = None) -> ta.Type[T]:
|
326
|
+
if issubclass(v, spec):
|
327
|
+
self._raise(
|
328
|
+
TypeError,
|
329
|
+
'Must not be subclass',
|
330
|
+
msg,
|
331
|
+
Checks._ArgsKwargs(v, spec),
|
332
|
+
render_fmt='issubclass(%s, %s)',
|
333
|
+
)
|
334
|
+
|
335
|
+
return v
|
336
|
+
|
337
|
+
#
|
338
|
+
|
339
|
+
def in_(self, v: T, c: ta.Container[T], msg: CheckMessage = None) -> T:
|
340
|
+
if v not in c:
|
341
|
+
self._raise(
|
342
|
+
ValueError,
|
343
|
+
'Must be in',
|
344
|
+
msg,
|
345
|
+
Checks._ArgsKwargs(v, c),
|
346
|
+
render_fmt='%s not in %s',
|
347
|
+
)
|
348
|
+
|
349
|
+
return v
|
350
|
+
|
351
|
+
def not_in(self, v: T, c: ta.Container[T], msg: CheckMessage = None) -> T:
|
352
|
+
if v in c:
|
353
|
+
self._raise(
|
354
|
+
ValueError,
|
355
|
+
'Must not be in',
|
356
|
+
msg,
|
357
|
+
Checks._ArgsKwargs(v, c),
|
358
|
+
render_fmt='%s in %s',
|
359
|
+
)
|
360
|
+
|
361
|
+
return v
|
362
|
+
|
363
|
+
def empty(self, v: SizedT, msg: CheckMessage = None) -> SizedT:
|
364
|
+
if len(v) != 0:
|
365
|
+
self._raise(
|
366
|
+
ValueError,
|
367
|
+
'Must be empty',
|
368
|
+
msg,
|
369
|
+
Checks._ArgsKwargs(v),
|
370
|
+
render_fmt='%s',
|
371
|
+
)
|
372
|
+
|
373
|
+
return v
|
374
|
+
|
375
|
+
def iterempty(self, v: ta.Iterable[T], msg: CheckMessage = None) -> ta.Iterable[T]:
|
376
|
+
it = iter(v)
|
377
|
+
try:
|
378
|
+
next(it)
|
379
|
+
except StopIteration:
|
380
|
+
pass
|
381
|
+
else:
|
382
|
+
self._raise(
|
383
|
+
ValueError,
|
384
|
+
'Must be empty',
|
385
|
+
msg,
|
386
|
+
Checks._ArgsKwargs(v),
|
387
|
+
render_fmt='%s',
|
388
|
+
)
|
389
|
+
|
390
|
+
return v
|
391
|
+
|
392
|
+
def not_empty(self, v: SizedT, msg: CheckMessage = None) -> SizedT:
|
393
|
+
if len(v) == 0:
|
394
|
+
self._raise(
|
395
|
+
ValueError,
|
396
|
+
'Must not be empty',
|
397
|
+
msg,
|
398
|
+
Checks._ArgsKwargs(v),
|
399
|
+
render_fmt='%s',
|
400
|
+
)
|
401
|
+
|
402
|
+
return v
|
403
|
+
|
404
|
+
def unique(self, it: ta.Iterable[T], msg: CheckMessage = None) -> ta.Iterable[T]:
|
405
|
+
dupes = [e for e, c in collections.Counter(it).items() if c > 1]
|
406
|
+
if dupes:
|
407
|
+
self._raise(
|
408
|
+
ValueError,
|
409
|
+
'Must be unique',
|
410
|
+
msg,
|
411
|
+
Checks._ArgsKwargs(it, dupes),
|
412
|
+
)
|
413
|
+
|
414
|
+
return it
|
415
|
+
|
416
|
+
def single(self, obj: ta.Iterable[T], msg: CheckMessage = None) -> T:
|
417
|
+
try:
|
418
|
+
[value] = obj
|
419
|
+
except ValueError:
|
420
|
+
self._raise(
|
421
|
+
ValueError,
|
422
|
+
'Must be single',
|
423
|
+
msg,
|
424
|
+
Checks._ArgsKwargs(obj),
|
425
|
+
render_fmt='%s',
|
426
|
+
)
|
427
|
+
|
428
|
+
return value
|
429
|
+
|
430
|
+
def opt_single(self, obj: ta.Iterable[T], msg: CheckMessage = None) -> ta.Optional[T]:
|
431
|
+
it = iter(obj)
|
432
|
+
try:
|
433
|
+
value = next(it)
|
434
|
+
except StopIteration:
|
435
|
+
return None
|
436
|
+
|
437
|
+
try:
|
438
|
+
next(it)
|
439
|
+
except StopIteration:
|
440
|
+
return value # noqa
|
441
|
+
|
442
|
+
self._raise(
|
443
|
+
ValueError,
|
444
|
+
'Must be empty or single',
|
445
|
+
msg,
|
446
|
+
Checks._ArgsKwargs(obj),
|
447
|
+
render_fmt='%s',
|
448
|
+
)
|
449
|
+
|
450
|
+
raise RuntimeError # noqa
|
451
|
+
|
452
|
+
#
|
453
|
+
|
454
|
+
def none(self, v: ta.Any, msg: CheckMessage = None) -> None:
|
455
|
+
if v is not None:
|
456
|
+
self._raise(
|
457
|
+
ValueError,
|
458
|
+
'Must be None',
|
459
|
+
msg,
|
460
|
+
Checks._ArgsKwargs(v),
|
461
|
+
render_fmt='%s',
|
462
|
+
)
|
463
|
+
|
464
|
+
def not_none(self, v: ta.Optional[T], msg: CheckMessage = None) -> T:
|
465
|
+
if v is None:
|
466
|
+
self._raise(
|
467
|
+
ValueError,
|
468
|
+
'Must not be None',
|
469
|
+
msg,
|
470
|
+
Checks._ArgsKwargs(v),
|
471
|
+
render_fmt='%s',
|
472
|
+
)
|
473
|
+
|
474
|
+
return v
|
475
|
+
|
476
|
+
#
|
477
|
+
|
478
|
+
def equal(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
|
479
|
+
if o != v:
|
480
|
+
self._raise(
|
481
|
+
ValueError,
|
482
|
+
'Must be equal',
|
483
|
+
msg,
|
484
|
+
Checks._ArgsKwargs(v, o),
|
485
|
+
render_fmt='%s != %s',
|
486
|
+
)
|
487
|
+
|
488
|
+
return v
|
489
|
+
|
490
|
+
def not_equal(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
|
491
|
+
if o == v:
|
492
|
+
self._raise(
|
493
|
+
ValueError,
|
494
|
+
'Must not be equal',
|
495
|
+
msg,
|
496
|
+
Checks._ArgsKwargs(v, o),
|
497
|
+
render_fmt='%s == %s',
|
498
|
+
)
|
499
|
+
|
500
|
+
return v
|
501
|
+
|
502
|
+
def is_(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
|
503
|
+
if o is not v:
|
504
|
+
self._raise(
|
505
|
+
ValueError,
|
506
|
+
'Must be the same',
|
507
|
+
msg,
|
508
|
+
Checks._ArgsKwargs(v, o),
|
509
|
+
render_fmt='%s is not %s',
|
510
|
+
)
|
511
|
+
|
512
|
+
return v
|
513
|
+
|
514
|
+
def is_not(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
|
515
|
+
if o is v:
|
516
|
+
self._raise(
|
517
|
+
ValueError,
|
518
|
+
'Must not be the same',
|
519
|
+
msg,
|
520
|
+
Checks._ArgsKwargs(v, o),
|
521
|
+
render_fmt='%s is %s',
|
522
|
+
)
|
523
|
+
|
524
|
+
return v
|
525
|
+
|
526
|
+
def callable(self, v: T, msg: CheckMessage = None) -> T: # noqa
|
527
|
+
if not callable(v):
|
528
|
+
self._raise(
|
529
|
+
TypeError,
|
530
|
+
'Must be callable',
|
531
|
+
msg,
|
532
|
+
Checks._ArgsKwargs(v),
|
533
|
+
render_fmt='%s',
|
534
|
+
)
|
535
|
+
|
536
|
+
return v
|
537
|
+
|
538
|
+
def non_empty_str(self, v: ta.Optional[str], msg: CheckMessage = None) -> str:
|
539
|
+
if not isinstance(v, str) or not v:
|
540
|
+
self._raise(
|
541
|
+
ValueError,
|
542
|
+
'Must be non-empty str',
|
543
|
+
msg,
|
544
|
+
Checks._ArgsKwargs(v),
|
545
|
+
render_fmt='%s',
|
546
|
+
)
|
547
|
+
|
548
|
+
return v
|
549
|
+
|
550
|
+
def replacing(self, expected: ta.Any, old: ta.Any, new: T, msg: CheckMessage = None) -> T:
|
551
|
+
if old != expected:
|
552
|
+
self._raise(
|
553
|
+
ValueError,
|
554
|
+
'Must be replacing',
|
555
|
+
msg,
|
556
|
+
Checks._ArgsKwargs(expected, old, new),
|
557
|
+
render_fmt='%s -> %s -> %s',
|
558
|
+
)
|
559
|
+
|
560
|
+
return new
|
561
|
+
|
562
|
+
def replacing_none(self, old: ta.Any, new: T, msg: CheckMessage = None) -> T:
|
563
|
+
if old is not None:
|
564
|
+
self._raise(
|
565
|
+
ValueError,
|
566
|
+
'Must be replacing None',
|
567
|
+
msg,
|
568
|
+
Checks._ArgsKwargs(old, new),
|
569
|
+
render_fmt='%s -> %s',
|
570
|
+
)
|
571
|
+
|
572
|
+
return new
|
573
|
+
|
574
|
+
#
|
575
|
+
|
576
|
+
def arg(self, v: bool, msg: CheckMessage = None) -> None:
|
577
|
+
if not v:
|
578
|
+
self._raise(
|
579
|
+
RuntimeError,
|
580
|
+
'Argument condition not met',
|
581
|
+
msg,
|
582
|
+
Checks._ArgsKwargs(v),
|
583
|
+
render_fmt='%s',
|
584
|
+
)
|
585
|
+
|
586
|
+
def state(self, v: bool, msg: CheckMessage = None) -> None:
|
587
|
+
if not v:
|
588
|
+
self._raise(
|
589
|
+
RuntimeError,
|
590
|
+
'State condition not met',
|
591
|
+
msg,
|
592
|
+
Checks._ArgsKwargs(v),
|
593
|
+
render_fmt='%s',
|
594
|
+
)
|
595
|
+
|
596
|
+
|
597
|
+
check = Checks()
|
598
|
+
|
599
|
+
|
600
|
+
########################################
|
601
|
+
# ../../../omlish/lite/reflect.py
|
602
|
+
|
603
|
+
|
604
|
+
##
|
605
|
+
|
606
|
+
|
607
|
+
_GENERIC_ALIAS_TYPES = (
|
608
|
+
ta._GenericAlias, # type: ignore # noqa
|
609
|
+
*([ta._SpecialGenericAlias] if hasattr(ta, '_SpecialGenericAlias') else []), # noqa
|
610
|
+
)
|
611
|
+
|
612
|
+
|
613
|
+
def is_generic_alias(obj, *, origin: ta.Any = None) -> bool:
|
614
|
+
return (
|
615
|
+
isinstance(obj, _GENERIC_ALIAS_TYPES) and
|
616
|
+
(origin is None or ta.get_origin(obj) is origin)
|
617
|
+
)
|
618
|
+
|
619
|
+
|
620
|
+
is_union_alias = functools.partial(is_generic_alias, origin=ta.Union)
|
621
|
+
is_callable_alias = functools.partial(is_generic_alias, origin=ta.Callable)
|
622
|
+
|
623
|
+
|
624
|
+
##
|
625
|
+
|
626
|
+
|
627
|
+
def is_optional_alias(spec: ta.Any) -> bool:
|
628
|
+
return (
|
629
|
+
isinstance(spec, _GENERIC_ALIAS_TYPES) and # noqa
|
630
|
+
ta.get_origin(spec) is ta.Union and
|
631
|
+
len(ta.get_args(spec)) == 2 and
|
632
|
+
any(a in (None, type(None)) for a in ta.get_args(spec))
|
633
|
+
)
|
634
|
+
|
635
|
+
|
636
|
+
def get_optional_alias_arg(spec: ta.Any) -> ta.Any:
|
637
|
+
[it] = [it for it in ta.get_args(spec) if it not in (None, type(None))]
|
638
|
+
return it
|
639
|
+
|
640
|
+
|
641
|
+
##
|
642
|
+
|
643
|
+
|
644
|
+
def is_new_type(spec: ta.Any) -> bool:
|
645
|
+
if isinstance(ta.NewType, type):
|
646
|
+
return isinstance(spec, ta.NewType)
|
647
|
+
else:
|
648
|
+
# Before https://github.com/python/cpython/commit/c2f33dfc83ab270412bf243fb21f724037effa1a
|
649
|
+
return isinstance(spec, types.FunctionType) and spec.__code__ is ta.NewType.__code__.co_consts[1] # type: ignore # noqa
|
650
|
+
|
651
|
+
|
652
|
+
def get_new_type_supertype(spec: ta.Any) -> ta.Any:
|
653
|
+
return spec.__supertype__
|
654
|
+
|
655
|
+
|
656
|
+
##
|
657
|
+
|
658
|
+
|
659
|
+
def is_literal_type(spec: ta.Any) -> bool:
|
660
|
+
if hasattr(ta, '_LiteralGenericAlias'):
|
661
|
+
return isinstance(spec, ta._LiteralGenericAlias) # noqa
|
662
|
+
else:
|
663
|
+
return (
|
664
|
+
isinstance(spec, ta._GenericAlias) and # type: ignore # noqa
|
665
|
+
spec.__origin__ is ta.Literal
|
666
|
+
)
|
667
|
+
|
668
|
+
|
669
|
+
def get_literal_type_args(spec: ta.Any) -> ta.Iterable[ta.Any]:
|
670
|
+
return spec.__args__
|
671
|
+
|
672
|
+
|
673
|
+
##
|
674
|
+
|
675
|
+
|
676
|
+
def deep_subclasses(cls: ta.Type[T]) -> ta.Iterator[ta.Type[T]]:
|
677
|
+
seen = set()
|
678
|
+
todo = list(reversed(cls.__subclasses__()))
|
679
|
+
while todo:
|
680
|
+
cur = todo.pop()
|
681
|
+
if cur in seen:
|
682
|
+
continue
|
683
|
+
seen.add(cur)
|
684
|
+
yield cur
|
685
|
+
todo.extend(reversed(cur.__subclasses__()))
|
686
|
+
|
687
|
+
|
688
|
+
########################################
|
689
|
+
# ../../../omlish/lite/strings.py
|
690
|
+
|
691
|
+
|
692
|
+
##
|
693
|
+
|
694
|
+
|
695
|
+
def camel_case(name: str, *, lower: bool = False) -> str:
|
696
|
+
if not name:
|
697
|
+
return ''
|
698
|
+
s = ''.join(map(str.capitalize, name.split('_'))) # noqa
|
699
|
+
if lower:
|
700
|
+
s = s[0].lower() + s[1:]
|
701
|
+
return s
|
702
|
+
|
703
|
+
|
704
|
+
def snake_case(name: str) -> str:
|
705
|
+
uppers: list[int | None] = [i for i, c in enumerate(name) if c.isupper()]
|
706
|
+
return '_'.join([name[l:r].lower() for l, r in zip([None, *uppers], [*uppers, None])]).strip('_')
|
707
|
+
|
708
|
+
|
709
|
+
##
|
710
|
+
|
711
|
+
|
712
|
+
def is_dunder(name: str) -> bool:
|
713
|
+
return (
|
714
|
+
name[:2] == name[-2:] == '__' and
|
715
|
+
name[2:3] != '_' and
|
716
|
+
name[-3:-2] != '_' and
|
717
|
+
len(name) > 4
|
718
|
+
)
|
719
|
+
|
720
|
+
|
721
|
+
def is_sunder(name: str) -> bool:
|
722
|
+
return (
|
723
|
+
name[0] == name[-1] == '_' and
|
724
|
+
name[1:2] != '_' and
|
725
|
+
name[-2:-1] != '_' and
|
726
|
+
len(name) > 2
|
727
|
+
)
|
728
|
+
|
729
|
+
|
730
|
+
##
|
731
|
+
|
732
|
+
|
733
|
+
def strip_with_newline(s: str) -> str:
|
734
|
+
if not s:
|
735
|
+
return ''
|
736
|
+
return s.strip() + '\n'
|
737
|
+
|
738
|
+
|
739
|
+
@ta.overload
|
740
|
+
def split_keep_delimiter(s: str, d: str) -> str:
|
741
|
+
...
|
742
|
+
|
743
|
+
|
744
|
+
@ta.overload
|
745
|
+
def split_keep_delimiter(s: bytes, d: bytes) -> bytes:
|
746
|
+
...
|
747
|
+
|
748
|
+
|
749
|
+
def split_keep_delimiter(s, d):
|
750
|
+
ps = []
|
751
|
+
i = 0
|
752
|
+
while i < len(s):
|
753
|
+
if (n := s.find(d, i)) < i:
|
754
|
+
ps.append(s[i:])
|
755
|
+
break
|
756
|
+
ps.append(s[i:n + 1])
|
757
|
+
i = n + 1
|
758
|
+
return ps
|
759
|
+
|
760
|
+
|
761
|
+
##
|
762
|
+
|
763
|
+
|
764
|
+
def attr_repr(obj: ta.Any, *attrs: str) -> str:
|
765
|
+
return f'{type(obj).__name__}({", ".join(f"{attr}={getattr(obj, attr)!r}" for attr in attrs)})'
|
766
|
+
|
767
|
+
|
768
|
+
##
|
769
|
+
|
770
|
+
|
771
|
+
FORMAT_NUM_BYTES_SUFFIXES: ta.Sequence[str] = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB']
|
772
|
+
|
773
|
+
|
774
|
+
def format_num_bytes(num_bytes: int) -> str:
|
775
|
+
for i, suffix in enumerate(FORMAT_NUM_BYTES_SUFFIXES):
|
776
|
+
value = num_bytes / 1024 ** i
|
777
|
+
if num_bytes < 1024 ** (i + 1):
|
778
|
+
if value.is_integer():
|
779
|
+
return f'{int(value)}{suffix}'
|
780
|
+
else:
|
781
|
+
return f'{value:.2f}{suffix}'
|
782
|
+
|
783
|
+
return f'{num_bytes / 1024 ** (len(FORMAT_NUM_BYTES_SUFFIXES) - 1):.2f}{FORMAT_NUM_BYTES_SUFFIXES[-1]}'
|
784
|
+
|
785
|
+
|
786
|
+
########################################
|
787
|
+
# ../../../omlish/lite/marshal.py
|
788
|
+
"""
|
789
|
+
TODO:
|
790
|
+
- pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
|
791
|
+
- Options.sequence_cls = list, mapping_cls = dict, ... - def with_mutable_containers() -> Options
|
792
|
+
"""
|
793
|
+
|
794
|
+
|
795
|
+
##
|
796
|
+
|
797
|
+
|
798
|
+
@dc.dataclass(frozen=True)
|
799
|
+
class ObjMarshalOptions:
|
800
|
+
raw_bytes: bool = False
|
801
|
+
non_strict_fields: bool = False
|
802
|
+
|
803
|
+
|
804
|
+
class ObjMarshaler(abc.ABC):
|
805
|
+
@abc.abstractmethod
|
806
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
807
|
+
raise NotImplementedError
|
808
|
+
|
809
|
+
@abc.abstractmethod
|
810
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
811
|
+
raise NotImplementedError
|
812
|
+
|
813
|
+
|
814
|
+
class NopObjMarshaler(ObjMarshaler):
|
815
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
816
|
+
return o
|
817
|
+
|
818
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
819
|
+
return o
|
820
|
+
|
821
|
+
|
822
|
+
@dc.dataclass()
|
823
|
+
class ProxyObjMarshaler(ObjMarshaler):
|
824
|
+
m: ta.Optional[ObjMarshaler] = None
|
825
|
+
|
826
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
827
|
+
return check.not_none(self.m).marshal(o, ctx)
|
828
|
+
|
829
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
830
|
+
return check.not_none(self.m).unmarshal(o, ctx)
|
831
|
+
|
832
|
+
|
833
|
+
@dc.dataclass(frozen=True)
|
834
|
+
class CastObjMarshaler(ObjMarshaler):
|
835
|
+
ty: type
|
836
|
+
|
837
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
838
|
+
return o
|
839
|
+
|
840
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
841
|
+
return self.ty(o)
|
842
|
+
|
843
|
+
|
844
|
+
class DynamicObjMarshaler(ObjMarshaler):
|
845
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
846
|
+
return ctx.manager.marshal_obj(o, opts=ctx.options)
|
847
|
+
|
848
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
849
|
+
return o
|
850
|
+
|
851
|
+
|
852
|
+
@dc.dataclass(frozen=True)
|
853
|
+
class Base64ObjMarshaler(ObjMarshaler):
|
854
|
+
ty: type
|
855
|
+
|
856
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
857
|
+
return base64.b64encode(o).decode('ascii')
|
858
|
+
|
859
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
860
|
+
return self.ty(base64.b64decode(o))
|
861
|
+
|
862
|
+
|
863
|
+
@dc.dataclass(frozen=True)
|
864
|
+
class BytesSwitchedObjMarshaler(ObjMarshaler):
|
865
|
+
m: ObjMarshaler
|
866
|
+
|
867
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
868
|
+
if ctx.options.raw_bytes:
|
869
|
+
return o
|
870
|
+
return self.m.marshal(o, ctx)
|
871
|
+
|
872
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
873
|
+
if ctx.options.raw_bytes:
|
874
|
+
return o
|
875
|
+
return self.m.unmarshal(o, ctx)
|
876
|
+
|
877
|
+
|
878
|
+
@dc.dataclass(frozen=True)
|
879
|
+
class EnumObjMarshaler(ObjMarshaler):
|
880
|
+
ty: type
|
881
|
+
|
882
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
883
|
+
return o.name
|
884
|
+
|
885
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
886
|
+
return self.ty.__members__[o] # type: ignore
|
887
|
+
|
888
|
+
|
889
|
+
@dc.dataclass(frozen=True)
|
890
|
+
class OptionalObjMarshaler(ObjMarshaler):
|
891
|
+
item: ObjMarshaler
|
892
|
+
|
893
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
894
|
+
if o is None:
|
895
|
+
return None
|
896
|
+
return self.item.marshal(o, ctx)
|
897
|
+
|
898
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
899
|
+
if o is None:
|
900
|
+
return None
|
901
|
+
return self.item.unmarshal(o, ctx)
|
902
|
+
|
903
|
+
|
904
|
+
@dc.dataclass(frozen=True)
|
905
|
+
class PrimitiveUnionObjMarshaler(ObjMarshaler):
|
906
|
+
pt: ta.Tuple[type, ...]
|
907
|
+
x: ta.Optional[ObjMarshaler] = None
|
908
|
+
|
909
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
910
|
+
if isinstance(o, self.pt):
|
911
|
+
return o
|
912
|
+
elif self.x is not None:
|
913
|
+
return self.x.marshal(o, ctx)
|
914
|
+
else:
|
915
|
+
raise TypeError(o)
|
916
|
+
|
917
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
918
|
+
if isinstance(o, self.pt):
|
919
|
+
return o
|
920
|
+
elif self.x is not None:
|
921
|
+
return self.x.unmarshal(o, ctx)
|
922
|
+
else:
|
923
|
+
raise TypeError(o)
|
924
|
+
|
925
|
+
|
926
|
+
@dc.dataclass(frozen=True)
|
927
|
+
class LiteralObjMarshaler(ObjMarshaler):
|
928
|
+
item: ObjMarshaler
|
929
|
+
vs: frozenset
|
930
|
+
|
931
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
932
|
+
return self.item.marshal(check.in_(o, self.vs), ctx)
|
933
|
+
|
934
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
935
|
+
return check.in_(self.item.unmarshal(o, ctx), self.vs)
|
936
|
+
|
937
|
+
|
938
|
+
@dc.dataclass(frozen=True)
|
939
|
+
class MappingObjMarshaler(ObjMarshaler):
|
940
|
+
ty: type
|
941
|
+
km: ObjMarshaler
|
942
|
+
vm: ObjMarshaler
|
943
|
+
|
944
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
945
|
+
return {self.km.marshal(k, ctx): self.vm.marshal(v, ctx) for k, v in o.items()}
|
946
|
+
|
947
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
948
|
+
return self.ty((self.km.unmarshal(k, ctx), self.vm.unmarshal(v, ctx)) for k, v in o.items())
|
949
|
+
|
950
|
+
|
951
|
+
@dc.dataclass(frozen=True)
|
952
|
+
class IterableObjMarshaler(ObjMarshaler):
|
953
|
+
ty: type
|
954
|
+
item: ObjMarshaler
|
955
|
+
|
956
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
957
|
+
return [self.item.marshal(e, ctx) for e in o]
|
958
|
+
|
959
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
960
|
+
return self.ty(self.item.unmarshal(e, ctx) for e in o)
|
961
|
+
|
962
|
+
|
963
|
+
@dc.dataclass(frozen=True)
|
964
|
+
class FieldsObjMarshaler(ObjMarshaler):
|
965
|
+
ty: type
|
966
|
+
|
967
|
+
@dc.dataclass(frozen=True)
|
968
|
+
class Field:
|
969
|
+
att: str
|
970
|
+
key: str
|
971
|
+
m: ObjMarshaler
|
972
|
+
|
973
|
+
omit_if_none: bool = False
|
974
|
+
|
975
|
+
fs: ta.Sequence[Field]
|
976
|
+
|
977
|
+
non_strict: bool = False
|
978
|
+
|
979
|
+
#
|
980
|
+
|
981
|
+
_fs_by_att: ta.ClassVar[ta.Mapping[str, Field]]
|
982
|
+
_fs_by_key: ta.ClassVar[ta.Mapping[str, Field]]
|
983
|
+
|
984
|
+
def __post_init__(self) -> None:
|
985
|
+
fs_by_att: dict = {}
|
986
|
+
fs_by_key: dict = {}
|
987
|
+
for f in self.fs:
|
988
|
+
check.not_in(check.non_empty_str(f.att), fs_by_att)
|
989
|
+
check.not_in(check.non_empty_str(f.key), fs_by_key)
|
990
|
+
fs_by_att[f.att] = f
|
991
|
+
fs_by_key[f.key] = f
|
992
|
+
self.__dict__['_fs_by_att'] = fs_by_att
|
993
|
+
self.__dict__['_fs_by_key'] = fs_by_key
|
994
|
+
|
995
|
+
#
|
996
|
+
|
997
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
998
|
+
d = {}
|
999
|
+
for f in self.fs:
|
1000
|
+
mv = f.m.marshal(getattr(o, f.att), ctx)
|
1001
|
+
if mv is None and f.omit_if_none:
|
1002
|
+
continue
|
1003
|
+
d[f.key] = mv
|
1004
|
+
return d
|
1005
|
+
|
1006
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
1007
|
+
kw = {}
|
1008
|
+
for k, v in o.items():
|
1009
|
+
if (f := self._fs_by_key.get(k)) is None:
|
1010
|
+
if not (self.non_strict or ctx.options.non_strict_fields):
|
1011
|
+
raise KeyError(k)
|
1012
|
+
continue
|
1013
|
+
kw[f.att] = f.m.unmarshal(v, ctx)
|
1014
|
+
return self.ty(**kw)
|
1015
|
+
|
1016
|
+
|
1017
|
+
@dc.dataclass(frozen=True)
|
1018
|
+
class SingleFieldObjMarshaler(ObjMarshaler):
|
1019
|
+
ty: type
|
1020
|
+
fld: str
|
1021
|
+
|
1022
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
1023
|
+
return getattr(o, self.fld)
|
1024
|
+
|
1025
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
1026
|
+
return self.ty(**{self.fld: o})
|
1027
|
+
|
1028
|
+
|
1029
|
+
@dc.dataclass(frozen=True)
|
1030
|
+
class PolymorphicObjMarshaler(ObjMarshaler):
|
1031
|
+
class Impl(ta.NamedTuple):
|
1032
|
+
ty: type
|
1033
|
+
tag: str
|
1034
|
+
m: ObjMarshaler
|
1035
|
+
|
1036
|
+
impls_by_ty: ta.Mapping[type, Impl]
|
1037
|
+
impls_by_tag: ta.Mapping[str, Impl]
|
1038
|
+
|
1039
|
+
@classmethod
|
1040
|
+
def of(cls, impls: ta.Iterable[Impl]) -> 'PolymorphicObjMarshaler':
|
1041
|
+
return cls(
|
1042
|
+
{i.ty: i for i in impls},
|
1043
|
+
{i.tag: i for i in impls},
|
1044
|
+
)
|
1045
|
+
|
1046
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
1047
|
+
impl = self.impls_by_ty[type(o)]
|
1048
|
+
return {impl.tag: impl.m.marshal(o, ctx)}
|
1049
|
+
|
1050
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
1051
|
+
[(t, v)] = o.items()
|
1052
|
+
impl = self.impls_by_tag[t]
|
1053
|
+
return impl.m.unmarshal(v, ctx)
|
1054
|
+
|
1055
|
+
|
1056
|
+
@dc.dataclass(frozen=True)
|
1057
|
+
class DatetimeObjMarshaler(ObjMarshaler):
|
1058
|
+
ty: type
|
1059
|
+
|
1060
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
1061
|
+
return o.isoformat()
|
1062
|
+
|
1063
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
1064
|
+
return self.ty.fromisoformat(o) # type: ignore
|
1065
|
+
|
1066
|
+
|
1067
|
+
class DecimalObjMarshaler(ObjMarshaler):
|
1068
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
1069
|
+
return str(check.isinstance(o, decimal.Decimal))
|
1070
|
+
|
1071
|
+
def unmarshal(self, v: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
1072
|
+
return decimal.Decimal(check.isinstance(v, str))
|
1073
|
+
|
1074
|
+
|
1075
|
+
class FractionObjMarshaler(ObjMarshaler):
|
1076
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
1077
|
+
fr = check.isinstance(o, fractions.Fraction)
|
1078
|
+
return [fr.numerator, fr.denominator]
|
1079
|
+
|
1080
|
+
def unmarshal(self, v: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
1081
|
+
num, denom = check.isinstance(v, list)
|
1082
|
+
return fractions.Fraction(num, denom)
|
1083
|
+
|
1084
|
+
|
1085
|
+
class UuidObjMarshaler(ObjMarshaler):
|
1086
|
+
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
1087
|
+
return str(o)
|
1088
|
+
|
1089
|
+
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
1090
|
+
return uuid.UUID(o)
|
1091
|
+
|
1092
|
+
|
1093
|
+
##
|
1094
|
+
|
1095
|
+
|
1096
|
+
_DEFAULT_OBJ_MARSHALERS: ta.Dict[ta.Any, ObjMarshaler] = {
|
1097
|
+
**{t: NopObjMarshaler() for t in (type(None),)},
|
1098
|
+
**{t: CastObjMarshaler(t) for t in (int, float, str, bool)},
|
1099
|
+
**{t: BytesSwitchedObjMarshaler(Base64ObjMarshaler(t)) for t in (bytes, bytearray)},
|
1100
|
+
**{t: IterableObjMarshaler(t, DynamicObjMarshaler()) for t in (list, tuple, set, frozenset)},
|
1101
|
+
**{t: MappingObjMarshaler(t, DynamicObjMarshaler(), DynamicObjMarshaler()) for t in (dict,)},
|
1102
|
+
|
1103
|
+
**{t: DynamicObjMarshaler() for t in (ta.Any, object)},
|
1104
|
+
|
1105
|
+
**{t: DatetimeObjMarshaler(t) for t in (datetime.date, datetime.time, datetime.datetime)},
|
1106
|
+
decimal.Decimal: DecimalObjMarshaler(),
|
1107
|
+
fractions.Fraction: FractionObjMarshaler(),
|
1108
|
+
uuid.UUID: UuidObjMarshaler(),
|
1109
|
+
}
|
1110
|
+
|
1111
|
+
_OBJ_MARSHALER_GENERIC_MAPPING_TYPES: ta.Dict[ta.Any, type] = {
|
1112
|
+
**{t: t for t in (dict,)},
|
1113
|
+
**{t: dict for t in (collections.abc.Mapping, collections.abc.MutableMapping)}, # noqa
|
1114
|
+
}
|
1115
|
+
|
1116
|
+
_OBJ_MARSHALER_GENERIC_ITERABLE_TYPES: ta.Dict[ta.Any, type] = {
|
1117
|
+
**{t: t for t in (list, tuple, set, frozenset)},
|
1118
|
+
collections.abc.Set: frozenset,
|
1119
|
+
collections.abc.MutableSet: set,
|
1120
|
+
collections.abc.Sequence: tuple,
|
1121
|
+
collections.abc.MutableSequence: list,
|
1122
|
+
}
|
1123
|
+
|
1124
|
+
_OBJ_MARSHALER_PRIMITIVE_TYPES: ta.Set[type] = {
|
1125
|
+
int,
|
1126
|
+
float,
|
1127
|
+
bool,
|
1128
|
+
str,
|
1129
|
+
}
|
1130
|
+
|
1131
|
+
|
1132
|
+
##
|
1133
|
+
|
1134
|
+
|
1135
|
+
_REGISTERED_OBJ_MARSHALERS_BY_TYPE: ta.MutableMapping[type, ObjMarshaler] = weakref.WeakKeyDictionary()
|
1136
|
+
|
1137
|
+
|
1138
|
+
def register_type_obj_marshaler(ty: type, om: ObjMarshaler) -> None:
|
1139
|
+
_REGISTERED_OBJ_MARSHALERS_BY_TYPE[ty] = om
|
1140
|
+
|
1141
|
+
|
1142
|
+
def register_single_field_type_obj_marshaler(fld, ty=None):
|
1143
|
+
def inner(ty): # noqa
|
1144
|
+
register_type_obj_marshaler(ty, SingleFieldObjMarshaler(ty, fld))
|
1145
|
+
return ty
|
1146
|
+
|
1147
|
+
if ty is not None:
|
1148
|
+
return inner(ty)
|
1149
|
+
else:
|
1150
|
+
return inner
|
1151
|
+
|
1152
|
+
|
1153
|
+
##
|
1154
|
+
|
1155
|
+
|
1156
|
+
class ObjMarshalerFieldMetadata:
|
1157
|
+
def __new__(cls, *args, **kwargs): # noqa
|
1158
|
+
raise TypeError
|
1159
|
+
|
1160
|
+
|
1161
|
+
class OBJ_MARSHALER_FIELD_KEY(ObjMarshalerFieldMetadata): # noqa
|
1162
|
+
pass
|
1163
|
+
|
1164
|
+
|
1165
|
+
class OBJ_MARSHALER_OMIT_IF_NONE(ObjMarshalerFieldMetadata): # noqa
|
1166
|
+
pass
|
1167
|
+
|
1168
|
+
|
1169
|
+
##
|
1170
|
+
|
1171
|
+
|
1172
|
+
class ObjMarshalerManager:
|
1173
|
+
def __init__(
|
1174
|
+
self,
|
1175
|
+
*,
|
1176
|
+
default_options: ObjMarshalOptions = ObjMarshalOptions(),
|
1177
|
+
|
1178
|
+
default_obj_marshalers: ta.Dict[ta.Any, ObjMarshaler] = _DEFAULT_OBJ_MARSHALERS, # noqa
|
1179
|
+
generic_mapping_types: ta.Dict[ta.Any, type] = _OBJ_MARSHALER_GENERIC_MAPPING_TYPES, # noqa
|
1180
|
+
generic_iterable_types: ta.Dict[ta.Any, type] = _OBJ_MARSHALER_GENERIC_ITERABLE_TYPES, # noqa
|
1181
|
+
|
1182
|
+
registered_obj_marshalers: ta.Mapping[type, ObjMarshaler] = _REGISTERED_OBJ_MARSHALERS_BY_TYPE,
|
1183
|
+
) -> None:
|
1184
|
+
super().__init__()
|
1185
|
+
|
1186
|
+
self._default_options = default_options
|
1187
|
+
|
1188
|
+
self._obj_marshalers = dict(default_obj_marshalers)
|
1189
|
+
self._generic_mapping_types = generic_mapping_types
|
1190
|
+
self._generic_iterable_types = generic_iterable_types
|
1191
|
+
self._registered_obj_marshalers = registered_obj_marshalers
|
1192
|
+
|
1193
|
+
self._lock = threading.RLock()
|
1194
|
+
self._marshalers: ta.Dict[ta.Any, ObjMarshaler] = dict(_DEFAULT_OBJ_MARSHALERS)
|
1195
|
+
self._proxies: ta.Dict[ta.Any, ProxyObjMarshaler] = {}
|
1196
|
+
|
1197
|
+
#
|
1198
|
+
|
1199
|
+
def make_obj_marshaler(
|
1200
|
+
self,
|
1201
|
+
ty: ta.Any,
|
1202
|
+
rec: ta.Callable[[ta.Any], ObjMarshaler],
|
1203
|
+
*,
|
1204
|
+
non_strict_fields: bool = False,
|
1205
|
+
) -> ObjMarshaler:
|
1206
|
+
if isinstance(ty, type):
|
1207
|
+
if (reg := self._registered_obj_marshalers.get(ty)) is not None:
|
1208
|
+
return reg
|
1209
|
+
|
1210
|
+
if abc.ABC in ty.__bases__:
|
1211
|
+
tn = ty.__name__
|
1212
|
+
impls: ta.List[ta.Tuple[type, str]] = [ # type: ignore[var-annotated]
|
1213
|
+
(ity, ity.__name__)
|
1214
|
+
for ity in deep_subclasses(ty)
|
1215
|
+
if abc.ABC not in ity.__bases__
|
1216
|
+
]
|
1217
|
+
|
1218
|
+
if all(itn.endswith(tn) for _, itn in impls):
|
1219
|
+
impls = [
|
1220
|
+
(ity, snake_case(itn[:-len(tn)]))
|
1221
|
+
for ity, itn in impls
|
1222
|
+
]
|
1223
|
+
|
1224
|
+
dupe_tns = sorted(
|
1225
|
+
dn
|
1226
|
+
for dn, dc in collections.Counter(itn for _, itn in impls).items()
|
1227
|
+
if dc > 1
|
1228
|
+
)
|
1229
|
+
if dupe_tns:
|
1230
|
+
raise KeyError(f'Duplicate impl names for {ty}: {dupe_tns}')
|
1231
|
+
|
1232
|
+
return PolymorphicObjMarshaler.of([
|
1233
|
+
PolymorphicObjMarshaler.Impl(
|
1234
|
+
ity,
|
1235
|
+
itn,
|
1236
|
+
rec(ity),
|
1237
|
+
)
|
1238
|
+
for ity, itn in impls
|
1239
|
+
])
|
1240
|
+
|
1241
|
+
if issubclass(ty, enum.Enum):
|
1242
|
+
return EnumObjMarshaler(ty)
|
1243
|
+
|
1244
|
+
if dc.is_dataclass(ty):
|
1245
|
+
return FieldsObjMarshaler(
|
1246
|
+
ty,
|
1247
|
+
[
|
1248
|
+
FieldsObjMarshaler.Field(
|
1249
|
+
att=f.name,
|
1250
|
+
key=check.non_empty_str(fk),
|
1251
|
+
m=rec(f.type),
|
1252
|
+
omit_if_none=check.isinstance(f.metadata.get(OBJ_MARSHALER_OMIT_IF_NONE, False), bool),
|
1253
|
+
)
|
1254
|
+
for f in dc.fields(ty)
|
1255
|
+
if (fk := f.metadata.get(OBJ_MARSHALER_FIELD_KEY, f.name)) is not None
|
1256
|
+
],
|
1257
|
+
non_strict=non_strict_fields,
|
1258
|
+
)
|
1259
|
+
|
1260
|
+
if issubclass(ty, tuple) and hasattr(ty, '_fields'):
|
1261
|
+
return FieldsObjMarshaler(
|
1262
|
+
ty,
|
1263
|
+
[
|
1264
|
+
FieldsObjMarshaler.Field(
|
1265
|
+
att=p.name,
|
1266
|
+
key=p.name,
|
1267
|
+
m=rec(p.annotation),
|
1268
|
+
)
|
1269
|
+
for p in inspect.signature(ty).parameters.values()
|
1270
|
+
],
|
1271
|
+
non_strict=non_strict_fields,
|
1272
|
+
)
|
1273
|
+
|
1274
|
+
if is_new_type(ty):
|
1275
|
+
return rec(get_new_type_supertype(ty))
|
1276
|
+
|
1277
|
+
if is_literal_type(ty):
|
1278
|
+
lvs = frozenset(get_literal_type_args(ty))
|
1279
|
+
if None in lvs:
|
1280
|
+
is_opt = True
|
1281
|
+
lvs -= frozenset([None])
|
1282
|
+
else:
|
1283
|
+
is_opt = False
|
1284
|
+
lty = check.single(set(map(type, lvs)))
|
1285
|
+
lm: ObjMarshaler = LiteralObjMarshaler(rec(lty), lvs)
|
1286
|
+
if is_opt:
|
1287
|
+
lm = OptionalObjMarshaler(lm)
|
1288
|
+
return lm
|
1289
|
+
|
1290
|
+
if is_generic_alias(ty):
|
1291
|
+
try:
|
1292
|
+
mt = self._generic_mapping_types[ta.get_origin(ty)]
|
1293
|
+
except KeyError:
|
1294
|
+
pass
|
1295
|
+
else:
|
1296
|
+
k, v = ta.get_args(ty)
|
1297
|
+
return MappingObjMarshaler(mt, rec(k), rec(v))
|
1298
|
+
|
1299
|
+
try:
|
1300
|
+
st = self._generic_iterable_types[ta.get_origin(ty)]
|
1301
|
+
except KeyError:
|
1302
|
+
pass
|
1303
|
+
else:
|
1304
|
+
[e] = ta.get_args(ty)
|
1305
|
+
return IterableObjMarshaler(st, rec(e))
|
1306
|
+
|
1307
|
+
if is_union_alias(ty):
|
1308
|
+
uts = frozenset(ta.get_args(ty))
|
1309
|
+
if None in uts or type(None) in uts:
|
1310
|
+
is_opt = True
|
1311
|
+
uts = frozenset(ut for ut in uts if ut not in (None, type(None)))
|
1312
|
+
else:
|
1313
|
+
is_opt = False
|
1314
|
+
|
1315
|
+
um: ObjMarshaler
|
1316
|
+
if not uts:
|
1317
|
+
raise TypeError(ty)
|
1318
|
+
elif len(uts) == 1:
|
1319
|
+
um = rec(check.single(uts))
|
1320
|
+
else:
|
1321
|
+
pt = tuple({ut for ut in uts if ut in _OBJ_MARSHALER_PRIMITIVE_TYPES})
|
1322
|
+
np_uts = {ut for ut in uts if ut not in _OBJ_MARSHALER_PRIMITIVE_TYPES}
|
1323
|
+
if not np_uts:
|
1324
|
+
um = PrimitiveUnionObjMarshaler(pt)
|
1325
|
+
elif len(np_uts) == 1:
|
1326
|
+
um = PrimitiveUnionObjMarshaler(pt, x=rec(check.single(np_uts)))
|
1327
|
+
else:
|
1328
|
+
raise TypeError(ty)
|
1329
|
+
|
1330
|
+
if is_opt:
|
1331
|
+
um = OptionalObjMarshaler(um)
|
1332
|
+
return um
|
1333
|
+
|
1334
|
+
raise TypeError(ty)
|
1335
|
+
|
1336
|
+
#
|
1337
|
+
|
1338
|
+
def set_obj_marshaler(
|
1339
|
+
self,
|
1340
|
+
ty: ta.Any,
|
1341
|
+
m: ObjMarshaler,
|
1342
|
+
*,
|
1343
|
+
override: bool = False,
|
1344
|
+
) -> None:
|
1345
|
+
with self._lock:
|
1346
|
+
if not override and ty in self._obj_marshalers:
|
1347
|
+
raise KeyError(ty)
|
1348
|
+
self._obj_marshalers[ty] = m
|
1349
|
+
|
1350
|
+
def get_obj_marshaler(
|
1351
|
+
self,
|
1352
|
+
ty: ta.Any,
|
1353
|
+
*,
|
1354
|
+
no_cache: bool = False,
|
1355
|
+
**kwargs: ta.Any,
|
1356
|
+
) -> ObjMarshaler:
|
1357
|
+
with self._lock:
|
1358
|
+
if not no_cache:
|
1359
|
+
try:
|
1360
|
+
return self._obj_marshalers[ty]
|
1361
|
+
except KeyError:
|
1362
|
+
pass
|
1363
|
+
|
1364
|
+
try:
|
1365
|
+
return self._proxies[ty]
|
1366
|
+
except KeyError:
|
1367
|
+
pass
|
1368
|
+
|
1369
|
+
rec = functools.partial(
|
1370
|
+
self.get_obj_marshaler,
|
1371
|
+
no_cache=no_cache,
|
1372
|
+
**kwargs,
|
1373
|
+
)
|
1374
|
+
|
1375
|
+
p = ProxyObjMarshaler()
|
1376
|
+
self._proxies[ty] = p
|
1377
|
+
try:
|
1378
|
+
m = self.make_obj_marshaler(ty, rec, **kwargs)
|
1379
|
+
finally:
|
1380
|
+
del self._proxies[ty]
|
1381
|
+
p.m = m
|
1382
|
+
|
1383
|
+
if not no_cache:
|
1384
|
+
self._obj_marshalers[ty] = m
|
1385
|
+
return m
|
1386
|
+
|
1387
|
+
#
|
1388
|
+
|
1389
|
+
def _make_context(self, opts: ta.Optional[ObjMarshalOptions]) -> 'ObjMarshalContext':
|
1390
|
+
return ObjMarshalContext(
|
1391
|
+
options=opts or self._default_options,
|
1392
|
+
manager=self,
|
1393
|
+
)
|
1394
|
+
|
1395
|
+
def marshal_obj(
|
1396
|
+
self,
|
1397
|
+
o: ta.Any,
|
1398
|
+
ty: ta.Any = None,
|
1399
|
+
opts: ta.Optional[ObjMarshalOptions] = None,
|
1400
|
+
) -> ta.Any:
|
1401
|
+
m = self.get_obj_marshaler(ty if ty is not None else type(o))
|
1402
|
+
return m.marshal(o, self._make_context(opts))
|
1403
|
+
|
1404
|
+
def unmarshal_obj(
|
1405
|
+
self,
|
1406
|
+
o: ta.Any,
|
1407
|
+
ty: ta.Union[ta.Type[T], ta.Any],
|
1408
|
+
opts: ta.Optional[ObjMarshalOptions] = None,
|
1409
|
+
) -> T:
|
1410
|
+
m = self.get_obj_marshaler(ty)
|
1411
|
+
return m.unmarshal(o, self._make_context(opts))
|
1412
|
+
|
1413
|
+
def roundtrip_obj(
|
1414
|
+
self,
|
1415
|
+
o: ta.Any,
|
1416
|
+
ty: ta.Any = None,
|
1417
|
+
opts: ta.Optional[ObjMarshalOptions] = None,
|
1418
|
+
) -> ta.Any:
|
1419
|
+
if ty is None:
|
1420
|
+
ty = type(o)
|
1421
|
+
m: ta.Any = self.marshal_obj(o, ty, opts)
|
1422
|
+
u: ta.Any = self.unmarshal_obj(m, ty, opts)
|
1423
|
+
return u
|
1424
|
+
|
1425
|
+
|
1426
|
+
@dc.dataclass(frozen=True)
|
1427
|
+
class ObjMarshalContext:
|
1428
|
+
options: ObjMarshalOptions
|
1429
|
+
manager: ObjMarshalerManager
|
1430
|
+
|
1431
|
+
|
1432
|
+
##
|
1433
|
+
|
1434
|
+
|
1435
|
+
OBJ_MARSHALER_MANAGER = ObjMarshalerManager()
|
1436
|
+
|
1437
|
+
set_obj_marshaler = OBJ_MARSHALER_MANAGER.set_obj_marshaler
|
1438
|
+
get_obj_marshaler = OBJ_MARSHALER_MANAGER.get_obj_marshaler
|
1439
|
+
|
1440
|
+
marshal_obj = OBJ_MARSHALER_MANAGER.marshal_obj
|
1441
|
+
unmarshal_obj = OBJ_MARSHALER_MANAGER.unmarshal_obj
|
1442
|
+
|
1443
|
+
|
1444
|
+
########################################
|
1445
|
+
# dumping.py
|
1446
|
+
|
1447
|
+
|
1448
|
+
##
|
1449
|
+
|
1450
|
+
|
1451
|
+
class _ModuleManifestDumper:
|
1452
|
+
def __init__(
|
1453
|
+
self,
|
1454
|
+
spec: str,
|
1455
|
+
*,
|
1456
|
+
output: ta.Optional[ta.Callable[[str], None]] = None,
|
1457
|
+
) -> None:
|
1458
|
+
super().__init__()
|
1459
|
+
|
1460
|
+
self._spec = spec
|
1461
|
+
if output is None:
|
1462
|
+
output = print
|
1463
|
+
self._output = output
|
1464
|
+
|
1465
|
+
#
|
1466
|
+
|
1467
|
+
@cached_nullary
|
1468
|
+
def _mod(self) -> ta.Any:
|
1469
|
+
return importlib.import_module(self._spec)
|
1470
|
+
|
1471
|
+
#
|
1472
|
+
|
1473
|
+
def _build_manifest_dct(self, manifest: ta.Any) -> ta.Mapping[str, ta.Any]:
|
1474
|
+
manifest_json = json.dumps(marshal_obj(manifest))
|
1475
|
+
manifest_dct = json.loads(manifest_json)
|
1476
|
+
|
1477
|
+
rt_manifest: ta.Any = unmarshal_obj(manifest_dct, type(manifest))
|
1478
|
+
rt_manifest_json: ta.Any = json.dumps(marshal_obj(rt_manifest))
|
1479
|
+
rt_manifest_dct: ta.Any = json.loads(rt_manifest_json)
|
1480
|
+
if rt_manifest_dct != manifest_dct:
|
1481
|
+
raise Exception(
|
1482
|
+
f'Manifest failed to roundtrip: '
|
1483
|
+
f'{manifest} => {manifest_dct} != {rt_manifest} => {rt_manifest_dct}',
|
1484
|
+
)
|
1485
|
+
|
1486
|
+
return manifest_dct
|
1487
|
+
|
1488
|
+
#
|
1489
|
+
|
1490
|
+
def _load_attr_manifest(self, target: dict) -> dict:
|
1491
|
+
attr = target['attr']
|
1492
|
+
manifest = getattr(self._mod(), attr)
|
1493
|
+
|
1494
|
+
if dc.is_dataclass(manifest):
|
1495
|
+
# Support static dataclasses
|
1496
|
+
if isinstance(manifest, type):
|
1497
|
+
manifest = manifest()
|
1498
|
+
|
1499
|
+
manifest_dct = self._build_manifest_dct(manifest)
|
1500
|
+
|
1501
|
+
cls = type(manifest)
|
1502
|
+
key = f'${cls.__module__}.{cls.__qualname__}'
|
1503
|
+
|
1504
|
+
return {key: manifest_dct}
|
1505
|
+
|
1506
|
+
elif isinstance(manifest, collections.abc.Mapping):
|
1507
|
+
[(key, manifest_dct)] = manifest.items()
|
1508
|
+
if not key.startswith('$'): # noqa
|
1509
|
+
raise Exception(f'Bad key: {key}')
|
1510
|
+
|
1511
|
+
if not isinstance(manifest_dct, collections.abc.Mapping):
|
1512
|
+
raise Exception(f'Bad value: {manifest_dct}')
|
1513
|
+
|
1514
|
+
manifest_json = json.dumps(manifest_dct)
|
1515
|
+
|
1516
|
+
rt_manifest_dct = json.loads(manifest_json)
|
1517
|
+
if rt_manifest_dct != manifest_dct:
|
1518
|
+
raise Exception(f'Manifest failed to roundtrip: {manifest_dct} != {rt_manifest_dct}')
|
1519
|
+
|
1520
|
+
return {key: manifest_dct}
|
1521
|
+
|
1522
|
+
else:
|
1523
|
+
raise TypeError(f'Manifest must be dataclass or mapping: {manifest!r}')
|
1524
|
+
|
1525
|
+
#
|
1526
|
+
|
1527
|
+
class _LazyGlobals(dict):
|
1528
|
+
def __init__(self, get_missing: ta.Callable[[str], ta.Any]) -> None:
|
1529
|
+
super().__init__()
|
1530
|
+
|
1531
|
+
self.__get_missing = get_missing
|
1532
|
+
|
1533
|
+
def __missing__(self, key):
|
1534
|
+
return self.__get_missing(key)
|
1535
|
+
|
1536
|
+
def _load_inline_manifest(self, target: dict) -> dict:
|
1537
|
+
cls: ta.Any = importlib.import_module(target['cls_mod_name'])
|
1538
|
+
for p in target['cls_qualname'].split('.'):
|
1539
|
+
cls = getattr(cls, p)
|
1540
|
+
if not isinstance(cls, type) or not dc.is_dataclass(cls):
|
1541
|
+
raise TypeError(cls)
|
1542
|
+
|
1543
|
+
cls_fac = functools.partial(cls, **target['kwargs'])
|
1544
|
+
eval_attr_name = '__manifest_factory__'
|
1545
|
+
|
1546
|
+
inl_glo = self._LazyGlobals(lambda k: getattr(self._mod(), k))
|
1547
|
+
inl_glo.update({
|
1548
|
+
eval_attr_name: cls_fac,
|
1549
|
+
})
|
1550
|
+
|
1551
|
+
inl_src = eval_attr_name + target['init_src']
|
1552
|
+
inl_code = compile(inl_src, '<magic>', 'eval')
|
1553
|
+
|
1554
|
+
manifest = eval(inl_code, inl_glo) # noqa
|
1555
|
+
|
1556
|
+
manifest_dct = self._build_manifest_dct(manifest)
|
1557
|
+
|
1558
|
+
key = f'${cls.__module__}.{cls.__qualname__}'
|
1559
|
+
return {key: manifest_dct}
|
1560
|
+
|
1561
|
+
#
|
1562
|
+
|
1563
|
+
def __call__(
|
1564
|
+
self,
|
1565
|
+
*targets: dict, # .build.ManifestDumperTarget
|
1566
|
+
) -> None:
|
1567
|
+
out = []
|
1568
|
+
for target in targets:
|
1569
|
+
origin = target['origin']
|
1570
|
+
|
1571
|
+
if target['kind'] == 'attr':
|
1572
|
+
out_value = self._load_attr_manifest(target)
|
1573
|
+
|
1574
|
+
elif target['kind'] == 'inline':
|
1575
|
+
out_value = self._load_inline_manifest(target)
|
1576
|
+
|
1577
|
+
else:
|
1578
|
+
raise ValueError(target)
|
1579
|
+
|
1580
|
+
out.append({
|
1581
|
+
**origin,
|
1582
|
+
'value': out_value,
|
1583
|
+
})
|
1584
|
+
|
1585
|
+
out_json = json.dumps(out, indent=None, separators=(',', ':'))
|
1586
|
+
self._output(out_json)
|