omlish 0.0.0.dev6__py3-none-any.whl → 0.0.0.dev7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of omlish might be problematic. Click here for more details.
- omlish/__about__.py +109 -5
- omlish/__init__.py +0 -8
- omlish/asyncs/__init__.py +0 -9
- omlish/asyncs/anyio.py +40 -0
- omlish/bootstrap.py +737 -0
- omlish/check.py +1 -1
- omlish/collections/__init__.py +4 -0
- omlish/collections/exceptions.py +2 -0
- omlish/collections/utils.py +38 -9
- omlish/configs/strings.py +2 -0
- omlish/dataclasses/__init__.py +7 -0
- omlish/dataclasses/impl/descriptors.py +95 -0
- omlish/dataclasses/impl/reflect.py +1 -1
- omlish/dataclasses/utils.py +23 -0
- omlish/{lang/datetimes.py → datetimes.py} +8 -4
- omlish/diag/procfs.py +1 -1
- omlish/diag/threads.py +131 -48
- omlish/docker.py +16 -1
- omlish/fnpairs.py +0 -4
- omlish/{serde → formats}/dotenv.py +3 -0
- omlish/{serde → formats}/yaml.py +2 -2
- omlish/graphs/trees.py +1 -1
- omlish/http/consts.py +6 -0
- omlish/http/sessions.py +2 -2
- omlish/inject/__init__.py +4 -0
- omlish/inject/binder.py +3 -3
- omlish/inject/elements.py +1 -1
- omlish/inject/impl/injector.py +57 -27
- omlish/inject/impl/origins.py +2 -0
- omlish/inject/origins.py +3 -0
- omlish/inject/utils.py +18 -0
- omlish/iterators.py +69 -2
- omlish/lang/__init__.py +16 -7
- omlish/lang/classes/restrict.py +10 -0
- omlish/lang/contextmanagers.py +1 -1
- omlish/lang/descriptors.py +3 -3
- omlish/lang/imports.py +67 -0
- omlish/lang/iterables.py +40 -0
- omlish/lang/maybes.py +3 -0
- omlish/lang/objects.py +38 -0
- omlish/lang/strings.py +25 -0
- omlish/lang/sys.py +9 -0
- omlish/lang/typing.py +37 -0
- omlish/lite/__init__.py +1 -0
- omlish/lite/cached.py +18 -0
- omlish/lite/check.py +29 -0
- omlish/lite/contextmanagers.py +18 -0
- omlish/lite/json.py +30 -0
- omlish/lite/logs.py +52 -0
- omlish/lite/marshal.py +316 -0
- omlish/lite/reflect.py +49 -0
- omlish/lite/runtime.py +18 -0
- omlish/lite/secrets.py +19 -0
- omlish/lite/strings.py +25 -0
- omlish/lite/subprocesses.py +112 -0
- omlish/logs/configs.py +15 -2
- omlish/logs/formatters.py +7 -2
- omlish/marshal/__init__.py +28 -0
- omlish/marshal/any.py +5 -5
- omlish/marshal/base.py +27 -11
- omlish/marshal/base64.py +24 -9
- omlish/marshal/dataclasses.py +34 -28
- omlish/marshal/datetimes.py +74 -18
- omlish/marshal/enums.py +14 -8
- omlish/marshal/exceptions.py +11 -1
- omlish/marshal/factories.py +59 -74
- omlish/marshal/forbidden.py +35 -0
- omlish/marshal/global_.py +11 -4
- omlish/marshal/iterables.py +21 -24
- omlish/marshal/mappings.py +23 -26
- omlish/marshal/numbers.py +51 -0
- omlish/marshal/optionals.py +11 -12
- omlish/marshal/polymorphism.py +86 -21
- omlish/marshal/primitives.py +4 -5
- omlish/marshal/standard.py +13 -8
- omlish/marshal/uuids.py +4 -5
- omlish/matchfns.py +218 -0
- omlish/os.py +64 -0
- omlish/reflect/__init__.py +39 -0
- omlish/reflect/isinstance.py +38 -0
- omlish/reflect/ops.py +84 -0
- omlish/reflect/subst.py +110 -0
- omlish/reflect/types.py +275 -0
- omlish/secrets/__init__.py +18 -2
- omlish/secrets/crypto.py +132 -0
- omlish/secrets/marshal.py +36 -7
- omlish/secrets/openssl.py +207 -0
- omlish/secrets/secrets.py +260 -8
- omlish/secrets/subprocesses.py +42 -0
- omlish/sql/dbs.py +6 -5
- omlish/sql/exprs.py +12 -0
- omlish/sql/secrets.py +10 -0
- omlish/term.py +1 -1
- omlish/testing/pytest/plugins/switches.py +54 -19
- omlish/text/glyphsplit.py +5 -0
- omlish-0.0.0.dev7.dist-info/METADATA +50 -0
- {omlish-0.0.0.dev6.dist-info → omlish-0.0.0.dev7.dist-info}/RECORD +104 -76
- {omlish-0.0.0.dev6.dist-info → omlish-0.0.0.dev7.dist-info}/WHEEL +1 -1
- omlish/reflect.py +0 -470
- omlish-0.0.0.dev6.dist-info/METADATA +0 -34
- /omlish/{asyncs/futures.py → concurrent.py} +0 -0
- /omlish/{serde → formats}/__init__.py +0 -0
- /omlish/{serde → formats}/json.py +0 -0
- /omlish/{serde → formats}/props.py +0 -0
- {omlish-0.0.0.dev6.dist-info → omlish-0.0.0.dev7.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev6.dist-info → omlish-0.0.0.dev7.dist-info}/top_level.txt +0 -0
omlish/marshal/factories.py
CHANGED
|
@@ -1,86 +1,105 @@
|
|
|
1
|
-
import abc
|
|
2
1
|
import dataclasses as dc
|
|
3
|
-
import enum
|
|
4
2
|
import threading
|
|
5
3
|
import typing as ta
|
|
6
4
|
|
|
5
|
+
from .. import check
|
|
6
|
+
from .. import matchfns as mfs
|
|
7
7
|
from .. import reflect as rfl
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
R = ta.TypeVar('R')
|
|
11
11
|
C = ta.TypeVar('C')
|
|
12
|
-
A = ta.TypeVar('A')
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
##
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class Factory(abc.ABC, ta.Generic[R, C, A]):
|
|
19
|
-
@abc.abstractmethod
|
|
20
|
-
def __call__(self, ctx: C, arg: A) -> R | None:
|
|
21
|
-
raise NotImplementedError
|
|
22
12
|
|
|
23
13
|
|
|
24
14
|
##
|
|
25
15
|
|
|
26
16
|
|
|
27
17
|
@dc.dataclass(frozen=True)
|
|
28
|
-
class
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def __call__(self, ctx: C, arg: A) -> R | None:
|
|
32
|
-
return self.fn(ctx, arg)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
##
|
|
18
|
+
class TypeMapFactory(mfs.MatchFn[[C, rfl.Type], R]):
|
|
19
|
+
m: ta.Mapping[rfl.Type, R] = dc.field(default_factory=dict)
|
|
36
20
|
|
|
21
|
+
def __post_init__(self) -> None:
|
|
22
|
+
for k in self.m:
|
|
23
|
+
if not isinstance(k, rfl.TYPES):
|
|
24
|
+
raise TypeError(k)
|
|
37
25
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
26
|
+
def guard(self, ctx: C, rty: rfl.Type) -> bool:
|
|
27
|
+
check.isinstance(rty, rfl.TYPES)
|
|
28
|
+
return rty in self.m
|
|
41
29
|
|
|
42
|
-
def
|
|
43
|
-
|
|
30
|
+
def fn(self, ctx: C, rty: rfl.Type) -> R:
|
|
31
|
+
check.isinstance(rty, rfl.TYPES)
|
|
32
|
+
try:
|
|
33
|
+
return self.m[rty]
|
|
34
|
+
except KeyError:
|
|
35
|
+
raise mfs.MatchGuardError(ctx, rty) # noqa
|
|
44
36
|
|
|
45
37
|
|
|
46
38
|
##
|
|
47
39
|
|
|
48
40
|
|
|
49
|
-
class TypeCacheFactory(
|
|
50
|
-
def __init__(self, f:
|
|
41
|
+
class TypeCacheFactory(mfs.MatchFn[[C, rfl.Type], R]):
|
|
42
|
+
def __init__(self, f: mfs.MatchFn[[C, rfl.Type], R]) -> None:
|
|
51
43
|
super().__init__()
|
|
52
44
|
self._f = f
|
|
53
45
|
self._dct: dict[rfl.Type, R | None] = {}
|
|
54
46
|
self._mtx = threading.RLock()
|
|
55
47
|
|
|
56
|
-
def
|
|
57
|
-
|
|
58
|
-
return self._dct[rty]
|
|
59
|
-
except KeyError:
|
|
60
|
-
pass
|
|
48
|
+
def guard(self, ctx: C, rty: rfl.Type) -> bool:
|
|
49
|
+
check.isinstance(rty, rfl.TYPES)
|
|
61
50
|
with self._mtx:
|
|
62
51
|
try:
|
|
63
|
-
|
|
52
|
+
e = self._dct[rty]
|
|
64
53
|
except KeyError:
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
if self._f.guard(ctx, rty):
|
|
55
|
+
return True
|
|
56
|
+
else:
|
|
57
|
+
self._dct[rty] = None
|
|
58
|
+
return False
|
|
59
|
+
else:
|
|
60
|
+
return e is not None
|
|
61
|
+
|
|
62
|
+
def fn(self, ctx: C, rty: rfl.Type) -> R:
|
|
63
|
+
check.isinstance(rty, rfl.TYPES)
|
|
64
|
+
with self._mtx:
|
|
65
|
+
try:
|
|
66
|
+
e = self._dct[rty]
|
|
67
|
+
except KeyError:
|
|
68
|
+
try:
|
|
69
|
+
ret = self._f(ctx, rty)
|
|
70
|
+
except mfs.MatchGuardError:
|
|
71
|
+
self._dct[rty] = None
|
|
72
|
+
raise
|
|
73
|
+
else:
|
|
74
|
+
self._dct[rty] = ret
|
|
75
|
+
return ret
|
|
76
|
+
else:
|
|
77
|
+
if e is None:
|
|
78
|
+
raise mfs.MatchGuardError(ctx, rty)
|
|
79
|
+
else:
|
|
80
|
+
return e
|
|
67
81
|
|
|
68
82
|
|
|
69
83
|
##
|
|
70
84
|
|
|
71
85
|
|
|
72
|
-
class RecursiveTypeFactory(
|
|
86
|
+
class RecursiveTypeFactory(mfs.MatchFn[[C, rfl.Type], R]):
|
|
73
87
|
def __init__(
|
|
74
88
|
self,
|
|
75
|
-
f:
|
|
76
|
-
prx: ta.Callable[[], tuple[R
|
|
89
|
+
f: mfs.MatchFn[[C, rfl.Type], R],
|
|
90
|
+
prx: ta.Callable[[], tuple[R, ta.Callable[[R], None]]],
|
|
77
91
|
) -> None:
|
|
78
92
|
super().__init__()
|
|
79
93
|
self._f = f
|
|
80
94
|
self._prx = prx
|
|
81
|
-
self._dct: dict[rfl.Type, R
|
|
95
|
+
self._dct: dict[rfl.Type, R] = {}
|
|
96
|
+
|
|
97
|
+
def guard(self, ctx: C, rty: rfl.Type) -> bool:
|
|
98
|
+
check.isinstance(rty, rfl.TYPES)
|
|
99
|
+
return self._f.guard(ctx, rty)
|
|
82
100
|
|
|
83
|
-
def
|
|
101
|
+
def fn(self, ctx: C, rty: rfl.Type) -> R:
|
|
102
|
+
check.isinstance(rty, rfl.TYPES)
|
|
84
103
|
try:
|
|
85
104
|
return self._dct[rty]
|
|
86
105
|
except KeyError:
|
|
@@ -93,37 +112,3 @@ class RecursiveTypeFactory(Factory[R, C, rfl.Type]):
|
|
|
93
112
|
return r
|
|
94
113
|
finally:
|
|
95
114
|
del self._dct[rty]
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
##
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
class CompositeFactory(Factory[R, C, A]):
|
|
102
|
-
class Strategy(enum.Enum):
|
|
103
|
-
FIRST = enum.auto()
|
|
104
|
-
ONE = enum.auto()
|
|
105
|
-
|
|
106
|
-
def __init__(self, *fs: Factory[R, C, A], strategy: Strategy = Strategy.FIRST) -> None:
|
|
107
|
-
super().__init__()
|
|
108
|
-
self._fs = fs
|
|
109
|
-
self._st = strategy
|
|
110
|
-
|
|
111
|
-
def __call__(self, ctx: C, arg: A) -> R | None:
|
|
112
|
-
w: list[R] = []
|
|
113
|
-
for c in self._fs:
|
|
114
|
-
if (r := c(ctx, arg)) is None:
|
|
115
|
-
continue
|
|
116
|
-
if self._st is CompositeFactory.Strategy.FIRST:
|
|
117
|
-
return r
|
|
118
|
-
w.append(r)
|
|
119
|
-
|
|
120
|
-
if not w:
|
|
121
|
-
return None
|
|
122
|
-
|
|
123
|
-
if self._st is CompositeFactory.Strategy.ONE:
|
|
124
|
-
if len(w) == 1:
|
|
125
|
-
return w[0]
|
|
126
|
-
|
|
127
|
-
raise TypeError(f'multiple implementations: {arg} {w}')
|
|
128
|
-
|
|
129
|
-
raise TypeError(f'unknown composite strategy: {self._st}')
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import dataclasses as dc
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from .. import matchfns as mfs
|
|
5
|
+
from .. import reflect as rfl
|
|
6
|
+
from .base import MarshalContext
|
|
7
|
+
from .base import Marshaler
|
|
8
|
+
from .base import UnmarshalContext
|
|
9
|
+
from .base import Unmarshaler
|
|
10
|
+
from .exceptions import ForbiddenTypeError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
C = ta.TypeVar('C')
|
|
14
|
+
R = ta.TypeVar('R')
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dc.dataclass(frozen=True)
|
|
18
|
+
class ForbiddenTypeFactory(mfs.MatchFn[[C, rfl.Type], R]):
|
|
19
|
+
rtys: ta.AbstractSet[rfl.Type]
|
|
20
|
+
|
|
21
|
+
def guard(self, ctx: C, rty: rfl.Type) -> bool:
|
|
22
|
+
return rty in self.rtys
|
|
23
|
+
|
|
24
|
+
def fn(self, ctx: C, rty: rfl.Type) -> R:
|
|
25
|
+
raise ForbiddenTypeError(rty)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dc.dataclass(frozen=True)
|
|
29
|
+
class ForbiddenTypeMarshalerFactory(ForbiddenTypeFactory[MarshalContext, Marshaler]):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dc.dataclass(frozen=True)
|
|
34
|
+
class ForbiddenTypeUnmarshalerFactory(ForbiddenTypeFactory[UnmarshalContext, Unmarshaler]):
|
|
35
|
+
pass
|
omlish/marshal/global_.py
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import typing as ta
|
|
2
2
|
|
|
3
|
+
from .. import lang
|
|
3
4
|
from .base import MarshalContext
|
|
5
|
+
from .base import MarshalerFactory
|
|
4
6
|
from .base import UnmarshalContext
|
|
7
|
+
from .base import UnmarshalerFactory
|
|
5
8
|
from .registries import Registry
|
|
6
9
|
from .standard import new_standard_marshaler_factory
|
|
7
10
|
from .standard import new_standard_unmarshaler_factory
|
|
@@ -20,20 +23,24 @@ GLOBAL_REGISTRY = Registry()
|
|
|
20
23
|
##
|
|
21
24
|
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
@lang.cached_function(lock=True)
|
|
27
|
+
def global_marshaler_factory() -> MarshalerFactory:
|
|
28
|
+
return new_standard_marshaler_factory()
|
|
24
29
|
|
|
25
30
|
|
|
26
31
|
def marshal(obj: ta.Any, ty: type | None = None, **kwargs: ta.Any) -> Value:
|
|
27
|
-
mc = MarshalContext(GLOBAL_REGISTRY, factory=
|
|
32
|
+
mc = MarshalContext(GLOBAL_REGISTRY, factory=global_marshaler_factory(), **kwargs)
|
|
28
33
|
return mc.make(ty if ty is not None else type(obj)).marshal(mc, obj)
|
|
29
34
|
|
|
30
35
|
|
|
31
36
|
##
|
|
32
37
|
|
|
33
38
|
|
|
34
|
-
|
|
39
|
+
@lang.cached_function(lock=True)
|
|
40
|
+
def global_unmarshaler_factory() -> UnmarshalerFactory:
|
|
41
|
+
return new_standard_unmarshaler_factory()
|
|
35
42
|
|
|
36
43
|
|
|
37
44
|
def unmarshal(v: Value, ty: type[T], **kwargs: ta.Any) -> T:
|
|
38
|
-
uc = UnmarshalContext(GLOBAL_REGISTRY, factory=
|
|
45
|
+
uc = UnmarshalContext(GLOBAL_REGISTRY, factory=global_unmarshaler_factory(), **kwargs)
|
|
39
46
|
return uc.make(ty).unmarshal(uc, v)
|
omlish/marshal/iterables.py
CHANGED
|
@@ -4,13 +4,14 @@ import functools
|
|
|
4
4
|
import typing as ta
|
|
5
5
|
|
|
6
6
|
from .. import check
|
|
7
|
+
from .. import matchfns as mfs
|
|
7
8
|
from .. import reflect as rfl
|
|
8
9
|
from .base import MarshalContext
|
|
9
10
|
from .base import Marshaler
|
|
10
|
-
from .base import
|
|
11
|
+
from .base import MarshalerFactoryMatchClass
|
|
11
12
|
from .base import UnmarshalContext
|
|
12
13
|
from .base import Unmarshaler
|
|
13
|
-
from .base import
|
|
14
|
+
from .base import UnmarshalerFactoryMatchClass
|
|
14
15
|
from .values import Value
|
|
15
16
|
|
|
16
17
|
|
|
@@ -22,17 +23,15 @@ class IterableMarshaler(Marshaler):
|
|
|
22
23
|
return list(map(functools.partial(self.e.marshal, ctx), o))
|
|
23
24
|
|
|
24
25
|
|
|
25
|
-
class IterableMarshalerFactory(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return IterableMarshaler(e)
|
|
35
|
-
return None
|
|
26
|
+
class IterableMarshalerFactory(MarshalerFactoryMatchClass):
|
|
27
|
+
@mfs.simple(lambda _, ctx, rty: isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Iterable))
|
|
28
|
+
def _build_generic(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
|
|
29
|
+
gty = check.isinstance(rty, rfl.Generic)
|
|
30
|
+
return IterableMarshaler(ctx.make(check.single(gty.args)))
|
|
31
|
+
|
|
32
|
+
@mfs.simple(lambda _, ctx, rty: isinstance(rty, type) and issubclass(rty, collections.abc.Iterable))
|
|
33
|
+
def _build_concrete(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
|
|
34
|
+
return IterableMarshaler(ctx.make(ta.Any))
|
|
36
35
|
|
|
37
36
|
|
|
38
37
|
@dc.dataclass(frozen=True)
|
|
@@ -44,14 +43,12 @@ class IterableUnmarshaler(Unmarshaler):
|
|
|
44
43
|
return self.ctor(map(functools.partial(self.e.unmarshal, ctx), check.isinstance(v, collections.abc.Iterable)))
|
|
45
44
|
|
|
46
45
|
|
|
47
|
-
class IterableUnmarshalerFactory(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return IterableUnmarshaler(rty, e) # noqa
|
|
57
|
-
return None
|
|
46
|
+
class IterableUnmarshalerFactory(UnmarshalerFactoryMatchClass):
|
|
47
|
+
@mfs.simple(lambda _, ctx, rty: isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Iterable))
|
|
48
|
+
def _build_generic(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
|
|
49
|
+
gty = check.isinstance(rty, rfl.Generic)
|
|
50
|
+
return IterableUnmarshaler(gty.cls, ctx.make(check.single(gty.args)))
|
|
51
|
+
|
|
52
|
+
@mfs.simple(lambda _, ctx, rty: isinstance(rty, type) and issubclass(rty, collections.abc.Iterable))
|
|
53
|
+
def _build_concrete(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
|
|
54
|
+
return IterableUnmarshaler(check.isinstance(rty, type), ctx.make(ta.Any))
|
omlish/marshal/mappings.py
CHANGED
|
@@ -3,13 +3,14 @@ import dataclasses as dc
|
|
|
3
3
|
import typing as ta
|
|
4
4
|
|
|
5
5
|
from .. import check
|
|
6
|
+
from .. import matchfns as mfs
|
|
6
7
|
from .. import reflect as rfl
|
|
7
8
|
from .base import MarshalContext
|
|
8
9
|
from .base import Marshaler
|
|
9
|
-
from .base import
|
|
10
|
+
from .base import MarshalerFactoryMatchClass
|
|
10
11
|
from .base import UnmarshalContext
|
|
11
12
|
from .base import Unmarshaler
|
|
12
|
-
from .base import
|
|
13
|
+
from .base import UnmarshalerFactoryMatchClass
|
|
13
14
|
from .values import Value
|
|
14
15
|
|
|
15
16
|
|
|
@@ -25,18 +26,16 @@ class MappingMarshaler(Marshaler):
|
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
|
|
28
|
-
class MappingMarshalerFactory(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return MappingMarshaler(e, e)
|
|
39
|
-
return None
|
|
29
|
+
class MappingMarshalerFactory(MarshalerFactoryMatchClass):
|
|
30
|
+
@mfs.simple(lambda _, ctx, rty: isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Mapping))
|
|
31
|
+
def _build_generic(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
|
|
32
|
+
gty = check.isinstance(rty, rfl.Generic)
|
|
33
|
+
kt, vt = gty.args
|
|
34
|
+
return MappingMarshaler(ctx.make(kt), ctx.make(vt))
|
|
35
|
+
|
|
36
|
+
@mfs.simple(lambda _, ctx, rty: isinstance(rty, type) and issubclass(rty, collections.abc.Mapping))
|
|
37
|
+
def _build_concrete(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
|
|
38
|
+
return MappingMarshaler(a := ctx.make(ta.Any), a)
|
|
40
39
|
|
|
41
40
|
|
|
42
41
|
@dc.dataclass(frozen=True)
|
|
@@ -52,15 +51,13 @@ class MappingUnmarshaler(Unmarshaler):
|
|
|
52
51
|
return self.ctor(dct)
|
|
53
52
|
|
|
54
53
|
|
|
55
|
-
class MappingUnmarshalerFactory(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return MappingUnmarshaler(rty, e, e)
|
|
66
|
-
return None
|
|
54
|
+
class MappingUnmarshalerFactory(UnmarshalerFactoryMatchClass):
|
|
55
|
+
@mfs.simple(lambda _, ctx, rty: isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Mapping))
|
|
56
|
+
def _build_generic(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
|
|
57
|
+
gty = check.isinstance(rty, rfl.Generic)
|
|
58
|
+
kt, vt = gty.args
|
|
59
|
+
return MappingUnmarshaler(gty.cls, ctx.make(kt), ctx.make(vt))
|
|
60
|
+
|
|
61
|
+
@mfs.simple(lambda _, ctx, rty: isinstance(rty, type) and issubclass(rty, collections.abc.Mapping))
|
|
62
|
+
def _build_concrete(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
|
|
63
|
+
return MappingUnmarshaler(check.isinstance(rty, type), a := ctx.make(ta.Any), a)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import decimal
|
|
2
|
+
import fractions
|
|
3
|
+
import typing as ta
|
|
4
|
+
|
|
5
|
+
from .. import check
|
|
6
|
+
from .base import MarshalContext
|
|
7
|
+
from .base import Marshaler
|
|
8
|
+
from .base import TypeMapMarshalerFactory
|
|
9
|
+
from .base import TypeMapUnmarshalerFactory
|
|
10
|
+
from .base import UnmarshalContext
|
|
11
|
+
from .base import Unmarshaler
|
|
12
|
+
from .values import Value
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ComplexMarshalerUnmarshaler(Marshaler, Unmarshaler):
|
|
16
|
+
def marshal(self, ctx: MarshalContext, o: complex) -> Value:
|
|
17
|
+
return [o.real, o.imag]
|
|
18
|
+
|
|
19
|
+
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any:
|
|
20
|
+
real, imag = check.isinstance(v, list)
|
|
21
|
+
return complex(real, imag) # type: ignore
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DecimalMarshalerUnmarshaler(Marshaler, Unmarshaler):
|
|
25
|
+
def marshal(self, ctx: MarshalContext, o: decimal.Decimal) -> Value:
|
|
26
|
+
return str(o)
|
|
27
|
+
|
|
28
|
+
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any:
|
|
29
|
+
return decimal.Decimal(check.isinstance(v, str))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class FractionMarshalerUnmarshaler(Marshaler, Unmarshaler):
|
|
33
|
+
def marshal(self, ctx: MarshalContext, o: fractions.Fraction) -> Value:
|
|
34
|
+
return [o.numerator, o.denominator]
|
|
35
|
+
|
|
36
|
+
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any:
|
|
37
|
+
num, denom = check.isinstance(v, list)
|
|
38
|
+
return fractions.Fraction(num, denom) # type: ignore
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
NUMBERS_MARSHALER_FACTORY = TypeMapMarshalerFactory({
|
|
42
|
+
complex: ComplexMarshalerUnmarshaler(),
|
|
43
|
+
decimal.Decimal: DecimalMarshalerUnmarshaler(),
|
|
44
|
+
fractions.Fraction: FractionMarshalerUnmarshaler(),
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
NUMBERS_UNMARSHALER_FACTORY = TypeMapUnmarshalerFactory({
|
|
48
|
+
complex: ComplexMarshalerUnmarshaler(),
|
|
49
|
+
decimal.Decimal: DecimalMarshalerUnmarshaler(),
|
|
50
|
+
fractions.Fraction: FractionMarshalerUnmarshaler(),
|
|
51
|
+
})
|
omlish/marshal/optionals.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import dataclasses as dc
|
|
2
2
|
import typing as ta
|
|
3
3
|
|
|
4
|
+
from .. import check
|
|
4
5
|
from .. import reflect as rfl
|
|
5
6
|
from .base import MarshalContext
|
|
6
7
|
from .base import Marshaler
|
|
@@ -22,12 +23,11 @@ class OptionalMarshaler(Marshaler):
|
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
class OptionalMarshalerFactory(MarshalerFactory):
|
|
25
|
-
def
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return None
|
|
26
|
+
def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
|
|
27
|
+
return isinstance(rty, rfl.Union) and rty.is_optional
|
|
28
|
+
|
|
29
|
+
def fn(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
|
|
30
|
+
return OptionalMarshaler(ctx.make(check.isinstance(rty, rfl.Union).without_none()))
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
@dc.dataclass(frozen=True)
|
|
@@ -41,9 +41,8 @@ class OptionalUnmarshaler(Unmarshaler):
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
class OptionalUnmarshalerFactory(UnmarshalerFactory):
|
|
44
|
-
def
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return None
|
|
44
|
+
def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
|
|
45
|
+
return isinstance(rty, rfl.Union) and rty.is_optional
|
|
46
|
+
|
|
47
|
+
def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
|
|
48
|
+
return OptionalUnmarshaler(ctx.make(check.isinstance(rty, rfl.Union).without_none()))
|
omlish/marshal/polymorphism.py
CHANGED
|
@@ -19,12 +19,29 @@ from .base import Unmarshaler
|
|
|
19
19
|
from .base import UnmarshalerFactory
|
|
20
20
|
from .naming import Naming
|
|
21
21
|
from .naming import translate_name
|
|
22
|
+
from .registries import RegistryItem
|
|
22
23
|
from .values import Value
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
##
|
|
26
27
|
|
|
27
28
|
|
|
29
|
+
class TypeTagging(RegistryItem, lang.Abstract, lang.Sealed):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class WrapperTypeTagging(TypeTagging, lang.Final):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dc.dataclass(frozen=True)
|
|
38
|
+
class FieldTypeTagging(TypeTagging, lang.Final):
|
|
39
|
+
field: str
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
|
|
44
|
+
|
|
28
45
|
@dc.dataclass(frozen=True)
|
|
29
46
|
class Impl:
|
|
30
47
|
ty: type
|
|
@@ -33,7 +50,11 @@ class Impl:
|
|
|
33
50
|
|
|
34
51
|
|
|
35
52
|
class Polymorphism:
|
|
36
|
-
def __init__(
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
ty: type,
|
|
56
|
+
impls: ta.Iterable[Impl],
|
|
57
|
+
) -> None:
|
|
37
58
|
super().__init__()
|
|
38
59
|
self._ty = ty
|
|
39
60
|
self._impls = list(impls)
|
|
@@ -72,7 +93,11 @@ class Polymorphism:
|
|
|
72
93
|
return self._by_tag
|
|
73
94
|
|
|
74
95
|
|
|
75
|
-
def polymorphism_from_subclasses(
|
|
96
|
+
def polymorphism_from_subclasses(
|
|
97
|
+
ty: type,
|
|
98
|
+
*,
|
|
99
|
+
naming: Naming | None = None,
|
|
100
|
+
) -> Polymorphism:
|
|
76
101
|
dct: dict[str, Impl] = {}
|
|
77
102
|
seen: set[type] = set()
|
|
78
103
|
todo: list[type] = [ty]
|
|
@@ -97,7 +122,7 @@ def polymorphism_from_subclasses(ty: type, *, naming: Naming | None = None) -> P
|
|
|
97
122
|
|
|
98
123
|
|
|
99
124
|
@dc.dataclass(frozen=True)
|
|
100
|
-
class
|
|
125
|
+
class WrapperPolymorphismMarshaler(Marshaler):
|
|
101
126
|
m: ta.Mapping[type, tuple[str, Marshaler]]
|
|
102
127
|
|
|
103
128
|
def marshal(self, ctx: MarshalContext, o: ta.Any | None) -> Value:
|
|
@@ -105,24 +130,43 @@ class DictKeyPolymorphismMarshaler(Marshaler):
|
|
|
105
130
|
return {tag: m.marshal(ctx, o)}
|
|
106
131
|
|
|
107
132
|
|
|
133
|
+
@dc.dataclass(frozen=True)
|
|
134
|
+
class FieldPolymorphismMarshaler(Marshaler):
|
|
135
|
+
m: ta.Mapping[type, tuple[str, Marshaler]]
|
|
136
|
+
tf: str
|
|
137
|
+
|
|
138
|
+
def marshal(self, ctx: MarshalContext, o: ta.Any | None) -> Value:
|
|
139
|
+
tag, m = self.m[type(o)]
|
|
140
|
+
return {self.tf: tag, **m.marshal(ctx, o)} # type: ignore
|
|
141
|
+
|
|
142
|
+
|
|
108
143
|
@dc.dataclass(frozen=True)
|
|
109
144
|
class PolymorphismMarshalerFactory(MarshalerFactory):
|
|
110
145
|
p: Polymorphism
|
|
146
|
+
tt: TypeTagging = WrapperTypeTagging()
|
|
111
147
|
|
|
112
|
-
def
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
148
|
+
def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
|
|
149
|
+
return rty is self.p.ty
|
|
150
|
+
|
|
151
|
+
def fn(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
|
|
152
|
+
check.is_(rty, self.p.ty)
|
|
153
|
+
m = {
|
|
154
|
+
i.ty: (i.tag, ctx.make(i.ty))
|
|
155
|
+
for i in self.p.impls
|
|
156
|
+
}
|
|
157
|
+
if isinstance(self.tt, WrapperTypeTagging):
|
|
158
|
+
return WrapperPolymorphismMarshaler(m)
|
|
159
|
+
elif isinstance(self.tt, FieldTypeTagging):
|
|
160
|
+
return FieldPolymorphismMarshaler(m, self.tt.field)
|
|
161
|
+
else:
|
|
162
|
+
raise TypeError(self.tt)
|
|
119
163
|
|
|
120
164
|
|
|
121
165
|
##
|
|
122
166
|
|
|
123
167
|
|
|
124
168
|
@dc.dataclass(frozen=True)
|
|
125
|
-
class
|
|
169
|
+
class WrapperPolymorphismUnmarshaler(Unmarshaler):
|
|
126
170
|
m: ta.Mapping[str, Unmarshaler]
|
|
127
171
|
|
|
128
172
|
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any | None:
|
|
@@ -132,16 +176,37 @@ class DictKeyPolymorphismUnmarshaler(Unmarshaler):
|
|
|
132
176
|
return u.unmarshal(ctx, iv) # type: ignore
|
|
133
177
|
|
|
134
178
|
|
|
179
|
+
@dc.dataclass(frozen=True)
|
|
180
|
+
class FieldPolymorphismUnmarshaler(Unmarshaler):
|
|
181
|
+
m: ta.Mapping[str, Unmarshaler]
|
|
182
|
+
tf: str
|
|
183
|
+
|
|
184
|
+
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any | None:
|
|
185
|
+
ma = dict(check.isinstance(v, collections.abc.Mapping))
|
|
186
|
+
tag = ma.pop(self.tf) # type: ignore
|
|
187
|
+
u = self.m[tag] # type: ignore
|
|
188
|
+
return u.unmarshal(ctx, ma)
|
|
189
|
+
|
|
190
|
+
|
|
135
191
|
@dc.dataclass(frozen=True)
|
|
136
192
|
class PolymorphismUnmarshalerFactory(UnmarshalerFactory):
|
|
137
193
|
p: Polymorphism
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
194
|
+
tt: TypeTagging = WrapperTypeTagging()
|
|
195
|
+
|
|
196
|
+
def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
|
|
197
|
+
return rty is self.p.ty
|
|
198
|
+
|
|
199
|
+
def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
|
|
200
|
+
check.is_(rty, self.p.ty)
|
|
201
|
+
m = {
|
|
202
|
+
t: u
|
|
203
|
+
for i in self.p.impls
|
|
204
|
+
for u in [ctx.make(i.ty)]
|
|
205
|
+
for t in [i.tag, *i.alts]
|
|
206
|
+
}
|
|
207
|
+
if isinstance(self.tt, WrapperTypeTagging):
|
|
208
|
+
return WrapperPolymorphismUnmarshaler(m)
|
|
209
|
+
elif isinstance(self.tt, FieldTypeTagging):
|
|
210
|
+
return FieldPolymorphismUnmarshaler(m, self.tt.field)
|
|
211
|
+
else:
|
|
212
|
+
raise TypeError(self.tt)
|