omlish 0.0.0.dev245__py3-none-any.whl → 0.0.0.dev247__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- omlish/__about__.py +2 -2
- omlish/collections/__init__.py +0 -1
- omlish/collections/frozen.py +2 -2
- omlish/collections/hasheq.py +1 -2
- omlish/collections/identity.py +7 -2
- omlish/collections/mappings.py +0 -16
- omlish/collections/sorted/sorted.py +1 -2
- omlish/daemons/services.py +7 -0
- omlish/dataclasses/__init__.py +3 -0
- omlish/dataclasses/utils.py +14 -0
- omlish/http/handlers.py +16 -0
- omlish/lang/__init__.py +6 -0
- omlish/lang/attrs.py +29 -0
- omlish/lang/cached/function.py +1 -0
- omlish/lang/collections.py +50 -0
- omlish/marshal/__init__.py +23 -0
- omlish/marshal/composite/wrapped.py +26 -0
- omlish/marshal/objects/dataclasses.py +36 -11
- omlish/marshal/objects/namedtuples.py +9 -9
- omlish/marshal/polymorphism/marshal.py +16 -2
- omlish/marshal/polymorphism/unmarshal.py +16 -2
- omlish/os/pidfiles/pidfile.py +20 -4
- omlish/os/signals.py +5 -1
- omlish/specs/jmespath/cli.py +1 -1
- omlish/sql/queries/rendering.py +108 -87
- {omlish-0.0.0.dev245.dist-info → omlish-0.0.0.dev247.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev245.dist-info → omlish-0.0.0.dev247.dist-info}/RECORD +31 -30
- omlish/sql/queries/rendering2.py +0 -248
- {omlish-0.0.0.dev245.dist-info → omlish-0.0.0.dev247.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev245.dist-info → omlish-0.0.0.dev247.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev245.dist-info → omlish-0.0.0.dev247.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev245.dist-info → omlish-0.0.0.dev247.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
omlish/collections/__init__.py
CHANGED
omlish/collections/frozen.py
CHANGED
@@ -3,7 +3,7 @@ import collections.abc
|
|
3
3
|
import itertools
|
4
4
|
import typing as ta
|
5
5
|
|
6
|
-
from
|
6
|
+
from .. import lang
|
7
7
|
|
8
8
|
|
9
9
|
T = ta.TypeVar('T')
|
@@ -27,7 +27,7 @@ class FrozenDict(ta.Mapping[K, V], Frozen):
|
|
27
27
|
if len(args) > 1:
|
28
28
|
raise TypeError(args)
|
29
29
|
self._dct: dict[K, V] = {}
|
30
|
-
self._dct.update(yield_dict_init(*args, **kwargs))
|
30
|
+
self._dct.update(lang.yield_dict_init(*args, **kwargs))
|
31
31
|
|
32
32
|
@property
|
33
33
|
def debug(self) -> ta.Mapping[K, V]:
|
omlish/collections/hasheq.py
CHANGED
@@ -8,7 +8,6 @@ import dataclasses as dc
|
|
8
8
|
import typing as ta
|
9
9
|
|
10
10
|
from .. import lang
|
11
|
-
from .mappings import yield_dict_init
|
12
11
|
|
13
12
|
|
14
13
|
K = ta.TypeVar('K')
|
@@ -64,7 +63,7 @@ class HashEqMap(ta.MutableMapping[K, V]):
|
|
64
63
|
self._dct: dict[int, list[HashEqMap._Node[K, V]]] = {}
|
65
64
|
self._len = 0
|
66
65
|
|
67
|
-
for k, v in yield_dict_init(*args, **kwargs):
|
66
|
+
for k, v in lang.yield_dict_init(*args, **kwargs):
|
68
67
|
self[k] = v
|
69
68
|
|
70
69
|
def __len__(self) -> int:
|
omlish/collections/identity.py
CHANGED
@@ -4,7 +4,6 @@ import typing as ta
|
|
4
4
|
import weakref
|
5
5
|
|
6
6
|
from .. import lang
|
7
|
-
from .mappings import yield_dict_init
|
8
7
|
|
9
8
|
|
10
9
|
T = ta.TypeVar('T')
|
@@ -38,9 +37,12 @@ class IdentityKeyDict(ta.MutableMapping[K, V]):
|
|
38
37
|
def __init__(self, *args, **kwargs) -> None:
|
39
38
|
super().__init__()
|
40
39
|
self._dict: dict[int, tuple[K, V]] = {}
|
41
|
-
for k, v in yield_dict_init(*args, **kwargs):
|
40
|
+
for k, v in lang.yield_dict_init(*args, **kwargs):
|
42
41
|
self[k] = v
|
43
42
|
|
43
|
+
def __reduce__(self):
|
44
|
+
return (type(self), (list(self.items()),))
|
45
|
+
|
44
46
|
@property
|
45
47
|
def debug(self) -> ta.Sequence[tuple[K, V]]:
|
46
48
|
return list(self.items())
|
@@ -75,6 +77,9 @@ class IdentitySet(ta.MutableSet[T]):
|
|
75
77
|
for item in init:
|
76
78
|
self.add(item)
|
77
79
|
|
80
|
+
def __reduce__(self):
|
81
|
+
return (type(self), (list(self),))
|
82
|
+
|
78
83
|
@property
|
79
84
|
def debug(self) -> ta.Sequence[T]:
|
80
85
|
return list(self)
|
omlish/collections/mappings.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
import collections.abc
|
2
1
|
import typing as ta
|
3
2
|
import weakref
|
4
3
|
|
@@ -33,21 +32,6 @@ def guarded_map_update(
|
|
33
32
|
return dst
|
34
33
|
|
35
34
|
|
36
|
-
def yield_dict_init(*args, **kwargs) -> ta.Iterable[tuple[ta.Any, ta.Any]]:
|
37
|
-
if len(args) > 1:
|
38
|
-
raise TypeError
|
39
|
-
if args:
|
40
|
-
[src] = args
|
41
|
-
if isinstance(src, collections.abc.Mapping):
|
42
|
-
for k in src:
|
43
|
-
yield (k, src[k])
|
44
|
-
else:
|
45
|
-
for k, v in src:
|
46
|
-
yield (k, v)
|
47
|
-
for k, v in kwargs.items():
|
48
|
-
yield (k, v)
|
49
|
-
|
50
|
-
|
51
35
|
class TypeMap(ta.Generic[T]):
|
52
36
|
def __init__(self, items: ta.Iterable[T] = ()) -> None:
|
53
37
|
super().__init__()
|
@@ -2,7 +2,6 @@ import abc
|
|
2
2
|
import typing as ta
|
3
3
|
|
4
4
|
from ... import lang
|
5
|
-
from ..mappings import yield_dict_init
|
6
5
|
|
7
6
|
|
8
7
|
T = ta.TypeVar('T')
|
@@ -83,7 +82,7 @@ class SortedListDict(SortedMutableMapping[K, V]):
|
|
83
82
|
def __init__(self, impl: SortedCollection, *args, **kwargs) -> None:
|
84
83
|
super().__init__()
|
85
84
|
self._impl = impl
|
86
|
-
for k, v in yield_dict_init(*args, **kwargs):
|
85
|
+
for k, v in lang.yield_dict_init(*args, **kwargs):
|
87
86
|
self[k] = v
|
88
87
|
|
89
88
|
@property
|
omlish/daemons/services.py
CHANGED
omlish/dataclasses/__init__.py
CHANGED
omlish/dataclasses/utils.py
CHANGED
@@ -32,6 +32,9 @@ def fields_dict(cls_or_instance: ta.Any) -> dict[str, dc.Field]:
|
|
32
32
|
return {f.name: f for f in dc.fields(cls_or_instance)}
|
33
33
|
|
34
34
|
|
35
|
+
##
|
36
|
+
|
37
|
+
|
35
38
|
class field_modifier: # noqa
|
36
39
|
def __init__(self, fn: ta.Callable[[dc.Field], dc.Field]) -> None:
|
37
40
|
super().__init__()
|
@@ -112,6 +115,17 @@ def update_fields_metadata(
|
|
112
115
|
##
|
113
116
|
|
114
117
|
|
118
|
+
def shallow_astuple(o: ta.Any) -> tuple[ta.Any, ...]:
|
119
|
+
return tuple(getattr(o, f.name) for f in dc.fields(o))
|
120
|
+
|
121
|
+
|
122
|
+
def shallow_asdict(o: ta.Any) -> dict[str, ta.Any]:
|
123
|
+
return {f.name: getattr(o, f.name) for f in dc.fields(o)}
|
124
|
+
|
125
|
+
|
126
|
+
##
|
127
|
+
|
128
|
+
|
115
129
|
def deep_replace(o: T, *args: str | ta.Callable[[ta.Any], ta.Mapping[str, ta.Any]]) -> T:
|
116
130
|
if not args:
|
117
131
|
return o
|
omlish/http/handlers.py
CHANGED
@@ -79,6 +79,22 @@ class LoggingHttpHandler(HttpHandler_):
|
|
79
79
|
return resp
|
80
80
|
|
81
81
|
|
82
|
+
@dc.dataclass(frozen=True)
|
83
|
+
class ExceptionLoggingHttpHandler(HttpHandler_):
|
84
|
+
handler: HttpHandler
|
85
|
+
log: logging.Logger
|
86
|
+
message: ta.Union[str, ta.Callable[[HttpHandlerRequest, BaseException], str]] = 'Error in http handler'
|
87
|
+
|
88
|
+
def __call__(self, req: HttpHandlerRequest) -> HttpHandlerResponse:
|
89
|
+
try:
|
90
|
+
return self.handler(req)
|
91
|
+
except Exception as e: # noqa
|
92
|
+
if callable(msg := self.message):
|
93
|
+
msg = msg(req, e)
|
94
|
+
self.log.exception(msg)
|
95
|
+
raise
|
96
|
+
|
97
|
+
|
82
98
|
##
|
83
99
|
|
84
100
|
|
omlish/lang/__init__.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from .attrs import ( # noqa
|
2
2
|
AttrOps,
|
3
|
+
DictAttrOps,
|
3
4
|
STD_ATTR_OPS,
|
4
5
|
StdAttrOps,
|
5
6
|
TRANSIENT_ATTR_OPS,
|
@@ -76,6 +77,11 @@ from .cmp import ( # noqa
|
|
76
77
|
cmp,
|
77
78
|
)
|
78
79
|
|
80
|
+
from .collections import ( # noqa
|
81
|
+
merge_dicts,
|
82
|
+
yield_dict_init,
|
83
|
+
)
|
84
|
+
|
79
85
|
from .contextmanagers import ( # noqa
|
80
86
|
AsyncContextManager,
|
81
87
|
ContextManaged,
|
omlish/lang/attrs.py
CHANGED
@@ -47,6 +47,35 @@ STD_ATTR_OPS = StdAttrOps()
|
|
47
47
|
##
|
48
48
|
|
49
49
|
|
50
|
+
class DictAttrOps(AttrOps):
|
51
|
+
def __init__(self, dct: ta.MutableMapping[str, ta.Any] | None = None) -> None:
|
52
|
+
super().__init__()
|
53
|
+
|
54
|
+
if dct is None:
|
55
|
+
dct = {}
|
56
|
+
self._dct = dct
|
57
|
+
|
58
|
+
def getattr(self, obj: ta.Any, name: str, default: ta.Any = AttrOps.NOT_SET) -> ta.Any:
|
59
|
+
try:
|
60
|
+
return self._dct[name]
|
61
|
+
except KeyError:
|
62
|
+
if default is not AttrOps.NOT_SET:
|
63
|
+
return default
|
64
|
+
raise AttributeError(name) from None
|
65
|
+
|
66
|
+
def setattr(self, obj: ta.Any, name: str, value: ta.Any) -> None:
|
67
|
+
self._dct[name] = value
|
68
|
+
|
69
|
+
def delattr(self, obj: ta.Any, name: str) -> None:
|
70
|
+
try:
|
71
|
+
del self._dct[name]
|
72
|
+
except KeyError:
|
73
|
+
raise AttributeError(name) from None
|
74
|
+
|
75
|
+
|
76
|
+
##
|
77
|
+
|
78
|
+
|
50
79
|
class TransientDict(collections.abc.MutableMapping):
|
51
80
|
def __init__(self) -> None:
|
52
81
|
super().__init__()
|
omlish/lang/cached/function.py
CHANGED
@@ -6,6 +6,7 @@ TODO:
|
|
6
6
|
- must support free functions (which have no instance nor owner)
|
7
7
|
- 'staticmethod' or effective equiv - which must resolve to the shared instance
|
8
8
|
- and must be transient?
|
9
|
+
- use __transient_dict__ to support common state nuking
|
9
10
|
"""
|
10
11
|
import dataclasses as dc
|
11
12
|
import functools
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import collections.abc
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
|
5
|
+
K = ta.TypeVar('K')
|
6
|
+
V = ta.TypeVar('V')
|
7
|
+
|
8
|
+
|
9
|
+
##
|
10
|
+
|
11
|
+
|
12
|
+
def yield_dict_init(*args: ta.Any, **kwargs: ta.Any) -> ta.Iterable[tuple[ta.Any, ta.Any]]:
|
13
|
+
if len(args) > 1:
|
14
|
+
raise TypeError
|
15
|
+
if args:
|
16
|
+
[src] = args
|
17
|
+
if isinstance(src, collections.abc.Mapping):
|
18
|
+
for k in src:
|
19
|
+
yield (k, src[k])
|
20
|
+
else:
|
21
|
+
for k, v in src:
|
22
|
+
yield (k, v)
|
23
|
+
for k, v in kwargs.items():
|
24
|
+
yield (k, v)
|
25
|
+
|
26
|
+
|
27
|
+
def merge_dicts(
|
28
|
+
*dcts: ta.Mapping[K, V],
|
29
|
+
pair_fn: ta.Callable[[K, V], tuple[K, V] | None] | None = None,
|
30
|
+
conflict_fn: ta.Callable[[K, V, V], tuple[K, V] | None] | None = None,
|
31
|
+
) -> dict[K, V]:
|
32
|
+
out: dict[K, V] = {}
|
33
|
+
|
34
|
+
for d in dcts:
|
35
|
+
for k_v in d.items():
|
36
|
+
if pair_fn is not None and (k_v := pair_fn(*k_v)) is None: # type: ignore[assignment]
|
37
|
+
continue
|
38
|
+
|
39
|
+
k, v = k_v
|
40
|
+
if k in out:
|
41
|
+
if conflict_fn is None:
|
42
|
+
raise KeyError(k)
|
43
|
+
|
44
|
+
if (k_v := conflict_fn(k, out[k], v)) is None: # type: ignore[assignment]
|
45
|
+
continue
|
46
|
+
k, v = k_v
|
47
|
+
|
48
|
+
out[k] = v
|
49
|
+
|
50
|
+
return out
|
omlish/marshal/__init__.py
CHANGED
@@ -28,6 +28,16 @@ from .base import ( # noqa
|
|
28
28
|
ReflectOverride,
|
29
29
|
)
|
30
30
|
|
31
|
+
from .composite.iterables import ( # noqa
|
32
|
+
IterableMarshaler,
|
33
|
+
IterableUnmarshaler,
|
34
|
+
)
|
35
|
+
|
36
|
+
from .composite.wrapped import ( # noqa
|
37
|
+
WrappedMarshaler,
|
38
|
+
WrappedUnmarshaler,
|
39
|
+
)
|
40
|
+
|
31
41
|
from .exceptions import ( # noqa
|
32
42
|
ForbiddenTypeError,
|
33
43
|
MarshalError,
|
@@ -49,6 +59,14 @@ from .naming import ( # noqa
|
|
49
59
|
translate_name,
|
50
60
|
)
|
51
61
|
|
62
|
+
from .objects.dataclasses import ( # noqa
|
63
|
+
AbstractDataclassFactory,
|
64
|
+
DataclassMarshalerFactory,
|
65
|
+
DataclassUnmarshalerFactory,
|
66
|
+
get_dataclass_field_infos,
|
67
|
+
get_dataclass_metadata,
|
68
|
+
)
|
69
|
+
|
52
70
|
from .objects.helpers import ( # noqa
|
53
71
|
update_field_metadata,
|
54
72
|
update_fields_metadata,
|
@@ -75,14 +93,18 @@ from .objects.unmarshal import ( # noqa
|
|
75
93
|
)
|
76
94
|
|
77
95
|
from .polymorphism.marshal import ( # noqa
|
96
|
+
PolymorphismMarshaler,
|
78
97
|
PolymorphismMarshalerFactory,
|
79
98
|
make_polymorphism_marshaler,
|
80
99
|
)
|
81
100
|
|
82
101
|
from .polymorphism.metadata import ( # noqa
|
102
|
+
FieldTypeTagging,
|
83
103
|
Impl,
|
84
104
|
Impls,
|
85
105
|
Polymorphism,
|
106
|
+
TypeTagging,
|
107
|
+
WrapperTypeTagging,
|
86
108
|
polymorphism_from_subclasses,
|
87
109
|
)
|
88
110
|
|
@@ -97,6 +119,7 @@ from .polymorphism.unions import ( # noqa
|
|
97
119
|
)
|
98
120
|
|
99
121
|
from .polymorphism.unmarshal import ( # noqa
|
122
|
+
PolymorphismUnmarshaler,
|
100
123
|
PolymorphismUnmarshalerFactory,
|
101
124
|
make_polymorphism_unmarshaler,
|
102
125
|
)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import dataclasses as dc
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from ..base import MarshalContext
|
5
|
+
from ..base import Marshaler
|
6
|
+
from ..base import UnmarshalContext
|
7
|
+
from ..base import Unmarshaler
|
8
|
+
from ..values import Value
|
9
|
+
|
10
|
+
|
11
|
+
@dc.dataclass(frozen=True)
|
12
|
+
class WrappedMarshaler(Marshaler):
|
13
|
+
wrapper: ta.Callable[[MarshalContext, ta.Any], ta.Any]
|
14
|
+
m: Marshaler
|
15
|
+
|
16
|
+
def marshal(self, ctx: MarshalContext, o: ta.Any) -> Value:
|
17
|
+
return self.m.marshal(ctx, self.wrapper(ctx, o))
|
18
|
+
|
19
|
+
|
20
|
+
@dc.dataclass(frozen=True)
|
21
|
+
class WrappedUnmarshaler(Unmarshaler):
|
22
|
+
unwrapper: ta.Callable[[UnmarshalContext, ta.Any], ta.Any]
|
23
|
+
u: Unmarshaler
|
24
|
+
|
25
|
+
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any:
|
26
|
+
return self.unwrapper(ctx, self.u.unmarshal(ctx, v))
|
@@ -41,12 +41,16 @@ def get_dataclass_metadata(ty: type) -> ObjectMetadata:
|
|
41
41
|
) or ObjectMetadata()
|
42
42
|
|
43
43
|
|
44
|
-
def
|
44
|
+
def get_dataclass_field_infos(
|
45
45
|
ty: type,
|
46
|
-
opts: col.TypeMap[Option] =
|
46
|
+
opts: col.TypeMap[Option] | None = None,
|
47
47
|
) -> FieldInfos:
|
48
|
+
if opts is None:
|
49
|
+
opts = col.TypeMap()
|
50
|
+
|
48
51
|
dc_md = get_dataclass_metadata(ty)
|
49
52
|
dc_naming = dc_md.field_naming or opts.get(Naming)
|
53
|
+
dc_rf = dc.reflect(ty)
|
50
54
|
|
51
55
|
fi_defaults = {
|
52
56
|
k: v
|
@@ -62,18 +66,24 @@ def get_field_infos(
|
|
62
66
|
type_hints = ta.get_type_hints(ty)
|
63
67
|
|
64
68
|
ret: list[FieldInfo] = []
|
65
|
-
for field in
|
69
|
+
for field in dc_rf.fields.values():
|
66
70
|
if (f_naming := field.metadata.get(Naming, dc_naming)) is not None:
|
67
71
|
um_name = translate_name(field.name, f_naming)
|
68
72
|
else:
|
69
73
|
um_name = field.name
|
70
74
|
|
75
|
+
f_ty: ta.Any
|
76
|
+
if (cpx := dc_rf.cls_params_extras) is not None and cpx.generic_init:
|
77
|
+
f_ty = dc_rf.generic_replaced_field_annotations[field.name]
|
78
|
+
else:
|
79
|
+
f_ty = type_hints[field.name]
|
80
|
+
|
71
81
|
fi_kw = dict(fi_defaults)
|
72
82
|
fo_kw = dict(fo_defaults)
|
73
83
|
|
74
84
|
fi_kw.update(
|
75
85
|
name=field.name,
|
76
|
-
type=
|
86
|
+
type=f_ty,
|
77
87
|
metadata=FieldMetadata(),
|
78
88
|
|
79
89
|
marshal_name=um_name,
|
@@ -158,7 +168,22 @@ def _make_field_obj(ctx, ty, obj, fac):
|
|
158
168
|
##
|
159
169
|
|
160
170
|
|
161
|
-
class
|
171
|
+
class AbstractDataclassFactory(lang.Abstract):
|
172
|
+
def _get_metadata(self, ty: type) -> ObjectMetadata:
|
173
|
+
return get_dataclass_metadata(ty)
|
174
|
+
|
175
|
+
def _get_field_infos(
|
176
|
+
self,
|
177
|
+
ty: type,
|
178
|
+
opts: col.TypeMap[Option] | None = None,
|
179
|
+
) -> FieldInfos:
|
180
|
+
return get_dataclass_field_infos(ty, opts)
|
181
|
+
|
182
|
+
|
183
|
+
##
|
184
|
+
|
185
|
+
|
186
|
+
class DataclassMarshalerFactory(AbstractDataclassFactory, MarshalerFactory):
|
162
187
|
def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
|
163
188
|
return isinstance(rty, type) and dc.is_dataclass(rty) and not lang.is_abstract_class(rty)
|
164
189
|
|
@@ -167,8 +192,8 @@ class DataclassMarshalerFactory(MarshalerFactory):
|
|
167
192
|
check.state(dc.is_dataclass(ty))
|
168
193
|
check.state(not lang.is_abstract_class(ty))
|
169
194
|
|
170
|
-
dc_md =
|
171
|
-
fis =
|
195
|
+
dc_md = self._get_metadata(ty)
|
196
|
+
fis = self._get_field_infos(ty, ctx.options)
|
172
197
|
|
173
198
|
fields = [
|
174
199
|
(fi, _make_field_obj(ctx, fi.type, fi.metadata.marshaler, fi.metadata.marshaler_factory))
|
@@ -185,7 +210,7 @@ class DataclassMarshalerFactory(MarshalerFactory):
|
|
185
210
|
##
|
186
211
|
|
187
212
|
|
188
|
-
class DataclassUnmarshalerFactory(UnmarshalerFactory):
|
213
|
+
class DataclassUnmarshalerFactory(AbstractDataclassFactory, UnmarshalerFactory):
|
189
214
|
def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
|
190
215
|
return isinstance(rty, type) and dc.is_dataclass(rty) and not lang.is_abstract_class(rty)
|
191
216
|
|
@@ -194,8 +219,8 @@ class DataclassUnmarshalerFactory(UnmarshalerFactory):
|
|
194
219
|
check.state(dc.is_dataclass(ty))
|
195
220
|
check.state(not lang.is_abstract_class(ty))
|
196
221
|
|
197
|
-
dc_md =
|
198
|
-
fis =
|
222
|
+
dc_md = self._get_metadata(ty)
|
223
|
+
fis = self._get_field_infos(ty, ctx.options)
|
199
224
|
|
200
225
|
d: dict[str, tuple[FieldInfo, Unmarshaler]] = {}
|
201
226
|
defaults: dict[str, ta.Any] = {}
|
@@ -213,7 +238,7 @@ class DataclassUnmarshalerFactory(UnmarshalerFactory):
|
|
213
238
|
raise Exception(f'Embedded fields cannot have specials: {e_ty}')
|
214
239
|
|
215
240
|
embeds[fi.name] = e_ty
|
216
|
-
for e_fi in
|
241
|
+
for e_fi in self._get_field_infos(e_ty, ctx.options):
|
217
242
|
e_ns = add_field(e_fi, prefixes=[p + ep for p in prefixes for ep in fi.unmarshal_names])
|
218
243
|
embeds_by_unmarshal_name.update({e_f: (fi.name, e_fi.name) for e_f in e_ns})
|
219
244
|
ret.extend(e_ns)
|
@@ -22,7 +22,7 @@ from .unmarshal import ObjectUnmarshaler
|
|
22
22
|
##
|
23
23
|
|
24
24
|
|
25
|
-
def
|
25
|
+
def _is_namedtuple(rty: rfl.Type) -> bool:
|
26
26
|
return (
|
27
27
|
isinstance(rty, type) and
|
28
28
|
issubclass(rty, tuple) and
|
@@ -30,11 +30,11 @@ def _is_named_tuple(rty: rfl.Type) -> bool:
|
|
30
30
|
)
|
31
31
|
|
32
32
|
|
33
|
-
def
|
33
|
+
def get_namedtuple_field_infos(
|
34
34
|
ty: type,
|
35
35
|
opts: col.TypeMap[Option] = col.TypeMap(),
|
36
36
|
) -> FieldInfos:
|
37
|
-
check.arg(
|
37
|
+
check.arg(_is_namedtuple(ty), ty)
|
38
38
|
|
39
39
|
sig = inspect.signature(ty)
|
40
40
|
|
@@ -57,14 +57,14 @@ def get_field_infos(
|
|
57
57
|
|
58
58
|
class NamedtupleMarshalerFactory(MarshalerFactory):
|
59
59
|
def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
|
60
|
-
return
|
60
|
+
return _is_namedtuple(rty)
|
61
61
|
|
62
62
|
def fn(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
|
63
|
-
check.state(
|
63
|
+
check.state(_is_namedtuple(rty))
|
64
64
|
ty = check.isinstance(rty, type)
|
65
65
|
check.state(not lang.is_abstract_class(ty))
|
66
66
|
|
67
|
-
fis =
|
67
|
+
fis = get_namedtuple_field_infos(ty, ctx.options)
|
68
68
|
|
69
69
|
fields = [
|
70
70
|
(fi, ctx.make(fi.type))
|
@@ -81,14 +81,14 @@ class NamedtupleMarshalerFactory(MarshalerFactory):
|
|
81
81
|
|
82
82
|
class NamedtupleUnmarshalerFactory(UnmarshalerFactory):
|
83
83
|
def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
|
84
|
-
return
|
84
|
+
return _is_namedtuple(rty)
|
85
85
|
|
86
86
|
def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
|
87
|
-
check.state(
|
87
|
+
check.state(_is_namedtuple(rty))
|
88
88
|
ty = check.isinstance(rty, type)
|
89
89
|
check.state(not lang.is_abstract_class(ty))
|
90
90
|
|
91
|
-
fis =
|
91
|
+
fis = get_namedtuple_field_infos(ty, ctx.options)
|
92
92
|
|
93
93
|
d: dict[str, tuple[FieldInfo, Unmarshaler]] = {}
|
94
94
|
defaults: dict[str, ta.Any] = {}
|
@@ -1,7 +1,9 @@
|
|
1
|
+
import abc
|
1
2
|
import dataclasses as dc
|
2
3
|
import typing as ta
|
3
4
|
|
4
5
|
from ... import check
|
6
|
+
from ... import lang
|
5
7
|
from ... import reflect as rfl
|
6
8
|
from ..base import MarshalContext
|
7
9
|
from ..base import Marshaler
|
@@ -17,20 +19,32 @@ from .metadata import WrapperTypeTagging
|
|
17
19
|
##
|
18
20
|
|
19
21
|
|
22
|
+
class PolymorphismMarshaler(Marshaler, lang.Abstract):
|
23
|
+
@abc.abstractmethod
|
24
|
+
def get_impls(self) -> ta.Mapping[type, tuple[str, Marshaler]]:
|
25
|
+
raise NotImplementedError
|
26
|
+
|
27
|
+
|
20
28
|
@dc.dataclass(frozen=True)
|
21
|
-
class WrapperPolymorphismMarshaler(
|
29
|
+
class WrapperPolymorphismMarshaler(PolymorphismMarshaler):
|
22
30
|
m: ta.Mapping[type, tuple[str, Marshaler]]
|
23
31
|
|
32
|
+
def get_impls(self) -> ta.Mapping[type, tuple[str, Marshaler]]:
|
33
|
+
return self.m
|
34
|
+
|
24
35
|
def marshal(self, ctx: MarshalContext, o: ta.Any | None) -> Value:
|
25
36
|
tag, m = self.m[type(o)]
|
26
37
|
return {tag: m.marshal(ctx, o)}
|
27
38
|
|
28
39
|
|
29
40
|
@dc.dataclass(frozen=True)
|
30
|
-
class FieldPolymorphismMarshaler(
|
41
|
+
class FieldPolymorphismMarshaler(PolymorphismMarshaler):
|
31
42
|
m: ta.Mapping[type, tuple[str, Marshaler]]
|
32
43
|
tf: str
|
33
44
|
|
45
|
+
def get_impls(self) -> ta.Mapping[type, tuple[str, Marshaler]]:
|
46
|
+
return self.m
|
47
|
+
|
34
48
|
def marshal(self, ctx: MarshalContext, o: ta.Any | None) -> Value:
|
35
49
|
tag, m = self.m[type(o)]
|
36
50
|
return {self.tf: tag, **m.marshal(ctx, o)} # type: ignore
|
@@ -1,8 +1,10 @@
|
|
1
|
+
import abc
|
1
2
|
import collections.abc
|
2
3
|
import dataclasses as dc
|
3
4
|
import typing as ta
|
4
5
|
|
5
6
|
from ... import check
|
7
|
+
from ... import lang
|
6
8
|
from ... import reflect as rfl
|
7
9
|
from ..base import UnmarshalContext
|
8
10
|
from ..base import Unmarshaler
|
@@ -18,10 +20,19 @@ from .metadata import WrapperTypeTagging
|
|
18
20
|
##
|
19
21
|
|
20
22
|
|
23
|
+
class PolymorphismUnmarshaler(Unmarshaler, lang.Abstract):
|
24
|
+
@abc.abstractmethod
|
25
|
+
def get_impls(self) -> ta.Mapping[str, Unmarshaler]:
|
26
|
+
raise NotImplementedError
|
27
|
+
|
28
|
+
|
21
29
|
@dc.dataclass(frozen=True)
|
22
|
-
class WrapperPolymorphismUnmarshaler(
|
30
|
+
class WrapperPolymorphismUnmarshaler(PolymorphismUnmarshaler):
|
23
31
|
m: ta.Mapping[str, Unmarshaler]
|
24
32
|
|
33
|
+
def get_impls(self) -> ta.Mapping[str, Unmarshaler]:
|
34
|
+
return self.m
|
35
|
+
|
25
36
|
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any | None:
|
26
37
|
ma = check.isinstance(v, collections.abc.Mapping)
|
27
38
|
[(tag, iv)] = ma.items()
|
@@ -30,10 +41,13 @@ class WrapperPolymorphismUnmarshaler(Unmarshaler):
|
|
30
41
|
|
31
42
|
|
32
43
|
@dc.dataclass(frozen=True)
|
33
|
-
class FieldPolymorphismUnmarshaler(
|
44
|
+
class FieldPolymorphismUnmarshaler(PolymorphismUnmarshaler):
|
34
45
|
m: ta.Mapping[str, Unmarshaler]
|
35
46
|
tf: str
|
36
47
|
|
48
|
+
def get_impls(self) -> ta.Mapping[str, Unmarshaler]:
|
49
|
+
return self.m
|
50
|
+
|
37
51
|
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any | None:
|
38
52
|
ma = dict(check.isinstance(v, collections.abc.Mapping))
|
39
53
|
tag = ma.pop(self.tf)
|