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.
Files changed (39) hide show
  1. omlish/.manifests.json +3 -3
  2. omlish/__about__.py +2 -2
  3. omlish/dataclasses/impl/__init__.py +8 -0
  4. omlish/dataclasses/impl/params.py +3 -0
  5. omlish/dataclasses/impl/slots.py +61 -7
  6. omlish/formats/json/__init__.py +8 -1
  7. omlish/formats/json/backends/__init__.py +7 -0
  8. omlish/formats/json/backends/base.py +38 -0
  9. omlish/formats/json/backends/default.py +10 -0
  10. omlish/formats/json/backends/jiter.py +25 -0
  11. omlish/formats/json/backends/orjson.py +46 -2
  12. omlish/formats/json/backends/std.py +39 -0
  13. omlish/formats/json/backends/ujson.py +49 -0
  14. omlish/formats/json/cli/__init__.py +0 -0
  15. omlish/formats/json/{cli.py → cli/cli.py} +50 -48
  16. omlish/formats/json/cli/formats.py +64 -0
  17. omlish/formats/json/consts.py +22 -0
  18. omlish/formats/json/encoding.py +17 -0
  19. omlish/formats/json/json.py +9 -39
  20. omlish/formats/json/render.py +49 -28
  21. omlish/formats/json/stream/__init__.py +0 -0
  22. omlish/formats/json/stream/build.py +113 -0
  23. omlish/formats/json/{stream.py → stream/lex.py} +68 -172
  24. omlish/formats/json/stream/parse.py +244 -0
  25. omlish/formats/json/stream/render.py +119 -0
  26. omlish/formats/xml.py +63 -0
  27. omlish/genmachine.py +14 -2
  28. omlish/marshal/base.py +2 -0
  29. omlish/marshal/newtypes.py +24 -0
  30. omlish/marshal/standard.py +4 -0
  31. omlish/reflect/__init__.py +1 -0
  32. omlish/reflect/types.py +6 -1
  33. {omlish-0.0.0.dev81.dist-info → omlish-0.0.0.dev83.dist-info}/METADATA +1 -1
  34. {omlish-0.0.0.dev81.dist-info → omlish-0.0.0.dev83.dist-info}/RECORD +39 -26
  35. /omlish/formats/json/{__main__.py → cli/__main__.py} +0 -0
  36. {omlish-0.0.0.dev81.dist-info → omlish-0.0.0.dev83.dist-info}/LICENSE +0 -0
  37. {omlish-0.0.0.dev81.dist-info → omlish-0.0.0.dev83.dist-info}/WHEEL +0 -0
  38. {omlish-0.0.0.dev81.dist-info → omlish-0.0.0.dev83.dist-info}/entry_points.txt +0 -0
  39. {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
- self.close()
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)
@@ -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(),
@@ -33,6 +33,7 @@ from .types import ( # noqa
33
33
  TYPES,
34
34
  Type,
35
35
  Union,
36
+ get_newtype_supertype,
36
37
  get_orig_bases,
37
38
  get_orig_class,
38
39
  get_params,
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev81
3
+ Version: 0.0.0.dev83
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause