omlish 0.0.0.dev452__py3-none-any.whl → 0.0.0.dev454__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 +7 -1
- omlish/collections/attrregistry.py +182 -0
- omlish/collections/identity.py +1 -0
- omlish/collections/mappings.py +25 -0
- omlish/dataclasses/__init__.py +2 -0
- omlish/dispatch/methods.py +50 -140
- omlish/dom/rendering.py +1 -1
- omlish/funcs/guard.py +225 -0
- omlish/graphs/dot/rendering.py +1 -1
- omlish/lang/__init__.py +1 -0
- omlish/lang/iterables.py +8 -0
- omlish/lite/attrops.py +2 -0
- omlish/lite/dataclasses.py +30 -0
- omlish/lite/maybes.py +8 -0
- omlish/marshal/__init__.py +12 -11
- omlish/marshal/base/contexts.py +4 -7
- omlish/marshal/base/funcs.py +16 -11
- omlish/marshal/base/types.py +17 -7
- omlish/marshal/composite/iterables.py +31 -20
- omlish/marshal/composite/literals.py +14 -18
- omlish/marshal/composite/mappings.py +34 -23
- omlish/marshal/composite/maybes.py +27 -19
- omlish/marshal/composite/newtypes.py +14 -14
- omlish/marshal/composite/optionals.py +12 -14
- omlish/marshal/composite/special.py +13 -13
- omlish/marshal/composite/unions/__init__.py +0 -0
- omlish/marshal/composite/unions/literals.py +91 -0
- omlish/marshal/composite/unions/primitives.py +101 -0
- omlish/marshal/factories/invalidate.py +16 -66
- omlish/marshal/factories/method.py +28 -0
- omlish/marshal/factories/moduleimport/factories.py +13 -54
- omlish/marshal/factories/multi.py +11 -23
- omlish/marshal/factories/recursive.py +40 -56
- omlish/marshal/factories/typecache.py +23 -75
- omlish/marshal/factories/typemap.py +40 -41
- omlish/marshal/objects/dataclasses.py +106 -97
- omlish/marshal/objects/marshal.py +15 -12
- omlish/marshal/objects/namedtuples.py +46 -40
- omlish/marshal/objects/unmarshal.py +16 -13
- omlish/marshal/polymorphism/marshal.py +6 -9
- omlish/marshal/polymorphism/unions.py +15 -9
- omlish/marshal/polymorphism/unmarshal.py +6 -8
- omlish/marshal/singular/enums.py +12 -18
- omlish/marshal/standard.py +8 -8
- omlish/marshal/trivial/forbidden.py +19 -24
- omlish/os/forkhooks.py +4 -0
- omlish/specs/jsonrpc/_marshal.py +33 -24
- omlish/specs/openapi/_marshal.py +20 -17
- omlish/sql/queries/rendering.py +1 -1
- omlish/text/parts.py +2 -2
- omlish/typedvalues/marshal.py +81 -55
- {omlish-0.0.0.dev452.dist-info → omlish-0.0.0.dev454.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev452.dist-info → omlish-0.0.0.dev454.dist-info}/RECORD +58 -55
- omlish/marshal/composite/unions.py +0 -213
- omlish/marshal/factories/match.py +0 -34
- omlish/marshal/factories/simple.py +0 -28
- {omlish-0.0.0.dev452.dist-info → omlish-0.0.0.dev454.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev452.dist-info → omlish-0.0.0.dev454.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev452.dist-info → omlish-0.0.0.dev454.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev452.dist-info → omlish-0.0.0.dev454.dist-info}/top_level.txt +0 -0
omlish/funcs/guard.py
ADDED
@@ -0,0 +1,225 @@
|
|
1
|
+
import abc
|
2
|
+
import functools
|
3
|
+
import operator
|
4
|
+
import typing as ta
|
5
|
+
|
6
|
+
from .. import check
|
7
|
+
from .. import collections as col
|
8
|
+
from .. import lang
|
9
|
+
|
10
|
+
|
11
|
+
T = ta.TypeVar('T')
|
12
|
+
T_co = ta.TypeVar('T_co', covariant=True)
|
13
|
+
U = ta.TypeVar('U')
|
14
|
+
P = ta.ParamSpec('P')
|
15
|
+
|
16
|
+
|
17
|
+
##
|
18
|
+
|
19
|
+
|
20
|
+
class GuardFn(ta.Protocol[P, T_co]):
|
21
|
+
def __get__(self, instance, owner=None): ...
|
22
|
+
|
23
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> ta.Callable[[], T_co] | None: ...
|
24
|
+
|
25
|
+
|
26
|
+
##
|
27
|
+
|
28
|
+
|
29
|
+
@ta.final
|
30
|
+
class DumbGuardFn(ta.Generic[P, T]):
|
31
|
+
def __init__(self, fn: ta.Callable[P, T]) -> None:
|
32
|
+
self._fn = fn
|
33
|
+
|
34
|
+
def __get__(self, instance, owner=None):
|
35
|
+
return DumbGuardFn(self._fn.__get__(instance, owner)) # noqa
|
36
|
+
|
37
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> ta.Callable[[], T]:
|
38
|
+
return functools.partial(self._fn, *args, **kwargs)
|
39
|
+
|
40
|
+
|
41
|
+
dumb = DumbGuardFn
|
42
|
+
|
43
|
+
|
44
|
+
##
|
45
|
+
|
46
|
+
|
47
|
+
class AmbiguousGuardFnError(Exception):
|
48
|
+
pass
|
49
|
+
|
50
|
+
|
51
|
+
@ta.final
|
52
|
+
class MultiGuardFn(ta.Generic[P, T]):
|
53
|
+
def __init__(
|
54
|
+
self,
|
55
|
+
*children: GuardFn[P, T],
|
56
|
+
default: GuardFn[P, T] | None = None,
|
57
|
+
strict: bool = False,
|
58
|
+
) -> None:
|
59
|
+
self._children, self._default, self._strict = children, default, strict
|
60
|
+
|
61
|
+
lang.attr_ops(lambda self: (
|
62
|
+
self._children,
|
63
|
+
self._default,
|
64
|
+
self._strict,
|
65
|
+
)).install(locals())
|
66
|
+
|
67
|
+
def __get__(self, instance, owner=None):
|
68
|
+
return MultiGuardFn(*map(operator.methodcaller('__get__', instance, owner), self._children), default=self._default.__get__(instance, owner) if self._default is not None else None, strict=self._strict) # noqa
|
69
|
+
|
70
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> ta.Callable[[], T] | None:
|
71
|
+
matches = []
|
72
|
+
for c in self._children:
|
73
|
+
if (m := c(*args, **kwargs)) is not None:
|
74
|
+
if not self._strict:
|
75
|
+
return m
|
76
|
+
matches.append(m)
|
77
|
+
if not matches:
|
78
|
+
if (dfl := self._default) is not None:
|
79
|
+
return dfl(*args, **kwargs)
|
80
|
+
else:
|
81
|
+
return None
|
82
|
+
elif len(matches) > 1:
|
83
|
+
raise AmbiguousGuardFnError
|
84
|
+
else:
|
85
|
+
return matches[0]
|
86
|
+
|
87
|
+
|
88
|
+
multi = MultiGuardFn
|
89
|
+
|
90
|
+
|
91
|
+
##
|
92
|
+
|
93
|
+
|
94
|
+
class _BaseGuardFnMethod(lang.Abstract, ta.Generic[P, T]):
|
95
|
+
def __init__(
|
96
|
+
self,
|
97
|
+
*,
|
98
|
+
strict: bool = False,
|
99
|
+
requires_override: bool = False,
|
100
|
+
instance_cache: bool = False,
|
101
|
+
default: GuardFn[P, T] | None = None,
|
102
|
+
) -> None:
|
103
|
+
super().__init__()
|
104
|
+
|
105
|
+
self._strict = strict
|
106
|
+
self._instance_cache = instance_cache
|
107
|
+
self._default = default
|
108
|
+
|
109
|
+
self._registry: col.AttrRegistry[ta.Callable, None] = col.AttrRegistry(
|
110
|
+
requires_override=requires_override,
|
111
|
+
)
|
112
|
+
|
113
|
+
self._cache: col.AttrRegistryCache[ta.Callable, None, MultiGuardFn] = col.AttrRegistryCache(
|
114
|
+
self._registry,
|
115
|
+
self._prepare,
|
116
|
+
)
|
117
|
+
|
118
|
+
_owner: type | None = None
|
119
|
+
_name: str | None = None
|
120
|
+
|
121
|
+
def __set_name__(self, owner, name):
|
122
|
+
if self._owner is None:
|
123
|
+
self._owner = owner
|
124
|
+
if self._name is None:
|
125
|
+
self._name = name
|
126
|
+
|
127
|
+
def register(self, fn: U) -> U:
|
128
|
+
check.callable(fn)
|
129
|
+
self._registry.register(ta.cast(ta.Callable, fn), None)
|
130
|
+
return fn
|
131
|
+
|
132
|
+
def _prepare(self, instance_cls: type, collected: ta.Mapping[str, tuple[ta.Callable, None]]) -> MultiGuardFn:
|
133
|
+
return MultiGuardFn(
|
134
|
+
*[getattr(instance_cls, a) for a in collected],
|
135
|
+
default=self._default,
|
136
|
+
strict=self._strict,
|
137
|
+
)
|
138
|
+
|
139
|
+
@abc.abstractmethod
|
140
|
+
def _bind(self, instance, owner):
|
141
|
+
raise NotImplementedError
|
142
|
+
|
143
|
+
def __get__(self, instance, owner=None):
|
144
|
+
if instance is None:
|
145
|
+
return self
|
146
|
+
|
147
|
+
if self._instance_cache:
|
148
|
+
try:
|
149
|
+
return instance.__dict__[self._name]
|
150
|
+
except KeyError:
|
151
|
+
pass
|
152
|
+
|
153
|
+
bound = self._bind(instance, owner)
|
154
|
+
|
155
|
+
if self._instance_cache:
|
156
|
+
instance.__dict__[self._name] = bound
|
157
|
+
|
158
|
+
return bound
|
159
|
+
|
160
|
+
def _call(self, *args, **kwargs):
|
161
|
+
instance, *rest = args
|
162
|
+
return self.__get__(instance)(*rest, **kwargs)
|
163
|
+
|
164
|
+
#
|
165
|
+
|
166
|
+
|
167
|
+
@ta.final
|
168
|
+
class GuardFnMethod(_BaseGuardFnMethod[P, T]):
|
169
|
+
def _bind(self, instance, owner):
|
170
|
+
return self._cache.get(type(instance)).__get__(instance, owner) # noqa
|
171
|
+
|
172
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> ta.Callable[[], T] | None:
|
173
|
+
return self._call(*args, **kwargs)
|
174
|
+
|
175
|
+
|
176
|
+
def method(
|
177
|
+
*,
|
178
|
+
strict: bool = False,
|
179
|
+
requires_override: bool = False,
|
180
|
+
instance_cache: bool = False,
|
181
|
+
default: bool = False,
|
182
|
+
) -> ta.Callable[[ta.Callable[P, T]], GuardFnMethod[P, T]]: # noqa
|
183
|
+
def inner(fn):
|
184
|
+
return GuardFnMethod(
|
185
|
+
strict=strict,
|
186
|
+
requires_override=requires_override,
|
187
|
+
instance_cache=instance_cache,
|
188
|
+
default=fn if default else None,
|
189
|
+
)
|
190
|
+
|
191
|
+
return inner
|
192
|
+
|
193
|
+
|
194
|
+
#
|
195
|
+
|
196
|
+
|
197
|
+
@ta.final
|
198
|
+
class ImmediateGuardFnMethod(_BaseGuardFnMethod[P, T]):
|
199
|
+
def _bind(self, instance, owner):
|
200
|
+
gf = self._cache.get(type(instance)).__get__(instance, owner) # noqa
|
201
|
+
|
202
|
+
def inner(*args, **kwargs):
|
203
|
+
return gf(*args, **kwargs)() # Note: cannot be None due to non-optional default
|
204
|
+
|
205
|
+
return inner
|
206
|
+
|
207
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T:
|
208
|
+
return self._call(*args, **kwargs)
|
209
|
+
|
210
|
+
|
211
|
+
def immediate_method(
|
212
|
+
*,
|
213
|
+
strict: bool = False,
|
214
|
+
requires_override: bool = False,
|
215
|
+
instance_cache: bool = False,
|
216
|
+
) -> ta.Callable[[ta.Callable[P, T]], ImmediateGuardFnMethod[P, T]]: # noqa
|
217
|
+
def inner(fn):
|
218
|
+
return ImmediateGuardFnMethod(
|
219
|
+
strict=strict,
|
220
|
+
requires_override=requires_override,
|
221
|
+
instance_cache=instance_cache,
|
222
|
+
default=(lambda *args, **kwargs: lambda: fn(*args, **kwargs)),
|
223
|
+
)
|
224
|
+
|
225
|
+
return inner
|
omlish/graphs/dot/rendering.py
CHANGED
omlish/lang/__init__.py
CHANGED
omlish/lang/iterables.py
CHANGED
@@ -26,6 +26,14 @@ def consume(it: ta.Iterable[ta.Any]) -> None:
|
|
26
26
|
collections.deque(it, maxlen=0)
|
27
27
|
|
28
28
|
|
29
|
+
def iterfrom(seq: ta.Sequence[T], start: int = 0, stop: int | None = None) -> ta.Iterator[T]:
|
30
|
+
if start < 0:
|
31
|
+
start += len(seq)
|
32
|
+
if stop is None:
|
33
|
+
stop = len(seq)
|
34
|
+
return map(seq.__getitem__, range(start, stop))
|
35
|
+
|
36
|
+
|
29
37
|
def peek(vs: ta.Iterable[T]) -> tuple[T, ta.Iterator[T]]:
|
30
38
|
it = iter(vs)
|
31
39
|
v = next(it)
|
omlish/lite/attrops.py
CHANGED
@@ -6,6 +6,8 @@ TODO:
|
|
6
6
|
- per-attr repr transform / filter
|
7
7
|
- __ne__ ? cases where it still matters
|
8
8
|
- ordering ?
|
9
|
+
- repr_filter: ta.Union[ta.Callable[[ta.Any], ta.Optional[str]], ta.Literal['not_none', 'truthy']]] ?
|
10
|
+
- unify repr/repr_fn/repr_filter
|
9
11
|
"""
|
10
12
|
import types # noqa
|
11
13
|
import typing as ta
|
omlish/lite/dataclasses.py
CHANGED
@@ -100,6 +100,36 @@ def dataclass_repr_omit_falsey(obj: ta.Any) -> str:
|
|
100
100
|
##
|
101
101
|
|
102
102
|
|
103
|
+
def dataclass_descriptor_method(*bind_attrs: str, bind_owner: bool = False) -> ta.Callable:
|
104
|
+
if not bind_attrs:
|
105
|
+
def __get__(self, instance, owner=None): # noqa
|
106
|
+
return self
|
107
|
+
|
108
|
+
elif bind_owner:
|
109
|
+
def __get__(self, instance, owner=None): # noqa
|
110
|
+
# Guaranteed to return a new instance even with no attrs
|
111
|
+
return dc.replace(self, **{
|
112
|
+
a: v.__get__(instance, owner) if (v := getattr(self, a)) is not None else None
|
113
|
+
for a in bind_attrs
|
114
|
+
})
|
115
|
+
|
116
|
+
else:
|
117
|
+
def __get__(self, instance, owner=None): # noqa
|
118
|
+
if instance is None:
|
119
|
+
return self
|
120
|
+
|
121
|
+
# Guaranteed to return a new instance even with no attrs
|
122
|
+
return dc.replace(self, **{
|
123
|
+
a: v.__get__(instance, owner) if (v := getattr(self, a)) is not None else None
|
124
|
+
for a in bind_attrs
|
125
|
+
})
|
126
|
+
|
127
|
+
return __get__
|
128
|
+
|
129
|
+
|
130
|
+
##
|
131
|
+
|
132
|
+
|
103
133
|
def dataclass_kw_only_init():
|
104
134
|
def inner(cls):
|
105
135
|
if not isinstance(cls, type) and dc.is_dataclass(cls):
|
omlish/lite/maybes.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# ruff: noqa: UP007 UP045
|
2
2
|
import abc
|
3
3
|
import functools
|
4
|
+
import operator
|
4
5
|
import typing as ta
|
5
6
|
|
6
7
|
from .abstract import Abstract
|
@@ -208,3 +209,10 @@ class _EmptyMaybe(_Maybe[T]):
|
|
208
209
|
|
209
210
|
|
210
211
|
Maybe._empty = _EmptyMaybe() # noqa
|
212
|
+
|
213
|
+
|
214
|
+
##
|
215
|
+
|
216
|
+
|
217
|
+
setattr(Maybe, 'just', _JustMaybe) # noqa
|
218
|
+
setattr(Maybe, 'empty', functools.partial(operator.attrgetter('_empty'), Maybe))
|
omlish/marshal/__init__.py
CHANGED
@@ -113,10 +113,15 @@ with _lang.auto_proxy_init(globals()):
|
|
113
113
|
OptionalUnmarshaler,
|
114
114
|
)
|
115
115
|
|
116
|
-
from .composite.unions import ( # noqa
|
117
|
-
|
118
|
-
|
116
|
+
from .composite.unions.literals import ( # noqa
|
117
|
+
LITERAL_UNION_TYPES,
|
118
|
+
LiteralUnionMarshaler,
|
119
|
+
LiteralUnionMarshalerFactory,
|
120
|
+
LiteralUnionUnmarshaler,
|
121
|
+
LiteralUnionUnmarshalerFactory,
|
122
|
+
)
|
119
123
|
|
124
|
+
from .composite.unions.primitives import ( # noqa
|
120
125
|
PRIMITIVE_UNION_TYPES,
|
121
126
|
PrimitiveUnionMarshaler,
|
122
127
|
PrimitiveUnionMarshalerFactory,
|
@@ -129,14 +134,9 @@ with _lang.auto_proxy_init(globals()):
|
|
129
134
|
WrappedUnmarshaler,
|
130
135
|
)
|
131
136
|
|
132
|
-
from .factories.
|
133
|
-
|
134
|
-
|
135
|
-
)
|
136
|
-
|
137
|
-
from .factories.match import ( # noqa
|
138
|
-
MarshalerFactoryMatchClass,
|
139
|
-
UnmarshalerFactoryMatchClass,
|
137
|
+
from .factories.method import ( # noqa
|
138
|
+
MarshalerFactoryMethodClass,
|
139
|
+
UnmarshalerFactoryMethodClass,
|
140
140
|
)
|
141
141
|
|
142
142
|
from .factories.moduleimport.configs import ( # noqa
|
@@ -246,6 +246,7 @@ with _lang.auto_proxy_init(globals()):
|
|
246
246
|
|
247
247
|
from .trivial.forbidden import ( # noqa
|
248
248
|
ForbiddenTypeMarshalerFactory,
|
249
|
+
ForbiddenTypeMarshalerFactoryUnmarshalerFactory,
|
249
250
|
ForbiddenTypeUnmarshalerFactory,
|
250
251
|
)
|
251
252
|
|
omlish/marshal/base/contexts.py
CHANGED
@@ -5,7 +5,6 @@ from ... import check
|
|
5
5
|
from ... import collections as col
|
6
6
|
from ... import lang
|
7
7
|
from ... import reflect as rfl
|
8
|
-
from ...funcs import match as mfs
|
9
8
|
from .configs import EMPTY_CONFIG_REGISTRY
|
10
9
|
from .configs import ConfigRegistry
|
11
10
|
from .errors import UnhandledTypeError
|
@@ -48,10 +47,9 @@ class MarshalContext(BaseContext, lang.Final):
|
|
48
47
|
def make(self, o: ta.Any) -> 'Marshaler':
|
49
48
|
rty = self._reflect(o)
|
50
49
|
fac = check.not_none(self.factory)
|
51
|
-
|
52
|
-
return fac.make_marshaler(self, rty)
|
53
|
-
except mfs.MatchGuardError:
|
50
|
+
if (mfn := fac.make_marshaler(self, rty)) is None:
|
54
51
|
raise UnhandledTypeError(rty) # noqa
|
52
|
+
return mfn()
|
55
53
|
|
56
54
|
def marshal(self, obj: ta.Any, ty: ta.Any | None = None) -> 'Value':
|
57
55
|
return self.make(ty if ty is not None else type(obj)).marshal(self, obj)
|
@@ -64,10 +62,9 @@ class UnmarshalContext(BaseContext, lang.Final):
|
|
64
62
|
def make(self, o: ta.Any) -> 'Unmarshaler':
|
65
63
|
rty = self._reflect(o)
|
66
64
|
fac = check.not_none(self.factory)
|
67
|
-
|
68
|
-
return fac.make_unmarshaler(self, rty)
|
69
|
-
except mfs.MatchGuardError:
|
65
|
+
if (mfn := fac.make_unmarshaler(self, rty)) is None:
|
70
66
|
raise UnhandledTypeError(rty) # noqa
|
67
|
+
return mfn()
|
71
68
|
|
72
69
|
@ta.overload
|
73
70
|
def unmarshal(self, v: 'Value', ty: type[T]) -> T:
|
omlish/marshal/base/funcs.py
CHANGED
@@ -3,7 +3,7 @@ import typing as ta
|
|
3
3
|
|
4
4
|
from ... import lang
|
5
5
|
from ... import reflect as rfl
|
6
|
-
from ...funcs import
|
6
|
+
from ...funcs import guard as gfs
|
7
7
|
from .contexts import MarshalContext
|
8
8
|
from .contexts import UnmarshalContext
|
9
9
|
from .types import Marshaler
|
@@ -39,19 +39,24 @@ class FuncUnmarshaler(Unmarshaler, lang.Final):
|
|
39
39
|
|
40
40
|
@dc.dataclass(frozen=True)
|
41
41
|
class FuncMarshalerFactory(MarshalerFactory): # noqa
|
42
|
-
|
43
|
-
fn: ta.Callable[[MarshalContext, rfl.Type], Marshaler]
|
42
|
+
gf: MarshalerMaker
|
44
43
|
|
45
|
-
|
46
|
-
|
47
|
-
return mfs.simple(self.guard, self.fn)
|
44
|
+
def make_marshaler(self, ctx: MarshalContext, rty: rfl.Type) -> ta.Callable[[], Marshaler] | None:
|
45
|
+
return self.gf(ctx, rty)
|
48
46
|
|
49
47
|
|
50
48
|
@dc.dataclass(frozen=True)
|
51
49
|
class FuncUnmarshalerFactory(UnmarshalerFactory): # noqa
|
52
|
-
|
53
|
-
fn: ta.Callable[[UnmarshalContext, rfl.Type], Unmarshaler]
|
50
|
+
gf: UnmarshalerMaker
|
54
51
|
|
55
|
-
|
56
|
-
|
57
|
-
|
52
|
+
def make_unmarshaler(self, ctx: UnmarshalContext, rty: rfl.Type) -> ta.Callable[[], Unmarshaler] | None:
|
53
|
+
return self.gf(ctx, rty)
|
54
|
+
|
55
|
+
|
56
|
+
##
|
57
|
+
|
58
|
+
|
59
|
+
class GuardMethodMarshalerFactory(MarshalerFactory):
|
60
|
+
@gfs.method(instance_cache=True)
|
61
|
+
def make_marshaler(self, ctx: MarshalContext, rty: rfl.Type) -> ta.Callable[[], Marshaler] | None:
|
62
|
+
raise NotImplementedError
|
omlish/marshal/base/types.py
CHANGED
@@ -3,7 +3,7 @@ import typing as ta
|
|
3
3
|
|
4
4
|
from ... import lang
|
5
5
|
from ... import reflect as rfl
|
6
|
-
from ...funcs import
|
6
|
+
from ...funcs import guard as gfs
|
7
7
|
from .configs import ConfigRegistry
|
8
8
|
from .contexts import MarshalContext
|
9
9
|
from .contexts import UnmarshalContext
|
@@ -31,21 +31,19 @@ class Unmarshaler(lang.Abstract):
|
|
31
31
|
##
|
32
32
|
|
33
33
|
|
34
|
-
MarshalerMaker: ta.TypeAlias =
|
35
|
-
UnmarshalerMaker: ta.TypeAlias =
|
34
|
+
MarshalerMaker: ta.TypeAlias = gfs.GuardFn[[MarshalContext, rfl.Type], Marshaler]
|
35
|
+
UnmarshalerMaker: ta.TypeAlias = gfs.GuardFn[[UnmarshalContext, rfl.Type], Unmarshaler]
|
36
36
|
|
37
37
|
|
38
38
|
class MarshalerFactory(lang.Abstract):
|
39
|
-
@property
|
40
39
|
@abc.abstractmethod
|
41
|
-
def make_marshaler(self) ->
|
40
|
+
def make_marshaler(self, ctx: MarshalContext, rty: rfl.Type) -> ta.Callable[[], Marshaler] | None:
|
42
41
|
raise NotImplementedError
|
43
42
|
|
44
43
|
|
45
44
|
class UnmarshalerFactory(lang.Abstract):
|
46
|
-
@property
|
47
45
|
@abc.abstractmethod
|
48
|
-
def make_unmarshaler(self) ->
|
46
|
+
def make_unmarshaler(self, ctx: UnmarshalContext, rty: rfl.Type) -> ta.Callable[[], Unmarshaler] | None:
|
49
47
|
raise NotImplementedError
|
50
48
|
|
51
49
|
|
@@ -98,3 +96,15 @@ class Marshaling(lang.Abstract):
|
|
98
96
|
@ta.final
|
99
97
|
def unmarshal(self, v, ty, **kwargs):
|
100
98
|
return self.new_unmarshal_context(**kwargs).unmarshal(v, ty)
|
99
|
+
|
100
|
+
|
101
|
+
##
|
102
|
+
|
103
|
+
|
104
|
+
# MarshalerOrUnmarshaler: ta.TypeAlias = Marshaler | Unmarshaler
|
105
|
+
# MarshalerOrUnmarshalerT = ta.TypeVar('MarshalerOrUnmarshalerT', bound=MarshalerOrUnmarshaler)
|
106
|
+
#
|
107
|
+
# MarshalContextOrUnmarshalContext: ta.TypeAlias = MarshalContext | UnmarshalContext
|
108
|
+
# MarshalContextOrUnmarshalContextT = ta.TypeVar('MarshalContextOrUnmarshalContextT', bound=MarshalContextOrUnmarshalContext) # noqa
|
109
|
+
#
|
110
|
+
# MarshalerMakerOrUnmarshalerMaker: ta.TypeAlias = MarshalerMaker | UnmarshalerMaker
|
@@ -9,14 +9,13 @@ import typing as ta
|
|
9
9
|
|
10
10
|
from ... import check
|
11
11
|
from ... import reflect as rfl
|
12
|
-
from ...funcs import match as mfs
|
13
12
|
from ..base.contexts import MarshalContext
|
14
13
|
from ..base.contexts import UnmarshalContext
|
15
14
|
from ..base.types import Marshaler
|
16
15
|
from ..base.types import Unmarshaler
|
17
16
|
from ..base.values import Value
|
18
|
-
from ..factories.
|
19
|
-
from ..factories.
|
17
|
+
from ..factories.method import MarshalerFactoryMethodClass
|
18
|
+
from ..factories.method import UnmarshalerFactoryMethodClass
|
20
19
|
|
21
20
|
|
22
21
|
##
|
@@ -29,6 +28,9 @@ DEFAULT_ITERABLE_CONCRETE_TYPES: dict[type[collections.abc.Iterable], type[colle
|
|
29
28
|
}
|
30
29
|
|
31
30
|
|
31
|
+
#
|
32
|
+
|
33
|
+
|
32
34
|
@dc.dataclass(frozen=True)
|
33
35
|
class IterableMarshaler(Marshaler):
|
34
36
|
e: Marshaler
|
@@ -37,15 +39,21 @@ class IterableMarshaler(Marshaler):
|
|
37
39
|
return list(map(functools.partial(self.e.marshal, ctx), o))
|
38
40
|
|
39
41
|
|
40
|
-
class IterableMarshalerFactory(
|
41
|
-
@
|
42
|
-
def
|
43
|
-
|
44
|
-
|
42
|
+
class IterableMarshalerFactory(MarshalerFactoryMethodClass):
|
43
|
+
@MarshalerFactoryMethodClass.make_marshaler.register
|
44
|
+
def _make_generic(self, ctx: MarshalContext, rty: rfl.Type) -> ta.Callable[[], Marshaler] | None:
|
45
|
+
if not (isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Iterable)):
|
46
|
+
return None
|
47
|
+
return lambda: IterableMarshaler(ctx.make(check.single(rty.args)))
|
48
|
+
|
49
|
+
@MarshalerFactoryMethodClass.make_marshaler.register
|
50
|
+
def _make_concrete(self, ctx: MarshalContext, rty: rfl.Type) -> ta.Callable[[], Marshaler] | None:
|
51
|
+
if not (isinstance(rty, type) and issubclass(rty, collections.abc.Iterable)):
|
52
|
+
return None
|
53
|
+
return lambda: IterableMarshaler(ctx.make(ta.Any))
|
54
|
+
|
45
55
|
|
46
|
-
|
47
|
-
def _build_concrete(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
|
48
|
-
return IterableMarshaler(ctx.make(ta.Any))
|
56
|
+
#
|
49
57
|
|
50
58
|
|
51
59
|
@dc.dataclass(frozen=True)
|
@@ -57,13 +65,16 @@ class IterableUnmarshaler(Unmarshaler):
|
|
57
65
|
return self.ctor(map(functools.partial(self.e.unmarshal, ctx), check.isinstance(v, collections.abc.Iterable)))
|
58
66
|
|
59
67
|
|
60
|
-
class IterableUnmarshalerFactory(
|
61
|
-
@
|
62
|
-
def
|
63
|
-
|
64
|
-
|
65
|
-
|
68
|
+
class IterableUnmarshalerFactory(UnmarshalerFactoryMethodClass):
|
69
|
+
@UnmarshalerFactoryMethodClass.make_unmarshaler.register
|
70
|
+
def _make_generic(self, ctx: UnmarshalContext, rty: rfl.Type) -> ta.Callable[[], Unmarshaler] | None:
|
71
|
+
if not (isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Iterable)):
|
72
|
+
return None
|
73
|
+
cty = DEFAULT_ITERABLE_CONCRETE_TYPES.get(rty.cls, rty.cls) # noqa
|
74
|
+
return lambda: IterableUnmarshaler(cty, ctx.make(check.single(rty.args))) # noqa
|
66
75
|
|
67
|
-
@
|
68
|
-
def
|
69
|
-
|
76
|
+
@UnmarshalerFactoryMethodClass.make_unmarshaler.register
|
77
|
+
def _make_concrete(self, ctx: UnmarshalContext, rty: rfl.Type) -> ta.Callable[[], Unmarshaler] | None:
|
78
|
+
if not (isinstance(rty, type) and issubclass(rty, collections.abc.Iterable)):
|
79
|
+
return None
|
80
|
+
return lambda: IterableUnmarshaler(check.isinstance(rty, type), ctx.make(ta.Any))
|
@@ -10,10 +10,10 @@ from ... import reflect as rfl
|
|
10
10
|
from ..base.contexts import MarshalContext
|
11
11
|
from ..base.contexts import UnmarshalContext
|
12
12
|
from ..base.types import Marshaler
|
13
|
+
from ..base.types import MarshalerFactory
|
13
14
|
from ..base.types import Unmarshaler
|
15
|
+
from ..base.types import UnmarshalerFactory
|
14
16
|
from ..base.values import Value
|
15
|
-
from ..factories.simple import SimpleMarshalerFactory
|
16
|
-
from ..factories.simple import SimpleUnmarshalerFactory
|
17
17
|
|
18
18
|
|
19
19
|
##
|
@@ -28,14 +28,12 @@ class LiteralMarshaler(Marshaler):
|
|
28
28
|
return self.e.marshal(ctx, check.in_(o, self.vs))
|
29
29
|
|
30
30
|
|
31
|
-
class LiteralMarshalerFactory(
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
ety = check.single(set(map(type, lty.args)))
|
38
|
-
return LiteralMarshaler(ctx.make(ety), frozenset(lty.args))
|
31
|
+
class LiteralMarshalerFactory(MarshalerFactory):
|
32
|
+
def make_marshaler(self, ctx: MarshalContext, rty: rfl.Type) -> ta.Callable[[], Marshaler] | None:
|
33
|
+
if not (isinstance(rty, rfl.Literal) and len(set(map(type, rty.args))) == 1):
|
34
|
+
return None
|
35
|
+
ety = check.single(set(map(type, rty.args)))
|
36
|
+
return lambda: LiteralMarshaler(ctx.make(ety), frozenset(rty.args))
|
39
37
|
|
40
38
|
|
41
39
|
@dc.dataclass(frozen=True)
|
@@ -47,11 +45,9 @@ class LiteralUnmarshaler(Unmarshaler):
|
|
47
45
|
return check.in_(self.e.unmarshal(ctx, v), self.vs)
|
48
46
|
|
49
47
|
|
50
|
-
class LiteralUnmarshalerFactory(
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
ety = check.single(set(map(type, lty.args)))
|
57
|
-
return LiteralUnmarshaler(ctx.make(ety), frozenset(lty.args))
|
48
|
+
class LiteralUnmarshalerFactory(UnmarshalerFactory):
|
49
|
+
def make_unmarshaler(self, ctx: UnmarshalContext, rty: rfl.Type) -> ta.Callable[[], Unmarshaler] | None:
|
50
|
+
if not (isinstance(rty, rfl.Literal) and len(set(map(type, rty.args))) == 1):
|
51
|
+
return None
|
52
|
+
ety = check.single(set(map(type, rty.args)))
|
53
|
+
return lambda: LiteralUnmarshaler(ctx.make(ety), frozenset(rty.args))
|