omlish 0.0.0.dev5__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 +9 -9
- omlish/asyncs/anyio.py +123 -19
- omlish/asyncs/asyncio.py +23 -0
- omlish/asyncs/asyncs.py +9 -6
- omlish/asyncs/bridge.py +316 -0
- omlish/asyncs/trio_asyncio.py +7 -3
- omlish/bootstrap.py +737 -0
- omlish/check.py +1 -1
- omlish/collections/__init__.py +5 -0
- omlish/collections/exceptions.py +2 -0
- omlish/collections/identity.py +7 -0
- omlish/collections/utils.py +38 -9
- omlish/configs/strings.py +96 -0
- omlish/dataclasses/__init__.py +16 -0
- omlish/dataclasses/impl/copy.py +30 -0
- omlish/dataclasses/impl/descriptors.py +95 -0
- omlish/dataclasses/impl/exceptions.py +6 -0
- omlish/dataclasses/impl/fields.py +24 -25
- omlish/dataclasses/impl/init.py +4 -2
- omlish/dataclasses/impl/main.py +2 -0
- omlish/dataclasses/impl/reflect.py +1 -1
- omlish/dataclasses/utils.py +67 -0
- omlish/{lang/datetimes.py → datetimes.py} +8 -4
- omlish/diag/__init__.py +4 -0
- omlish/diag/procfs.py +2 -2
- omlish/{testing → diag}/pydevd.py +35 -0
- omlish/diag/threads.py +131 -48
- omlish/dispatch/_dispatch2.py +65 -0
- omlish/dispatch/_dispatch3.py +104 -0
- omlish/docker.py +16 -1
- omlish/fnpairs.py +11 -4
- omlish/formats/__init__.py +0 -0
- omlish/{configs → formats}/dotenv.py +15 -24
- omlish/{json.py → formats/json.py} +2 -1
- omlish/formats/yaml.py +223 -0
- omlish/graphs/trees.py +1 -1
- omlish/http/asgi.py +2 -1
- omlish/http/collections.py +15 -0
- omlish/http/consts.py +22 -1
- omlish/http/sessions.py +10 -3
- omlish/inject/__init__.py +49 -17
- omlish/inject/binder.py +185 -5
- omlish/inject/bindings.py +3 -36
- omlish/inject/eagers.py +2 -8
- omlish/inject/elements.py +31 -10
- omlish/inject/exceptions.py +1 -1
- omlish/inject/impl/elements.py +37 -12
- omlish/inject/impl/injector.py +72 -25
- omlish/inject/impl/inspect.py +33 -5
- omlish/inject/impl/origins.py +77 -0
- omlish/inject/impl/{private.py → privates.py} +2 -2
- omlish/inject/impl/scopes.py +6 -2
- omlish/inject/injector.py +8 -4
- omlish/inject/inspect.py +18 -0
- omlish/inject/keys.py +8 -14
- omlish/inject/listeners.py +26 -0
- omlish/inject/managed.py +76 -10
- omlish/inject/multis.py +68 -18
- omlish/inject/origins.py +30 -0
- omlish/inject/overrides.py +5 -4
- omlish/inject/{private.py → privates.py} +6 -10
- omlish/inject/providers.py +12 -85
- omlish/inject/scopes.py +13 -6
- omlish/inject/types.py +3 -1
- omlish/inject/utils.py +18 -0
- omlish/iterators.py +69 -2
- omlish/lang/__init__.py +24 -9
- omlish/lang/cached.py +2 -2
- omlish/lang/classes/restrict.py +12 -1
- omlish/lang/classes/simple.py +18 -8
- omlish/lang/contextmanagers.py +13 -4
- omlish/lang/descriptors.py +132 -1
- omlish/lang/functions.py +8 -28
- omlish/lang/imports.py +67 -0
- omlish/lang/iterables.py +60 -1
- 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 +42 -0
- omlish/lifecycles/__init__.py +34 -0
- omlish/lifecycles/abstract.py +43 -0
- omlish/lifecycles/base.py +51 -0
- omlish/lifecycles/contextmanagers.py +74 -0
- omlish/lifecycles/controller.py +116 -0
- omlish/lifecycles/manager.py +161 -0
- omlish/lifecycles/states.py +43 -0
- omlish/lifecycles/transitions.py +64 -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 +32 -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/naming.py +4 -0
- omlish/marshal/numbers.py +51 -0
- omlish/marshal/objects.py +1 -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 +23 -0
- omlish/secrets/crypto.py +132 -0
- omlish/secrets/marshal.py +70 -0
- omlish/secrets/openssl.py +207 -0
- omlish/secrets/passwords.py +120 -0
- omlish/secrets/secrets.py +299 -0
- omlish/secrets/subprocesses.py +42 -0
- omlish/sql/dbs.py +7 -6
- omlish/sql/duckdb.py +136 -0
- omlish/sql/exprs.py +12 -0
- omlish/sql/secrets.py +10 -0
- omlish/sql/sqlean.py +17 -0
- omlish/term.py +2 -2
- omlish/testing/pytest/__init__.py +3 -2
- omlish/testing/pytest/inject/harness.py +3 -3
- omlish/testing/pytest/marks.py +4 -7
- omlish/testing/pytest/plugins/__init__.py +1 -0
- omlish/testing/pytest/plugins/asyncs.py +136 -0
- omlish/testing/pytest/plugins/pydevd.py +1 -1
- omlish/testing/pytest/plugins/switches.py +54 -19
- omlish/text/glyphsplit.py +97 -0
- omlish-0.0.0.dev7.dist-info/METADATA +50 -0
- omlish-0.0.0.dev7.dist-info/RECORD +268 -0
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev7.dist-info}/WHEEL +1 -1
- omlish/reflect.py +0 -355
- omlish-0.0.0.dev5.dist-info/METADATA +0 -34
- omlish-0.0.0.dev5.dist-info/RECORD +0 -212
- /omlish/{asyncs/futures.py → concurrent.py} +0 -0
- /omlish/{configs → formats}/props.py +0 -0
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev7.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev7.dist-info}/top_level.txt +0 -0
omlish/marshal/base.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
TODO:
|
|
3
3
|
- mappings
|
|
4
4
|
- redacted
|
|
5
|
+
- strongly typed MarshalerFactory base class?
|
|
5
6
|
- strongly typed Composite/Cached Marshaler/Unmarshaler factories - footgun
|
|
6
7
|
- streaming? Start/EndObject, etc..
|
|
7
8
|
|
|
@@ -80,10 +81,12 @@ import typing as ta
|
|
|
80
81
|
from .. import check
|
|
81
82
|
from .. import collections as col
|
|
82
83
|
from .. import lang
|
|
84
|
+
from .. import matchfns as mfs
|
|
83
85
|
from .. import reflect as rfl
|
|
84
86
|
from .exceptions import UnhandledTypeError
|
|
85
|
-
from .factories import Factory
|
|
86
87
|
from .factories import RecursiveTypeFactory
|
|
88
|
+
from .factories import TypeCacheFactory
|
|
89
|
+
from .factories import TypeMapFactory
|
|
87
90
|
from .registries import Registry
|
|
88
91
|
from .registries import RegistryItem
|
|
89
92
|
from .utils import _Proxy
|
|
@@ -105,8 +108,19 @@ class Unmarshaler(lang.Abstract):
|
|
|
105
108
|
raise NotImplementedError
|
|
106
109
|
|
|
107
110
|
|
|
108
|
-
MarshalerFactory =
|
|
109
|
-
UnmarshalerFactory =
|
|
111
|
+
MarshalerFactory: ta.TypeAlias = mfs.MatchFn[['MarshalContext', rfl.Type], Marshaler]
|
|
112
|
+
UnmarshalerFactory: ta.TypeAlias = mfs.MatchFn[['UnmarshalContext', rfl.Type], Unmarshaler]
|
|
113
|
+
|
|
114
|
+
MarshalerFactoryMatchClass: ta.TypeAlias = mfs.MatchFnClass[['MarshalContext', rfl.Type], Marshaler]
|
|
115
|
+
UnmarshalerFactoryMatchClass: ta.TypeAlias = mfs.MatchFnClass[['UnmarshalContext', rfl.Type], Unmarshaler]
|
|
116
|
+
|
|
117
|
+
#
|
|
118
|
+
|
|
119
|
+
TypeMapMarshalerFactory: ta.TypeAlias = TypeMapFactory['MarshalContext', Marshaler]
|
|
120
|
+
TypeMapUnmarshalerFactory: ta.TypeAlias = TypeMapFactory['UnmarshalContext', Unmarshaler]
|
|
121
|
+
|
|
122
|
+
TypeCacheMarshalerFactory: ta.TypeAlias = TypeCacheFactory['MarshalContext', Marshaler]
|
|
123
|
+
TypeCacheUnmarshalerFactory: ta.TypeAlias = TypeCacheFactory['UnmarshalContext', Unmarshaler]
|
|
110
124
|
|
|
111
125
|
|
|
112
126
|
##
|
|
@@ -150,9 +164,10 @@ class MarshalContext(BaseContext, lang.Final):
|
|
|
150
164
|
|
|
151
165
|
def make(self, o: ta.Any) -> Marshaler:
|
|
152
166
|
rty = rfl.type_(o)
|
|
153
|
-
|
|
154
|
-
return
|
|
155
|
-
|
|
167
|
+
try:
|
|
168
|
+
return check.not_none(self.factory)(self, rty) # type: ignore
|
|
169
|
+
except mfs.MatchGuardError:
|
|
170
|
+
raise UnhandledTypeError(rty) # noqa
|
|
156
171
|
|
|
157
172
|
|
|
158
173
|
@dc.dataclass(frozen=True)
|
|
@@ -161,9 +176,10 @@ class UnmarshalContext(BaseContext, lang.Final):
|
|
|
161
176
|
|
|
162
177
|
def make(self, o: ta.Any) -> Unmarshaler:
|
|
163
178
|
rty = rfl.type_(o)
|
|
164
|
-
|
|
165
|
-
return
|
|
166
|
-
|
|
179
|
+
try:
|
|
180
|
+
return check.not_none(self.factory)(self, rty) # type: ignore
|
|
181
|
+
except mfs.MatchGuardError:
|
|
182
|
+
raise UnhandledTypeError(rty) # noqa
|
|
167
183
|
|
|
168
184
|
|
|
169
185
|
##
|
|
@@ -174,7 +190,7 @@ class _ProxyMarshaler(_Proxy[Marshaler], Marshaler):
|
|
|
174
190
|
return self._obj.marshal(ctx, o)
|
|
175
191
|
|
|
176
192
|
|
|
177
|
-
class RecursiveMarshalerFactory(RecursiveTypeFactory[
|
|
193
|
+
class RecursiveMarshalerFactory(RecursiveTypeFactory[MarshalContext, Marshaler], lang.Final):
|
|
178
194
|
def __init__(self, f: MarshalerFactory) -> None:
|
|
179
195
|
super().__init__(f, _ProxyMarshaler._new) # noqa
|
|
180
196
|
|
|
@@ -184,7 +200,7 @@ class _ProxyUnmarshaler(_Proxy[Unmarshaler], Unmarshaler):
|
|
|
184
200
|
return self._obj.unmarshal(ctx, v)
|
|
185
201
|
|
|
186
202
|
|
|
187
|
-
class RecursiveUnmarshalerFactory(RecursiveTypeFactory[
|
|
203
|
+
class RecursiveUnmarshalerFactory(RecursiveTypeFactory[UnmarshalContext, Unmarshaler], lang.Final):
|
|
188
204
|
def __init__(self, f: UnmarshalerFactory) -> None:
|
|
189
205
|
super().__init__(f, _ProxyUnmarshaler._new) # noqa
|
|
190
206
|
|
omlish/marshal/base64.py
CHANGED
|
@@ -1,25 +1,40 @@
|
|
|
1
1
|
import base64
|
|
2
|
+
import typing as ta
|
|
2
3
|
|
|
3
4
|
from .. import check
|
|
5
|
+
from .. import dataclasses as dc
|
|
4
6
|
from .base import MarshalContext
|
|
5
7
|
from .base import Marshaler
|
|
6
|
-
from .base import
|
|
8
|
+
from .base import TypeMapMarshalerFactory
|
|
9
|
+
from .base import TypeMapUnmarshalerFactory
|
|
7
10
|
from .base import UnmarshalContext
|
|
8
11
|
from .base import Unmarshaler
|
|
9
|
-
from .base import UnmarshalerFactory
|
|
10
|
-
from .factories import TypeMapFactory
|
|
11
12
|
from .values import Value
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
T = ta.TypeVar('T')
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dc.dataclass(frozen=True)
|
|
19
|
+
class Base64MarshalerUnmarshaler(Marshaler, Unmarshaler, ta.Generic[T]):
|
|
20
|
+
ty: type[T]
|
|
21
|
+
|
|
15
22
|
def marshal(self, ctx: MarshalContext, o: bytes) -> str:
|
|
16
23
|
return base64.b64encode(o).decode()
|
|
17
24
|
|
|
18
|
-
def unmarshal(self, ctx: UnmarshalContext, v: Value) ->
|
|
19
|
-
return base64.b64decode(check.isinstance(v, str).encode())
|
|
25
|
+
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> T:
|
|
26
|
+
return self.ty(base64.b64decode(check.isinstance(v, str).encode())) # type: ignore
|
|
27
|
+
|
|
20
28
|
|
|
29
|
+
BASE64_TYPES = (
|
|
30
|
+
bytes,
|
|
31
|
+
bytearray,
|
|
32
|
+
)
|
|
21
33
|
|
|
22
|
-
|
|
34
|
+
BASE64_MARSHALER_FACTORY = TypeMapMarshalerFactory({
|
|
35
|
+
ty: Base64MarshalerUnmarshaler(ty) for ty in BASE64_TYPES
|
|
36
|
+
})
|
|
23
37
|
|
|
24
|
-
|
|
25
|
-
|
|
38
|
+
BASE64_UNMARSHALER_FACTORY = TypeMapUnmarshalerFactory({
|
|
39
|
+
ty: Base64MarshalerUnmarshaler(ty) for ty in BASE64_TYPES
|
|
40
|
+
})
|
omlish/marshal/dataclasses.py
CHANGED
|
@@ -79,37 +79,43 @@ def _make_field_obj(ctx, ty, obj, fac):
|
|
|
79
79
|
|
|
80
80
|
|
|
81
81
|
class DataclassMarshalerFactory(MarshalerFactory):
|
|
82
|
-
def
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return
|
|
82
|
+
def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
|
|
83
|
+
return isinstance(rty, type) and dc.is_dataclass(rty)
|
|
84
|
+
|
|
85
|
+
def fn(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
|
|
86
|
+
ty = check.isinstance(rty, type)
|
|
87
|
+
check.state(dc.is_dataclass(ty))
|
|
88
|
+
dc_md = get_dataclass_metadata(ty)
|
|
89
|
+
fields = [
|
|
90
|
+
(fi, _make_field_obj(ctx, fi.type, fi.metadata.marshaler, fi.metadata.marshaler_factory))
|
|
91
|
+
for fi in get_field_infos(ty, ctx.options)
|
|
92
|
+
]
|
|
93
|
+
return ObjectMarshaler(
|
|
94
|
+
fields,
|
|
95
|
+
unknown_field=dc_md.unknown_field,
|
|
96
|
+
)
|
|
94
97
|
|
|
95
98
|
|
|
96
99
|
##
|
|
97
100
|
|
|
98
101
|
|
|
99
102
|
class DataclassUnmarshalerFactory(UnmarshalerFactory):
|
|
100
|
-
def
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
103
|
+
def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
|
|
104
|
+
return isinstance(rty, type) and dc.is_dataclass(rty)
|
|
105
|
+
|
|
106
|
+
def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
|
|
107
|
+
ty = check.isinstance(rty, type)
|
|
108
|
+
check.state(dc.is_dataclass(ty))
|
|
109
|
+
dc_md = get_dataclass_metadata(ty)
|
|
110
|
+
d: dict[str, tuple[FieldInfo, Unmarshaler]] = {}
|
|
111
|
+
for fi in get_field_infos(ty, ctx.options):
|
|
112
|
+
tup = (fi, _make_field_obj(ctx, fi.type, fi.metadata.unmarshaler, fi.metadata.unmarshaler_factory))
|
|
113
|
+
for un in fi.unmarshal_names:
|
|
114
|
+
if un in d:
|
|
115
|
+
raise KeyError(f'Duplicate fields for name {un!r}: {fi.name!r}, {d[un][0].name!r}')
|
|
116
|
+
d[un] = tup
|
|
117
|
+
return ObjectUnmarshaler(
|
|
118
|
+
ty,
|
|
119
|
+
d,
|
|
120
|
+
unknown_field=dc_md.unknown_field,
|
|
121
|
+
)
|
omlish/marshal/datetimes.py
CHANGED
|
@@ -3,16 +3,28 @@ import datetime
|
|
|
3
3
|
import typing as ta
|
|
4
4
|
|
|
5
5
|
from .. import check
|
|
6
|
+
from .. import datetimes as dts
|
|
6
7
|
from .base import MarshalContext
|
|
7
8
|
from .base import Marshaler
|
|
8
|
-
from .base import
|
|
9
|
+
from .base import TypeMapMarshalerFactory
|
|
10
|
+
from .base import TypeMapUnmarshalerFactory
|
|
9
11
|
from .base import UnmarshalContext
|
|
10
12
|
from .base import Unmarshaler
|
|
11
|
-
from .base import UnmarshalerFactory
|
|
12
|
-
from .factories import TypeMapFactory
|
|
13
13
|
from .values import Value
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
DatetimeLikeT = ta.TypeVar('DatetimeLikeT', bound=datetime.datetime | datetime.date | datetime.time)
|
|
17
|
+
|
|
18
|
+
_DATETIME_LIKES = (
|
|
19
|
+
datetime.datetime,
|
|
20
|
+
datetime.date,
|
|
21
|
+
datetime.time,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
##
|
|
26
|
+
|
|
27
|
+
|
|
16
28
|
DATE_FORMATS: ta.Sequence[str] = [
|
|
17
29
|
'%Y-%m-%d',
|
|
18
30
|
]
|
|
@@ -43,48 +55,92 @@ DATETIME_FORMATS: ta.Sequence[str] = [
|
|
|
43
55
|
]
|
|
44
56
|
|
|
45
57
|
|
|
58
|
+
##
|
|
59
|
+
|
|
60
|
+
|
|
46
61
|
@dc.dataclass(frozen=True)
|
|
47
|
-
class DatetimeMarshaler(Marshaler):
|
|
62
|
+
class DatetimeMarshaler(Marshaler, ta.Generic[DatetimeLikeT]):
|
|
63
|
+
cls: type[DatetimeLikeT]
|
|
48
64
|
fmt: str
|
|
49
65
|
|
|
50
|
-
def marshal(self, ctx: MarshalContext, o:
|
|
66
|
+
def marshal(self, ctx: MarshalContext, o: DatetimeLikeT) -> Value:
|
|
51
67
|
return o.strftime(self.fmt)
|
|
52
68
|
|
|
53
69
|
|
|
70
|
+
_ZERO_DATE = datetime.datetime.now().strptime('', '').date() # noqa
|
|
71
|
+
_ZERO_TIME = datetime.time(0)
|
|
72
|
+
|
|
73
|
+
|
|
54
74
|
@dc.dataclass(frozen=True)
|
|
55
|
-
class DatetimeUnmarshaler(Unmarshaler):
|
|
75
|
+
class DatetimeUnmarshaler(Unmarshaler, ta.Generic[DatetimeLikeT]):
|
|
76
|
+
cls: type[DatetimeLikeT]
|
|
56
77
|
fmts: ta.Sequence[str]
|
|
57
78
|
try_iso: bool = False
|
|
58
79
|
|
|
59
|
-
def unmarshal(self, ctx: UnmarshalContext, v: Value) ->
|
|
80
|
+
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> DatetimeLikeT:
|
|
60
81
|
v = check.isinstance(v, str)
|
|
61
82
|
|
|
62
83
|
if self.try_iso:
|
|
63
84
|
try:
|
|
64
|
-
return
|
|
85
|
+
return self.cls.fromisoformat(v) # type: ignore
|
|
65
86
|
except ValueError:
|
|
66
87
|
pass
|
|
67
88
|
|
|
68
89
|
for fmt in self.fmts:
|
|
69
90
|
try:
|
|
70
|
-
|
|
91
|
+
dt = datetime.datetime.strptime(v, fmt) # FIXME: timezone # noqa
|
|
71
92
|
except ValueError:
|
|
72
93
|
pass
|
|
94
|
+
else:
|
|
95
|
+
if self.cls is datetime.datetime:
|
|
96
|
+
return dt # type: ignore
|
|
97
|
+
elif self.cls is datetime.date:
|
|
98
|
+
if dt.time() != _ZERO_TIME:
|
|
99
|
+
raise ValueError(dt)
|
|
100
|
+
return dt.date() # type: ignore
|
|
101
|
+
elif self.cls is datetime.time:
|
|
102
|
+
if dt.date() != _ZERO_DATE:
|
|
103
|
+
raise ValueError(dt)
|
|
104
|
+
return dt.time() # type: ignore
|
|
105
|
+
else:
|
|
106
|
+
raise TypeError(self.cls)
|
|
73
107
|
|
|
74
108
|
raise ValueError(v)
|
|
75
109
|
|
|
76
110
|
|
|
77
|
-
|
|
78
|
-
DATETIME_UNMARSHALER = DatetimeUnmarshaler(DATETIME_FORMATS, try_iso=True)
|
|
79
|
-
|
|
80
|
-
DATETIME_MARSHALER_FACTORY: MarshalerFactory = TypeMapFactory({datetime.datetime: DATETIME_MARSHALER})
|
|
81
|
-
DATETIME_UNMARSHALER_FACTORY: UnmarshalerFactory = TypeMapFactory({datetime.datetime: DATETIME_UNMARSHALER})
|
|
111
|
+
##
|
|
82
112
|
|
|
83
113
|
|
|
84
|
-
class IsoDatetimeMarshalerUnmarshaler(Marshaler, Unmarshaler):
|
|
114
|
+
class IsoDatetimeMarshalerUnmarshaler(Marshaler, Unmarshaler, ta.Generic[DatetimeLikeT]):
|
|
115
|
+
cls: type[DatetimeLikeT]
|
|
85
116
|
|
|
86
|
-
def marshal(self, ctx: MarshalContext, o:
|
|
117
|
+
def marshal(self, ctx: MarshalContext, o: DatetimeLikeT) -> Value:
|
|
87
118
|
return o.isoformat()
|
|
88
119
|
|
|
89
|
-
def unmarshal(self, ctx: UnmarshalContext, v: Value) ->
|
|
90
|
-
return
|
|
120
|
+
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> DatetimeLikeT:
|
|
121
|
+
return self.cls.fromisoformat(v) # type: ignore
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
##
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class TimedeltaMarshalerUnmarshaler(Marshaler, Unmarshaler):
|
|
128
|
+
def marshal(self, ctx: MarshalContext, o: datetime.timedelta) -> Value:
|
|
129
|
+
return str(o)
|
|
130
|
+
|
|
131
|
+
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any:
|
|
132
|
+
return dts.parse_timedelta(check.isinstance(v, str))
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
##
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
DATETIME_MARSHALER_FACTORY = TypeMapMarshalerFactory({
|
|
139
|
+
**{cls: DatetimeMarshaler(cls, DATETIME_FORMATS[0]) for cls in _DATETIME_LIKES},
|
|
140
|
+
datetime.timedelta: TimedeltaMarshalerUnmarshaler(),
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
DATETIME_UNMARSHALER_FACTORY = TypeMapUnmarshalerFactory({
|
|
144
|
+
**{cls: DatetimeUnmarshaler(cls, DATETIME_FORMATS, try_iso=True) for cls in _DATETIME_LIKES},
|
|
145
|
+
datetime.timedelta: TimedeltaMarshalerUnmarshaler(),
|
|
146
|
+
})
|
omlish/marshal/enums.py
CHANGED
|
@@ -22,10 +22,13 @@ class EnumMarshaler(Marshaler):
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class EnumMarshalerFactory(MarshalerFactory):
|
|
25
|
-
def
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
|
|
26
|
+
return isinstance(rty, type) and issubclass(rty, enum.Enum)
|
|
27
|
+
|
|
28
|
+
def fn(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
|
|
29
|
+
ty = check.isinstance(rty, type)
|
|
30
|
+
check.state(issubclass(ty, enum.Enum))
|
|
31
|
+
return EnumMarshaler(ty)
|
|
29
32
|
|
|
30
33
|
|
|
31
34
|
@dc.dataclass(frozen=True)
|
|
@@ -37,7 +40,10 @@ class EnumUnmarshaler(Unmarshaler):
|
|
|
37
40
|
|
|
38
41
|
|
|
39
42
|
class EnumUnmarshalerFactory(UnmarshalerFactory):
|
|
40
|
-
def
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
|
|
44
|
+
return isinstance(rty, type) and issubclass(rty, enum.Enum)
|
|
45
|
+
|
|
46
|
+
def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
|
|
47
|
+
ty = check.isinstance(rty, type)
|
|
48
|
+
check.state(issubclass(ty, enum.Enum))
|
|
49
|
+
return EnumUnmarshaler(ty)
|
omlish/marshal/exceptions.py
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
from .. import reflect as rfl
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
class
|
|
4
|
+
class MarshalError(Exception):
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class UnhandledTypeError(MarshalError):
|
|
9
|
+
@property
|
|
10
|
+
def rty(self) -> rfl.Type:
|
|
11
|
+
return self.args[0]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ForbiddenTypeError(MarshalError):
|
|
5
15
|
@property
|
|
6
16
|
def rty(self) -> rfl.Type:
|
|
7
17
|
return self.args[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)
|