omlish 0.0.0.dev81__py3-none-any.whl → 0.0.0.dev83__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- omlish/.manifests.json +3 -3
- omlish/__about__.py +2 -2
- omlish/dataclasses/impl/__init__.py +8 -0
- omlish/dataclasses/impl/params.py +3 -0
- omlish/dataclasses/impl/slots.py +61 -7
- omlish/formats/json/__init__.py +8 -1
- omlish/formats/json/backends/__init__.py +7 -0
- omlish/formats/json/backends/base.py +38 -0
- omlish/formats/json/backends/default.py +10 -0
- omlish/formats/json/backends/jiter.py +25 -0
- omlish/formats/json/backends/orjson.py +46 -2
- omlish/formats/json/backends/std.py +39 -0
- omlish/formats/json/backends/ujson.py +49 -0
- omlish/formats/json/cli/__init__.py +0 -0
- omlish/formats/json/{cli.py → cli/cli.py} +50 -48
- omlish/formats/json/cli/formats.py +64 -0
- omlish/formats/json/consts.py +22 -0
- omlish/formats/json/encoding.py +17 -0
- omlish/formats/json/json.py +9 -39
- omlish/formats/json/render.py +49 -28
- omlish/formats/json/stream/__init__.py +0 -0
- omlish/formats/json/stream/build.py +113 -0
- omlish/formats/json/{stream.py → stream/lex.py} +68 -172
- omlish/formats/json/stream/parse.py +244 -0
- omlish/formats/json/stream/render.py +119 -0
- omlish/formats/xml.py +63 -0
- omlish/genmachine.py +14 -2
- omlish/marshal/base.py +2 -0
- omlish/marshal/newtypes.py +24 -0
- omlish/marshal/standard.py +4 -0
- omlish/reflect/__init__.py +1 -0
- omlish/reflect/types.py +6 -1
- {omlish-0.0.0.dev81.dist-info → omlish-0.0.0.dev83.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev81.dist-info → omlish-0.0.0.dev83.dist-info}/RECORD +39 -26
- /omlish/formats/json/{__main__.py → cli/__main__.py} +0 -0
- {omlish-0.0.0.dev81.dist-info → omlish-0.0.0.dev83.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev81.dist-info → omlish-0.0.0.dev83.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev81.dist-info → omlish-0.0.0.dev83.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev81.dist-info → omlish-0.0.0.dev83.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,244 @@
|
|
1
|
+
import typing as ta
|
2
|
+
|
3
|
+
from .... import lang
|
4
|
+
from ....genmachine import GenMachine
|
5
|
+
from .lex import SCALAR_VALUE_TYPES
|
6
|
+
from .lex import VALUE_TOKEN_KINDS
|
7
|
+
from .lex import ScalarValue
|
8
|
+
from .lex import Token
|
9
|
+
|
10
|
+
|
11
|
+
##
|
12
|
+
|
13
|
+
|
14
|
+
class BeginObject(lang.Marker):
|
15
|
+
pass
|
16
|
+
|
17
|
+
|
18
|
+
class Key(ta.NamedTuple):
|
19
|
+
key: str
|
20
|
+
|
21
|
+
|
22
|
+
class EndObject(lang.Marker):
|
23
|
+
pass
|
24
|
+
|
25
|
+
|
26
|
+
class BeginArray(lang.Marker):
|
27
|
+
pass
|
28
|
+
|
29
|
+
|
30
|
+
class EndArray(lang.Marker):
|
31
|
+
pass
|
32
|
+
|
33
|
+
|
34
|
+
JsonStreamParserEvent: ta.TypeAlias = ta.Union[ # noqa
|
35
|
+
type[BeginObject],
|
36
|
+
Key,
|
37
|
+
type[EndObject],
|
38
|
+
|
39
|
+
type[BeginArray],
|
40
|
+
type[EndArray],
|
41
|
+
|
42
|
+
ScalarValue,
|
43
|
+
]
|
44
|
+
|
45
|
+
|
46
|
+
class JsonStreamParserEvents(lang.Namespace):
|
47
|
+
BeginObject = BeginObject
|
48
|
+
Key = Key
|
49
|
+
EndObject = EndObject
|
50
|
+
|
51
|
+
BeginArray = BeginArray
|
52
|
+
EndArray = EndArray
|
53
|
+
|
54
|
+
|
55
|
+
##
|
56
|
+
|
57
|
+
|
58
|
+
def yield_parser_events(obj: ta.Any) -> ta.Generator[JsonStreamParserEvent, None, None]:
|
59
|
+
if isinstance(obj, SCALAR_VALUE_TYPES):
|
60
|
+
yield obj # type: ignore
|
61
|
+
|
62
|
+
elif isinstance(obj, ta.Mapping):
|
63
|
+
yield BeginObject
|
64
|
+
for k, v in obj.items():
|
65
|
+
yield Key(k)
|
66
|
+
yield from yield_parser_events(v)
|
67
|
+
yield EndObject
|
68
|
+
|
69
|
+
elif isinstance(obj, ta.Sequence):
|
70
|
+
yield BeginArray
|
71
|
+
for v in obj:
|
72
|
+
yield from yield_parser_events(v)
|
73
|
+
yield EndArray
|
74
|
+
|
75
|
+
else:
|
76
|
+
raise TypeError(obj)
|
77
|
+
|
78
|
+
|
79
|
+
##
|
80
|
+
|
81
|
+
|
82
|
+
class JsonStreamObject(list):
|
83
|
+
def __repr__(self) -> str:
|
84
|
+
return f'{self.__class__.__name__}({super().__repr__()})'
|
85
|
+
|
86
|
+
|
87
|
+
class JsonStreamParser(GenMachine[Token, JsonStreamParserEvent]):
|
88
|
+
def __init__(self) -> None:
|
89
|
+
super().__init__(self._do_value())
|
90
|
+
|
91
|
+
self._stack: list[ta.Literal['OBJECT', 'KEY', 'ARRAY']] = []
|
92
|
+
|
93
|
+
#
|
94
|
+
|
95
|
+
def _emit_event(self, v):
|
96
|
+
if not self._stack:
|
97
|
+
return ((v,), self._do_value())
|
98
|
+
|
99
|
+
tt = self._stack[-1]
|
100
|
+
if tt == 'KEY':
|
101
|
+
self._stack.pop()
|
102
|
+
if not self._stack:
|
103
|
+
raise self.StateError
|
104
|
+
|
105
|
+
tt2 = self._stack[-1]
|
106
|
+
if tt2 == 'OBJECT':
|
107
|
+
return ((v,), self._do_after_pair())
|
108
|
+
|
109
|
+
else:
|
110
|
+
raise self.StateError
|
111
|
+
|
112
|
+
elif tt == 'ARRAY':
|
113
|
+
return ((v,), self._do_after_element())
|
114
|
+
|
115
|
+
else:
|
116
|
+
raise self.StateError
|
117
|
+
|
118
|
+
#
|
119
|
+
|
120
|
+
def _do_value(self):
|
121
|
+
try:
|
122
|
+
tok = yield None
|
123
|
+
except GeneratorExit:
|
124
|
+
if self._stack:
|
125
|
+
raise self.StateError from None
|
126
|
+
else:
|
127
|
+
raise
|
128
|
+
|
129
|
+
if tok.kind in VALUE_TOKEN_KINDS:
|
130
|
+
y, r = self._emit_event(tok.value)
|
131
|
+
yield y
|
132
|
+
return r
|
133
|
+
|
134
|
+
elif tok.kind == 'LBRACE':
|
135
|
+
y, r = self._emit_begin_object()
|
136
|
+
yield y
|
137
|
+
return r
|
138
|
+
|
139
|
+
elif tok.kind == 'LBRACKET':
|
140
|
+
y, r = self._emit_begin_array()
|
141
|
+
yield y
|
142
|
+
return r
|
143
|
+
|
144
|
+
elif tok.kind == 'RBRACKET':
|
145
|
+
y, r = self._emit_end_array()
|
146
|
+
yield y
|
147
|
+
return r
|
148
|
+
|
149
|
+
else:
|
150
|
+
raise self.StateError
|
151
|
+
|
152
|
+
#
|
153
|
+
|
154
|
+
def _emit_begin_object(self):
|
155
|
+
self._stack.append('OBJECT')
|
156
|
+
return ((BeginObject,), self._do_object_body())
|
157
|
+
|
158
|
+
def _emit_end_object(self):
|
159
|
+
if not self._stack:
|
160
|
+
raise self.StateError
|
161
|
+
|
162
|
+
tt = self._stack.pop()
|
163
|
+
if tt != 'OBJECT':
|
164
|
+
raise self.StateError
|
165
|
+
|
166
|
+
return self._emit_event(EndObject)
|
167
|
+
|
168
|
+
def _do_object_body(self):
|
169
|
+
try:
|
170
|
+
tok = yield None
|
171
|
+
except GeneratorExit:
|
172
|
+
raise self.StateError from None
|
173
|
+
|
174
|
+
if tok.kind == 'STRING':
|
175
|
+
k = tok.value
|
176
|
+
|
177
|
+
try:
|
178
|
+
tok = yield None
|
179
|
+
except GeneratorExit:
|
180
|
+
raise self.StateError from None
|
181
|
+
if tok.kind != 'COLON':
|
182
|
+
raise self.StateError
|
183
|
+
|
184
|
+
yield (Key(k),)
|
185
|
+
self._stack.append('KEY')
|
186
|
+
return self._do_value()
|
187
|
+
|
188
|
+
elif tok.kind == 'RBRACE':
|
189
|
+
y, r = self._emit_end_object()
|
190
|
+
yield y
|
191
|
+
return r
|
192
|
+
|
193
|
+
else:
|
194
|
+
raise self.StateError
|
195
|
+
|
196
|
+
def _do_after_pair(self):
|
197
|
+
try:
|
198
|
+
tok = yield None
|
199
|
+
except GeneratorExit:
|
200
|
+
raise self.StateError from None
|
201
|
+
|
202
|
+
if tok.kind == 'COMMA':
|
203
|
+
return self._do_object_body()
|
204
|
+
|
205
|
+
elif tok.kind == 'RBRACE':
|
206
|
+
y, r = self._emit_end_object()
|
207
|
+
yield y
|
208
|
+
return r
|
209
|
+
|
210
|
+
else:
|
211
|
+
raise self.StateError
|
212
|
+
|
213
|
+
#
|
214
|
+
|
215
|
+
def _emit_begin_array(self):
|
216
|
+
self._stack.append('ARRAY')
|
217
|
+
return ((BeginArray,), self._do_value())
|
218
|
+
|
219
|
+
def _emit_end_array(self):
|
220
|
+
if not self._stack:
|
221
|
+
raise self.StateError
|
222
|
+
|
223
|
+
tt = self._stack.pop()
|
224
|
+
if tt != 'ARRAY':
|
225
|
+
raise self.StateError
|
226
|
+
|
227
|
+
return self._emit_event(EndArray)
|
228
|
+
|
229
|
+
def _do_after_element(self):
|
230
|
+
try:
|
231
|
+
tok = yield None
|
232
|
+
except GeneratorExit:
|
233
|
+
raise self.StateError from None
|
234
|
+
|
235
|
+
if tok.kind == 'COMMA':
|
236
|
+
return self._do_value()
|
237
|
+
|
238
|
+
elif tok.kind == 'RBRACKET':
|
239
|
+
y, r = self._emit_end_array()
|
240
|
+
yield y
|
241
|
+
return r
|
242
|
+
|
243
|
+
else:
|
244
|
+
raise self.StateError
|
@@ -0,0 +1,119 @@
|
|
1
|
+
import json
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from ..render import AbstractJsonRenderer
|
5
|
+
from ..render import JsonRendererOut
|
6
|
+
from .build import JsonObjectBuilder
|
7
|
+
from .parse import BeginArray
|
8
|
+
from .parse import BeginObject
|
9
|
+
from .parse import EndArray
|
10
|
+
from .parse import EndObject
|
11
|
+
from .parse import JsonStreamParserEvent
|
12
|
+
from .parse import Key
|
13
|
+
|
14
|
+
|
15
|
+
##
|
16
|
+
|
17
|
+
|
18
|
+
class StreamJsonRenderer(AbstractJsonRenderer[ta.Iterable[JsonStreamParserEvent]]):
|
19
|
+
def __init__(
|
20
|
+
self,
|
21
|
+
out: JsonRendererOut,
|
22
|
+
opts: AbstractJsonRenderer.Options = AbstractJsonRenderer.Options(),
|
23
|
+
) -> None:
|
24
|
+
if opts.sort_keys:
|
25
|
+
raise TypeError('Not yet implemented')
|
26
|
+
|
27
|
+
super().__init__(out, opts)
|
28
|
+
|
29
|
+
self._stack: list[tuple[ta.Literal['OBJECT', 'ARRAY'], int]] = []
|
30
|
+
self._builder: JsonObjectBuilder | None = None
|
31
|
+
|
32
|
+
def _render_value(
|
33
|
+
self,
|
34
|
+
o: ta.Any,
|
35
|
+
state: AbstractJsonRenderer.State = AbstractJsonRenderer.State.VALUE,
|
36
|
+
) -> None:
|
37
|
+
if self._opts.style is not None:
|
38
|
+
pre, post = self._opts.style(o, state)
|
39
|
+
self._write(pre)
|
40
|
+
else:
|
41
|
+
post = None
|
42
|
+
|
43
|
+
if o is None or isinstance(o, bool):
|
44
|
+
self._write(self._literals[o])
|
45
|
+
|
46
|
+
elif isinstance(o, (str, int, float)):
|
47
|
+
self._write(json.dumps(o))
|
48
|
+
|
49
|
+
else:
|
50
|
+
raise TypeError(o)
|
51
|
+
|
52
|
+
if post:
|
53
|
+
self._write(post)
|
54
|
+
|
55
|
+
def _render(self, e: JsonStreamParserEvent) -> None:
|
56
|
+
if e != EndArray and self._stack and (tt := self._stack[-1])[0] == 'ARRAY':
|
57
|
+
if tt[1]:
|
58
|
+
self._write(self._comma)
|
59
|
+
self._write_indent()
|
60
|
+
|
61
|
+
self._stack[-1] = ('ARRAY', tt[1] + 1)
|
62
|
+
|
63
|
+
#
|
64
|
+
|
65
|
+
if e is None or isinstance(e, (str, int, float, bool)):
|
66
|
+
self._render_value(e)
|
67
|
+
|
68
|
+
#
|
69
|
+
|
70
|
+
elif e is BeginObject:
|
71
|
+
self._stack.append(('OBJECT', 0))
|
72
|
+
self._write('{')
|
73
|
+
self._level += 1
|
74
|
+
|
75
|
+
elif isinstance(e, Key):
|
76
|
+
if not self._stack or (tt := self._stack.pop())[0] != 'OBJECT':
|
77
|
+
raise Exception
|
78
|
+
|
79
|
+
if tt[1]:
|
80
|
+
self._write(self._comma)
|
81
|
+
self._write_indent()
|
82
|
+
self._render_value(e.key, AbstractJsonRenderer.State.KEY)
|
83
|
+
self._write(self._colon)
|
84
|
+
|
85
|
+
self._stack.append(('OBJECT', tt[1] + 1))
|
86
|
+
|
87
|
+
elif e is EndObject:
|
88
|
+
if not self._stack or (tt := self._stack.pop())[0] != 'OBJECT':
|
89
|
+
raise Exception
|
90
|
+
|
91
|
+
self._level -= 1
|
92
|
+
if tt[1]:
|
93
|
+
self._write_indent()
|
94
|
+
self._write('}')
|
95
|
+
|
96
|
+
#
|
97
|
+
|
98
|
+
elif e is BeginArray:
|
99
|
+
self._stack.append(('ARRAY', 0))
|
100
|
+
self._write('[')
|
101
|
+
self._level += 1
|
102
|
+
|
103
|
+
elif e is EndArray:
|
104
|
+
if not self._stack or (tt := self._stack.pop())[0] != 'ARRAY':
|
105
|
+
raise Exception
|
106
|
+
|
107
|
+
self._level -= 1
|
108
|
+
if tt[1]:
|
109
|
+
self._write_indent()
|
110
|
+
self._write(']')
|
111
|
+
|
112
|
+
#
|
113
|
+
|
114
|
+
else:
|
115
|
+
raise TypeError(e)
|
116
|
+
|
117
|
+
def render(self, events: ta.Iterable[JsonStreamParserEvent]) -> None:
|
118
|
+
for e in events:
|
119
|
+
self._render(e)
|
omlish/formats/xml.py
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
"""
|
2
|
+
TODO:
|
3
|
+
- lxml abstraction
|
4
|
+
- stuff from ommlx/wiki
|
5
|
+
"""
|
6
|
+
import typing as ta
|
7
|
+
|
8
|
+
from .. import dataclasses as dc
|
9
|
+
from .. import lang
|
10
|
+
|
11
|
+
|
12
|
+
if ta.TYPE_CHECKING:
|
13
|
+
import xml.etree.ElementTree as ET
|
14
|
+
else:
|
15
|
+
ET = lang.proxy_import('xml.etree.ElementTree')
|
16
|
+
|
17
|
+
|
18
|
+
##
|
19
|
+
|
20
|
+
|
21
|
+
@dc.dataclass(frozen=True)
|
22
|
+
class SimpleElement:
|
23
|
+
tag: str
|
24
|
+
attributes: ta.Mapping[str, str] | None = dc.xfield(default=None, repr_fn=dc.truthy_repr)
|
25
|
+
body: ta.Sequence[ta.Union['SimpleElement', str]] | None = dc.xfield(default=None, repr_fn=dc.truthy_repr)
|
26
|
+
|
27
|
+
def as_dict(self) -> dict[str, ta.Any]:
|
28
|
+
dct: dict[str, ta.Any] = {'tag': self.tag}
|
29
|
+
if self.attributes:
|
30
|
+
dct['attributes'] = self.attributes
|
31
|
+
if self.body:
|
32
|
+
dct['body'] = [
|
33
|
+
c.as_dict() if isinstance(c, SimpleElement) else c
|
34
|
+
for c in self.body
|
35
|
+
]
|
36
|
+
return dct
|
37
|
+
|
38
|
+
|
39
|
+
def build_simple_element(element: 'ET.Element') -> SimpleElement:
|
40
|
+
atts = {}
|
41
|
+
for name, value in element.attrib.items():
|
42
|
+
atts[name] = value # noqa
|
43
|
+
|
44
|
+
body: list[SimpleElement | str] = []
|
45
|
+
|
46
|
+
if element.text and (s := element.text.strip()):
|
47
|
+
body.append(s)
|
48
|
+
|
49
|
+
for child in element:
|
50
|
+
body.append(build_simple_element(child))
|
51
|
+
|
52
|
+
if child.tail and (s := child.tail.strip()):
|
53
|
+
body.append(s)
|
54
|
+
|
55
|
+
return SimpleElement(
|
56
|
+
element.tag,
|
57
|
+
atts,
|
58
|
+
body,
|
59
|
+
)
|
60
|
+
|
61
|
+
|
62
|
+
def parse_tree(s: str) -> 'ET.ElementTree':
|
63
|
+
return ET.ElementTree(ET.fromstring(s.strip())) # noqa
|
omlish/genmachine.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
"""
|
2
2
|
TODO:
|
3
3
|
- feed_iter helper
|
4
|
+
- accept yielding outputs on transitions, *except* on initial state - add test
|
4
5
|
|
5
6
|
See:
|
6
7
|
- https://github.com/pytransitions/transitions
|
@@ -25,10 +26,19 @@ class GenMachine(ta.Generic[I, O]):
|
|
25
26
|
`None` to terminate.
|
26
27
|
"""
|
27
28
|
|
28
|
-
def __init__(self, initial: MachineGen) -> None:
|
29
|
+
def __init__(self, initial: MachineGen | None = None) -> None:
|
29
30
|
super().__init__()
|
31
|
+
|
32
|
+
if initial is None:
|
33
|
+
initial = self._initial_state()
|
34
|
+
if initial is None:
|
35
|
+
raise TypeError('No initial state')
|
36
|
+
|
30
37
|
self._advance(initial)
|
31
38
|
|
39
|
+
def _initial_state(self) -> MachineGen | None:
|
40
|
+
return None
|
41
|
+
|
32
42
|
_gen: MachineGen | None
|
33
43
|
|
34
44
|
def __repr__(self) -> str:
|
@@ -57,7 +67,8 @@ class GenMachine(ta.Generic[I, O]):
|
|
57
67
|
return self
|
58
68
|
|
59
69
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
60
|
-
|
70
|
+
if exc_type is None:
|
71
|
+
self.close()
|
61
72
|
|
62
73
|
#
|
63
74
|
|
@@ -74,6 +85,7 @@ class GenMachine(ta.Generic[I, O]):
|
|
74
85
|
|
75
86
|
def _advance(self, gen: MachineGen) -> None:
|
76
87
|
self._gen = gen
|
88
|
+
|
77
89
|
if (n := next(self._gen)) is not None: # noqa
|
78
90
|
raise GenMachine.ClosedError
|
79
91
|
|
omlish/marshal/base.py
CHANGED
@@ -6,6 +6,8 @@ TODO:
|
|
6
6
|
- streaming? Start/EndObject, etc..
|
7
7
|
- lang.Marker - class name, handle type[Foo]
|
8
8
|
- can't disambiguate from str - can't coexist in bare union
|
9
|
+
- factories being free MatchFns does more harm than good - in practice these are such big guns you want to write a
|
10
|
+
class body if only ceremonially
|
9
11
|
|
10
12
|
See:
|
11
13
|
- https://github.com/python-attrs/cattrs
|
@@ -0,0 +1,24 @@
|
|
1
|
+
from .. import check
|
2
|
+
from .. import reflect as rfl
|
3
|
+
from .base import MarshalContext
|
4
|
+
from .base import Marshaler
|
5
|
+
from .base import MarshalerFactory
|
6
|
+
from .base import UnmarshalContext
|
7
|
+
from .base import Unmarshaler
|
8
|
+
from .base import UnmarshalerFactory
|
9
|
+
|
10
|
+
|
11
|
+
class NewtypeMarshalerFactory(MarshalerFactory):
|
12
|
+
def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
|
13
|
+
return isinstance(rty, rfl.NewType)
|
14
|
+
|
15
|
+
def fn(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
|
16
|
+
return ctx.make(check.isinstance(rty, rfl.NewType).ty)
|
17
|
+
|
18
|
+
|
19
|
+
class NewtypeUnmarshalerFactory(UnmarshalerFactory):
|
20
|
+
def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
|
21
|
+
return isinstance(rty, rfl.NewType)
|
22
|
+
|
23
|
+
def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
|
24
|
+
return ctx.make(check.isinstance(rty, rfl.NewType).ty)
|
omlish/marshal/standard.py
CHANGED
@@ -21,6 +21,8 @@ from .mappings import MappingMarshalerFactory
|
|
21
21
|
from .mappings import MappingUnmarshalerFactory
|
22
22
|
from .maybes import MaybeMarshalerFactory
|
23
23
|
from .maybes import MaybeUnmarshalerFactory
|
24
|
+
from .newtypes import NewtypeMarshalerFactory
|
25
|
+
from .newtypes import NewtypeUnmarshalerFactory
|
24
26
|
from .numbers import NUMBERS_MARSHALER_FACTORY
|
25
27
|
from .numbers import NUMBERS_UNMARSHALER_FACTORY
|
26
28
|
from .optionals import OptionalMarshalerFactory
|
@@ -38,6 +40,7 @@ from .uuids import UUID_UNMARSHALER_FACTORY
|
|
38
40
|
|
39
41
|
STANDARD_MARSHALER_FACTORIES: list[MarshalerFactory] = [
|
40
42
|
PRIMITIVE_MARSHALER_FACTORY,
|
43
|
+
NewtypeMarshalerFactory(),
|
41
44
|
OptionalMarshalerFactory(),
|
42
45
|
PrimitiveUnionMarshalerFactory(),
|
43
46
|
DataclassMarshalerFactory(),
|
@@ -68,6 +71,7 @@ def new_standard_marshaler_factory() -> MarshalerFactory:
|
|
68
71
|
|
69
72
|
STANDARD_UNMARSHALER_FACTORIES: list[UnmarshalerFactory] = [
|
70
73
|
PRIMITIVE_UNMARSHALER_FACTORY,
|
74
|
+
NewtypeUnmarshalerFactory(),
|
71
75
|
OptionalUnmarshalerFactory(),
|
72
76
|
PrimitiveUnionUnmarshalerFactory(),
|
73
77
|
DataclassUnmarshalerFactory(),
|
omlish/reflect/__init__.py
CHANGED
omlish/reflect/types.py
CHANGED
@@ -107,6 +107,10 @@ def get_orig_class(obj: ta.Any) -> ta.Any:
|
|
107
107
|
return obj.__orig_class__ # noqa
|
108
108
|
|
109
109
|
|
110
|
+
def get_newtype_supertype(obj: ta.Any) -> ta.Any:
|
111
|
+
return obj.__supertype__
|
112
|
+
|
113
|
+
|
110
114
|
##
|
111
115
|
|
112
116
|
|
@@ -164,6 +168,7 @@ class Generic:
|
|
164
168
|
@dc.dataclass(frozen=True)
|
165
169
|
class NewType:
|
166
170
|
obj: ta.Any
|
171
|
+
ty: Type
|
167
172
|
|
168
173
|
|
169
174
|
@dc.dataclass(frozen=True)
|
@@ -243,7 +248,7 @@ class Reflector:
|
|
243
248
|
return Union(frozenset(self.type(a) for a in ta.get_args(obj)))
|
244
249
|
|
245
250
|
if isinstance(obj, ta.NewType): # noqa
|
246
|
-
return NewType(obj)
|
251
|
+
return NewType(obj, get_newtype_supertype(obj))
|
247
252
|
|
248
253
|
if (
|
249
254
|
is_simple_generic_alias_type(oty) or
|