jmux 0.0.5__py3-none-any.whl → 0.0.6__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.
- jmux/decoder.py +20 -4
- jmux/demux.py +99 -57
- jmux/types.py +5 -0
- {jmux-0.0.5.dist-info → jmux-0.0.6.dist-info}/METADATA +1 -1
- jmux-0.0.6.dist-info/RECORD +13 -0
- jmux-0.0.5.dist-info/RECORD +0 -13
- {jmux-0.0.5.dist-info → jmux-0.0.6.dist-info}/WHEEL +0 -0
- {jmux-0.0.5.dist-info → jmux-0.0.6.dist-info}/licenses/LICENSE +0 -0
- {jmux-0.0.5.dist-info → jmux-0.0.6.dist-info}/top_level.txt +0 -0
jmux/decoder.py
CHANGED
|
@@ -32,26 +32,40 @@ class StringEscapeDecoder:
|
|
|
32
32
|
def __init__(self):
|
|
33
33
|
self._buffer = ""
|
|
34
34
|
self._string_escape = False
|
|
35
|
+
self._is_parsing_unicode = False
|
|
36
|
+
self._unicode_buffer = ""
|
|
35
37
|
|
|
36
38
|
def push(self, ch: str) -> str | None:
|
|
39
|
+
if self._is_parsing_unicode:
|
|
40
|
+
self._unicode_buffer += ch
|
|
41
|
+
if len(self._unicode_buffer) == 4:
|
|
42
|
+
code_point = int(self._unicode_buffer, 16)
|
|
43
|
+
decoded_char = chr(code_point)
|
|
44
|
+
self._buffer += decoded_char
|
|
45
|
+
self._is_parsing_unicode = False
|
|
46
|
+
self._unicode_buffer = ""
|
|
47
|
+
return decoded_char
|
|
48
|
+
return None
|
|
49
|
+
|
|
37
50
|
if self._string_escape:
|
|
38
51
|
self._string_escape = False
|
|
39
52
|
if ch == "u":
|
|
40
|
-
self.
|
|
41
|
-
self.
|
|
42
|
-
return
|
|
53
|
+
self._is_parsing_unicode = True
|
|
54
|
+
self._unicode_buffer = ""
|
|
55
|
+
return None
|
|
43
56
|
escaped_char = self.escape_map.get(ch, ch)
|
|
44
57
|
self._buffer += escaped_char
|
|
45
58
|
return escaped_char
|
|
46
59
|
|
|
47
60
|
if ch == "\\":
|
|
48
61
|
self._string_escape = True
|
|
62
|
+
return None
|
|
49
63
|
else:
|
|
50
64
|
self._buffer += ch
|
|
51
65
|
return ch
|
|
52
66
|
|
|
53
67
|
def is_terminating_quote(self, ch: str) -> bool:
|
|
54
|
-
if self._string_escape:
|
|
68
|
+
if self._string_escape or self._is_parsing_unicode:
|
|
55
69
|
return False
|
|
56
70
|
if ch == '"':
|
|
57
71
|
return True
|
|
@@ -60,6 +74,8 @@ class StringEscapeDecoder:
|
|
|
60
74
|
def reset(self) -> None:
|
|
61
75
|
self._buffer = ""
|
|
62
76
|
self._string_escape = False
|
|
77
|
+
self._is_parsing_unicode = False
|
|
78
|
+
self._unicode_buffer = ""
|
|
63
79
|
|
|
64
80
|
@property
|
|
65
81
|
def buffer(self) -> str:
|
jmux/demux.py
CHANGED
|
@@ -47,6 +47,8 @@ from jmux.types import (
|
|
|
47
47
|
BOOLEAN_OPEN,
|
|
48
48
|
COLON,
|
|
49
49
|
COMMA,
|
|
50
|
+
EXPECT_KEY_IN_ROOT,
|
|
51
|
+
EXPECT_VALUE_IN_ARRAY,
|
|
50
52
|
FLOAT_ALLOWED,
|
|
51
53
|
INTERGER_ALLOWED,
|
|
52
54
|
JSON_FALSE,
|
|
@@ -199,60 +201,74 @@ class JMux(ABC):
|
|
|
199
201
|
ForbiddenTypeHintsError: If a type hint is not allowed.
|
|
200
202
|
ObjectMissmatchedError: If the JMux class does not match the Pydantic model.
|
|
201
203
|
"""
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
MaybePydanticType = get_type_hints(pydantic_model).get(attr_name, None)
|
|
208
|
-
if MaybePydanticType is None:
|
|
209
|
-
if NoneType in jmux_subtype_set:
|
|
210
|
-
continue
|
|
211
|
-
else:
|
|
212
|
-
raise MissingAttributeError(
|
|
213
|
-
object_name=pydantic_model.__name__,
|
|
214
|
-
attribute=attr_name,
|
|
215
|
-
)
|
|
204
|
+
try:
|
|
205
|
+
for attr_name, type_alias in get_type_hints(cls).items():
|
|
206
|
+
jmux_main_type_set, jmux_subtype_set = extract_types_from_generic_alias(
|
|
207
|
+
type_alias
|
|
208
|
+
)
|
|
216
209
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
cls._assert_correct_set_combinations(
|
|
227
|
-
jmux_main_type_set,
|
|
228
|
-
pydantic_main_type_set,
|
|
229
|
-
pydantic_subtype_set,
|
|
230
|
-
)
|
|
210
|
+
MaybePydanticType = get_type_hints(pydantic_model).get(attr_name, None)
|
|
211
|
+
if MaybePydanticType is None:
|
|
212
|
+
if NoneType in jmux_subtype_set:
|
|
213
|
+
continue
|
|
214
|
+
else:
|
|
215
|
+
raise MissingAttributeError(
|
|
216
|
+
object_name=pydantic_model.__name__,
|
|
217
|
+
attribute=attr_name,
|
|
218
|
+
)
|
|
231
219
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
jmux_subtype_set,
|
|
235
|
-
pydantic_subtype_set,
|
|
236
|
-
pydantic_main_type_set,
|
|
237
|
-
pydantic_model,
|
|
238
|
-
attr_name,
|
|
220
|
+
pydantic_main_type_set, pydantic_subtype_set = (
|
|
221
|
+
extract_types_from_generic_alias(MaybePydanticType)
|
|
239
222
|
)
|
|
240
|
-
|
|
241
|
-
|
|
223
|
+
cls._assert_only_allowed_types(
|
|
224
|
+
jmux_main_type_set,
|
|
242
225
|
jmux_subtype_set,
|
|
243
|
-
pydantic_subtype_set,
|
|
244
226
|
pydantic_main_type_set,
|
|
245
|
-
|
|
246
|
-
attr_name,
|
|
227
|
+
pydantic_subtype_set,
|
|
247
228
|
)
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
attribute=attr_name,
|
|
253
|
-
message="Unexpected main type on JMux",
|
|
229
|
+
cls._assert_correct_set_combinations(
|
|
230
|
+
jmux_main_type_set,
|
|
231
|
+
pydantic_main_type_set,
|
|
232
|
+
pydantic_subtype_set,
|
|
254
233
|
)
|
|
255
234
|
|
|
235
|
+
if StreamableValues in jmux_main_type_set:
|
|
236
|
+
cls._assert_is_allowed_streamable_values(
|
|
237
|
+
jmux_subtype_set,
|
|
238
|
+
pydantic_subtype_set,
|
|
239
|
+
pydantic_main_type_set,
|
|
240
|
+
pydantic_model,
|
|
241
|
+
attr_name,
|
|
242
|
+
)
|
|
243
|
+
elif AwaitableValue in jmux_main_type_set:
|
|
244
|
+
cls._assert_is_allowed_awaitable_value(
|
|
245
|
+
jmux_subtype_set,
|
|
246
|
+
pydantic_subtype_set,
|
|
247
|
+
pydantic_main_type_set,
|
|
248
|
+
pydantic_model,
|
|
249
|
+
attr_name,
|
|
250
|
+
)
|
|
251
|
+
else:
|
|
252
|
+
raise ObjectMissmatchedError(
|
|
253
|
+
jmux_model=cls.__name__,
|
|
254
|
+
pydantic_model=pydantic_model.__name__,
|
|
255
|
+
attribute=attr_name,
|
|
256
|
+
message="Unexpected main type on JMux",
|
|
257
|
+
)
|
|
258
|
+
except TypeError as e:
|
|
259
|
+
raise ForbiddenTypeHintsError(message=f"Unexpected type hint: {e}") from e
|
|
260
|
+
except MissingAttributeError as e:
|
|
261
|
+
raise e
|
|
262
|
+
except ObjectMissmatchedError as e:
|
|
263
|
+
raise e
|
|
264
|
+
except Exception as e:
|
|
265
|
+
raise ObjectMissmatchedError(
|
|
266
|
+
jmux_model=cls.__name__,
|
|
267
|
+
pydantic_model=pydantic_model.__name__,
|
|
268
|
+
attribute=attr_name,
|
|
269
|
+
message=f"Unexpected main type on JMux: {e}",
|
|
270
|
+
)
|
|
271
|
+
|
|
256
272
|
@classmethod
|
|
257
273
|
def _assert_correct_set_combinations(
|
|
258
274
|
cls,
|
|
@@ -481,18 +497,27 @@ class JMux(ABC):
|
|
|
481
497
|
# CONTEXT: Root
|
|
482
498
|
case M.ROOT:
|
|
483
499
|
match self._pda.state:
|
|
484
|
-
case
|
|
500
|
+
case _ if self._pda.state in EXPECT_KEY_IN_ROOT:
|
|
485
501
|
if ch in JSON_WHITESPACE:
|
|
486
502
|
pass
|
|
487
503
|
elif ch == '"':
|
|
488
504
|
self._pda.set_state(S.PARSING_KEY)
|
|
489
505
|
self._decoder.reset()
|
|
506
|
+
elif ch in OBJECT_CLOSE:
|
|
507
|
+
if self._pda.state is S.EXPECT_KEY_AFTER_COMMA:
|
|
508
|
+
raise UnexpectedCharacterError(
|
|
509
|
+
ch,
|
|
510
|
+
self._pda.stack,
|
|
511
|
+
self._pda.state,
|
|
512
|
+
"Trailing comma in object is not allowed.",
|
|
513
|
+
)
|
|
514
|
+
await self._finalize()
|
|
490
515
|
else:
|
|
491
516
|
raise UnexpectedCharacterError(
|
|
492
517
|
ch,
|
|
493
518
|
self._pda.stack,
|
|
494
519
|
self._pda.state,
|
|
495
|
-
"Char needs to be '\"' or JSON whitespaces",
|
|
520
|
+
"Char needs to be '\"', '}' or JSON whitespaces",
|
|
496
521
|
)
|
|
497
522
|
|
|
498
523
|
case S.PARSING_KEY:
|
|
@@ -537,6 +562,14 @@ class JMux(ABC):
|
|
|
537
562
|
"Expected '[' or '\"' for 'StreamableValues'",
|
|
538
563
|
)
|
|
539
564
|
elif ch in ARRAY_OPEN:
|
|
565
|
+
if self._sink.current_sink_type == SinkType.AWAITABLE_VALUE:
|
|
566
|
+
raise UnexpectedCharacterError(
|
|
567
|
+
ch,
|
|
568
|
+
self._pda.stack,
|
|
569
|
+
self._pda.state,
|
|
570
|
+
"Trying to parse 'array' but sink type is "
|
|
571
|
+
"'AwaitableValue'.",
|
|
572
|
+
)
|
|
540
573
|
self._pda.set_state(S.EXPECT_VALUE)
|
|
541
574
|
self._pda.push(M.ARRAY)
|
|
542
575
|
else:
|
|
@@ -579,12 +612,12 @@ class JMux(ABC):
|
|
|
579
612
|
await self._parse_primitive()
|
|
580
613
|
await self._sink.close()
|
|
581
614
|
self._decoder.reset()
|
|
582
|
-
if ch in
|
|
583
|
-
self._pda.set_state(S.
|
|
584
|
-
|
|
585
|
-
self._pda.set_state(S.EXPECT_KEY)
|
|
586
|
-
if ch in OBJECT_CLOSE:
|
|
615
|
+
if ch in COMMA:
|
|
616
|
+
self._pda.set_state(S.EXPECT_KEY_AFTER_COMMA)
|
|
617
|
+
elif ch in OBJECT_CLOSE:
|
|
587
618
|
await self._finalize()
|
|
619
|
+
else:
|
|
620
|
+
self._pda.set_state(S.EXPECT_COMMA_OR_EOC)
|
|
588
621
|
else:
|
|
589
622
|
self._assert_primitive_character_allowed_in_state(ch)
|
|
590
623
|
self._decoder.push(ch)
|
|
@@ -593,7 +626,7 @@ class JMux(ABC):
|
|
|
593
626
|
if ch in JSON_WHITESPACE:
|
|
594
627
|
pass
|
|
595
628
|
elif ch in COMMA:
|
|
596
|
-
self._pda.set_state(S.
|
|
629
|
+
self._pda.set_state(S.EXPECT_KEY_AFTER_COMMA)
|
|
597
630
|
elif ch in OBJECT_CLOSE:
|
|
598
631
|
await self._finalize()
|
|
599
632
|
else:
|
|
@@ -622,12 +655,19 @@ class JMux(ABC):
|
|
|
622
655
|
)
|
|
623
656
|
|
|
624
657
|
match self._pda.state:
|
|
625
|
-
case
|
|
658
|
+
case _ if self._pda.state in EXPECT_VALUE_IN_ARRAY:
|
|
626
659
|
if ch in JSON_WHITESPACE:
|
|
627
660
|
pass
|
|
628
661
|
elif await self._handle_common__expect_value(ch):
|
|
629
662
|
pass
|
|
630
663
|
elif ch in ARRAY_CLOSE:
|
|
664
|
+
if self._pda.state is S.EXPECT_VALUE_AFTER_COMMA:
|
|
665
|
+
raise UnexpectedCharacterError(
|
|
666
|
+
ch,
|
|
667
|
+
self._pda.stack,
|
|
668
|
+
self._pda.state,
|
|
669
|
+
"Trailing comma in array is not allowed.",
|
|
670
|
+
)
|
|
631
671
|
await self._close_context(S.EXPECT_COMMA_OR_EOC)
|
|
632
672
|
else:
|
|
633
673
|
raise UnexpectedCharacterError(
|
|
@@ -670,9 +710,11 @@ class JMux(ABC):
|
|
|
670
710
|
await self._parse_primitive()
|
|
671
711
|
self._decoder.reset()
|
|
672
712
|
if ch in COMMA:
|
|
673
|
-
self._pda.set_state(S.
|
|
713
|
+
self._pda.set_state(S.EXPECT_VALUE_AFTER_COMMA)
|
|
674
714
|
elif ch in ARRAY_CLOSE:
|
|
675
715
|
await self._close_context(S.EXPECT_COMMA_OR_EOC)
|
|
716
|
+
else:
|
|
717
|
+
self._pda.set_state(S.EXPECT_COMMA_OR_EOC)
|
|
676
718
|
else:
|
|
677
719
|
self._assert_primitive_character_allowed_in_state(ch)
|
|
678
720
|
self._decoder.push(ch)
|
|
@@ -681,7 +723,7 @@ class JMux(ABC):
|
|
|
681
723
|
if ch in JSON_WHITESPACE:
|
|
682
724
|
pass
|
|
683
725
|
elif ch in COMMA:
|
|
684
|
-
self._pda.set_state(S.
|
|
726
|
+
self._pda.set_state(S.EXPECT_VALUE_AFTER_COMMA)
|
|
685
727
|
elif ch in ARRAY_CLOSE:
|
|
686
728
|
await self._close_context(S.EXPECT_COMMA_OR_EOC)
|
|
687
729
|
else:
|
jmux/types.py
CHANGED
|
@@ -9,8 +9,10 @@ class State(Enum):
|
|
|
9
9
|
ERROR = "error"
|
|
10
10
|
# expect
|
|
11
11
|
EXPECT_KEY = "expect_key"
|
|
12
|
+
EXPECT_KEY_AFTER_COMMA = "expect_key_after_comma"
|
|
12
13
|
EXPECT_COLON = "expect_colon"
|
|
13
14
|
EXPECT_VALUE = "expect_value"
|
|
15
|
+
EXPECT_VALUE_AFTER_COMMA = "expect_value_after_comma"
|
|
14
16
|
EXPECT_COMMA_OR_EOC = "expect_comma_or_eoc"
|
|
15
17
|
# parsing
|
|
16
18
|
PARSING_KEY = "parsing_key"
|
|
@@ -29,6 +31,9 @@ PARSING_PRIMITIVE_STATES: Set[State] = {
|
|
|
29
31
|
State.PARSING_NULL,
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
EXPECT_KEY_IN_ROOT = {State.EXPECT_KEY, State.EXPECT_KEY_AFTER_COMMA}
|
|
35
|
+
EXPECT_VALUE_IN_ARRAY = {State.EXPECT_VALUE, State.EXPECT_VALUE_AFTER_COMMA}
|
|
36
|
+
|
|
32
37
|
|
|
33
38
|
class Mode(Enum):
|
|
34
39
|
ROOT = "$"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
jmux/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
jmux/awaitable.py,sha256=otX4vbWzVmIwxvMDWOtaIvS9do8YJtbls9xFjwH9Yw0,8534
|
|
3
|
+
jmux/decoder.py,sha256=YoCkBKposMx1PA78BIVawgFw96QsqTZaOW4gNCSkI7I,2197
|
|
4
|
+
jmux/demux.py,sha256=R2vazLOtfh4zvQKoj3_92RgdqG-zaG3CkQMBz72fAIE,38800
|
|
5
|
+
jmux/error.py,sha256=VZJYivt8RPfjcF2bs-T7_UkH3dVA3xH-xGbZggQV14k,4665
|
|
6
|
+
jmux/helpers.py,sha256=zOlw1Yk7-sdKAeasswRRcuUOTEBAUbymoAGwBTMaOjg,2902
|
|
7
|
+
jmux/pda.py,sha256=19joQd0DD5OAmwRpp2jVVbtiFXnjv5P_1mZm87-QOeY,922
|
|
8
|
+
jmux/types.py,sha256=a5Xyx_8A3gsZkscII1_7E6oyu4VQ-oI4_osTnPLi_xs,1649
|
|
9
|
+
jmux-0.0.6.dist-info/licenses/LICENSE,sha256=y0qnwaAe4bEqzNPyq4M_VZA2I2mQly8MawajyZhqw0k,1169
|
|
10
|
+
jmux-0.0.6.dist-info/METADATA,sha256=cJJfsEjvnpvffxjEgdwbsTQFxB8uqIArJApCYRjTETo,13330
|
|
11
|
+
jmux-0.0.6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
12
|
+
jmux-0.0.6.dist-info/top_level.txt,sha256=TF2N6kHqLghfOkCiNlCueMDX4l5rPn_5MSPNtYrS1-o,5
|
|
13
|
+
jmux-0.0.6.dist-info/RECORD,,
|
jmux-0.0.5.dist-info/RECORD
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
jmux/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
jmux/awaitable.py,sha256=otX4vbWzVmIwxvMDWOtaIvS9do8YJtbls9xFjwH9Yw0,8534
|
|
3
|
-
jmux/decoder.py,sha256=Y6KVryRDLvGV5nBsneXpTvC0WUGhR5Z89Dvqz4HMAgg,1562
|
|
4
|
-
jmux/demux.py,sha256=OuUaNwvKI6WJxdzBzbBgx0yPq1kn4jSttuB5lvwzztk,36422
|
|
5
|
-
jmux/error.py,sha256=VZJYivt8RPfjcF2bs-T7_UkH3dVA3xH-xGbZggQV14k,4665
|
|
6
|
-
jmux/helpers.py,sha256=zOlw1Yk7-sdKAeasswRRcuUOTEBAUbymoAGwBTMaOjg,2902
|
|
7
|
-
jmux/pda.py,sha256=19joQd0DD5OAmwRpp2jVVbtiFXnjv5P_1mZm87-QOeY,922
|
|
8
|
-
jmux/types.py,sha256=CJhFS9RVgR0cDBNJR8ROAFnxzG4YTYpNZ90hyD2SxsY,1389
|
|
9
|
-
jmux-0.0.5.dist-info/licenses/LICENSE,sha256=y0qnwaAe4bEqzNPyq4M_VZA2I2mQly8MawajyZhqw0k,1169
|
|
10
|
-
jmux-0.0.5.dist-info/METADATA,sha256=31D2JB7C5kqeislSLEg3WRkyGXO5cx3mY34SAJd1ntU,13330
|
|
11
|
-
jmux-0.0.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
12
|
-
jmux-0.0.5.dist-info/top_level.txt,sha256=TF2N6kHqLghfOkCiNlCueMDX4l5rPn_5MSPNtYrS1-o,5
|
|
13
|
-
jmux-0.0.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|