omlish 0.0.0.dev81__py3-none-any.whl → 0.0.0.dev83__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.
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