jmux 0.0.3__py3-none-any.whl → 0.0.4__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/demux.py CHANGED
@@ -216,17 +216,11 @@ class JMux(ABC):
216
216
  pydantic_main_type_set,
217
217
  pydantic_subtype_set,
218
218
  )
219
- if (
220
- pydantic_wrong := len(pydantic_main_type_set) != 1
221
- and len(pydantic_subtype_set) > 0
222
- ) or len(jmux_main_type_set) != 1:
223
- wrong_obj = "pydantic" if pydantic_wrong else "JMux"
224
- wrong_set = (
225
- jmux_main_type_set if pydantic_wrong else pydantic_main_type_set
226
- )
227
- raise ForbiddenTypeHintsError(
228
- message=(f"Forbidden typing received on {wrong_obj}: {wrong_set}"),
229
- )
219
+ cls._assert_correct_set_combinations(
220
+ jmux_main_type_set,
221
+ pydantic_main_type_set,
222
+ pydantic_subtype_set,
223
+ )
230
224
 
231
225
  if StreamableValues in jmux_main_type_set:
232
226
  cls._assert_is_allowed_streamable_values(
@@ -252,6 +246,80 @@ class JMux(ABC):
252
246
  message="Unexpected main type on JMux",
253
247
  )
254
248
 
249
+ @classmethod
250
+ def _assert_correct_set_combinations(
251
+ cls,
252
+ jmux_main_type_set: Set[Type],
253
+ pydantic_main_type_set: Set[Type],
254
+ pydantic_subtype_set: Set[Type],
255
+ ):
256
+ if (
257
+ pydantic_wrong := (
258
+ len(pydantic_main_type_set) != 1 and list not in pydantic_main_type_set
259
+ )
260
+ and len(pydantic_subtype_set) > 0
261
+ ) or len(jmux_main_type_set) != 1:
262
+ wrong_obj = "pydantic" if pydantic_wrong else "JMux"
263
+ wrong_set = pydantic_main_type_set if pydantic_wrong else jmux_main_type_set
264
+ raise ForbiddenTypeHintsError(
265
+ message=(f"Forbidden typing received on {wrong_obj}: {wrong_set}"),
266
+ )
267
+
268
+ @classmethod
269
+ def _assert_only_allowed_types(
270
+ cls,
271
+ jmux_main_type_set: Set[Type],
272
+ jmux_subtype_set: Set[Type],
273
+ pydantic_main_type_set: Set[Type],
274
+ pydantic_subtype_set: Set[Type],
275
+ ) -> None:
276
+ if not all(t in (AwaitableValue, StreamableValues) for t in jmux_main_type_set):
277
+ raise ForbiddenTypeHintsError(
278
+ message=(
279
+ "JMux must have either AwaitableValue or StreamableValues as "
280
+ f"main type, got {jmux_main_type_set}."
281
+ )
282
+ )
283
+
284
+ if not cls._all_elements_in_set_a_are_subclass_of_an_element_in_set_b(
285
+ set_a=jmux_subtype_set,
286
+ set_b={int, float, str, bool, NoneType, JMux, Enum},
287
+ ):
288
+ raise ForbiddenTypeHintsError(
289
+ message=(
290
+ "JMux sub type must be one of the emittable types, got: "
291
+ f"{jmux_subtype_set}."
292
+ )
293
+ )
294
+
295
+ if not cls._all_elements_in_set_a_are_subclass_of_an_element_in_set_b(
296
+ set_a=pydantic_subtype_set,
297
+ set_b={int, float, str, bool, NoneType, BaseModel, Enum},
298
+ ):
299
+ raise ForbiddenTypeHintsError(
300
+ message=(
301
+ "Pydantic sub type must be one of the primitive, enum or "
302
+ f"BaseModel, got: {pydantic_subtype_set}."
303
+ )
304
+ )
305
+
306
+ if not cls._all_elements_in_set_a_are_subclass_of_an_element_in_set_b(
307
+ set_a=pydantic_main_type_set,
308
+ set_b={int, float, str, bool, list, NoneType, BaseModel, Enum},
309
+ ):
310
+ raise ForbiddenTypeHintsError(
311
+ message=(
312
+ "Pydantic main type must be one of the primitive, enum, list "
313
+ f"or BaseModel, got {pydantic_main_type_set}."
314
+ )
315
+ )
316
+
317
+ @classmethod
318
+ def _all_elements_in_set_a_are_subclass_of_an_element_in_set_b(
319
+ cls, set_a: Set[Type], set_b: Set[Type]
320
+ ) -> bool:
321
+ return all(any(issubclass(elem, t) for t in set_b) for elem in set_a)
322
+
255
323
  @classmethod
256
324
  def _assert_is_allowed_streamable_values(
257
325
  cls,
@@ -329,58 +397,6 @@ class JMux(ABC):
329
397
  ),
330
398
  )
331
399
 
332
- @classmethod
333
- def _assert_only_allowed_types(
334
- cls,
335
- jmux_main_type_set: Set[Type],
336
- jmux_subtype_set: Set[Type],
337
- pydantic_main_type_set: Set[Type],
338
- pydantic_subtype_set: Set[Type],
339
- ) -> None:
340
- if not all(t in (AwaitableValue, StreamableValues) for t in jmux_main_type_set):
341
- raise ForbiddenTypeHintsError(
342
- message=(
343
- "JMux must have either AwaitableValue or StreamableValues as "
344
- f"main type, got {jmux_main_type_set}."
345
- )
346
- )
347
-
348
- if not any(
349
- issubclass(elem, t)
350
- for t in (int, float, str, bool, NoneType, JMux, Enum)
351
- for elem in jmux_subtype_set
352
- ):
353
- raise ForbiddenTypeHintsError(
354
- message=(
355
- "JMux sub type must be one of the emittable types: "
356
- f"{jmux_subtype_set}."
357
- )
358
- )
359
-
360
- if len(pydantic_subtype_set) > 0 and not any(
361
- issubclass(elem, t)
362
- for t in (int, float, str, bool, NoneType, BaseModel, Enum)
363
- for elem in pydantic_subtype_set
364
- ):
365
- raise ForbiddenTypeHintsError(
366
- message=(
367
- "Pydantic sub type must be one of the primitive, enum or "
368
- f"BaseModel, got: {pydantic_subtype_set}."
369
- )
370
- )
371
-
372
- if not any(
373
- issubclass(elem, t)
374
- for t in (int, float, str, bool, list, NoneType, BaseModel, Enum)
375
- for elem in pydantic_main_type_set
376
- ):
377
- raise ForbiddenTypeHintsError(
378
- message=(
379
- "Pydantic main type must be one of the primitive, enum, list "
380
- f"or BaseModel, got {pydantic_main_type_set}."
381
- )
382
- )
383
-
384
400
  async def feed_chunks(self, chunks: str) -> None:
385
401
  """
386
402
  Feeds a string of characters to the JMux parser.
@@ -556,7 +572,10 @@ class JMux(ABC):
556
572
  await self._parse_primitive()
557
573
  await self._sink.close()
558
574
  self._decoder.reset()
559
- self._pda.set_state(S.EXPECT_KEY)
575
+ if ch in JSON_WHITESPACE:
576
+ self._pda.set_state(S.EXPECT_COMMA_OR_EOC)
577
+ else:
578
+ self._pda.set_state(S.EXPECT_KEY)
560
579
  if ch in OBJECT_CLOSE:
561
580
  await self._finalize()
562
581
  else:
jmux/helpers.py CHANGED
@@ -1,7 +1,8 @@
1
- from types import NoneType, UnionType
2
- from typing import Set, Tuple, Type, Union, get_args, get_origin
1
+ from types import NoneType
2
+ from typing import Set, Tuple, Type, get_args, get_origin
3
3
 
4
4
  from jmux.error import ParsePrimitiveError
5
+ from jmux.types import TYPES_LIKE_NONE, TYPES_LIKE_UNION
5
6
 
6
7
 
7
8
  def str_to_bool(s: str) -> bool:
@@ -20,8 +21,17 @@ def extract_types_from_generic_alias(UnknownType: Type) -> Tuple[Set[Type], Set[
20
21
  Origin: Type | None = get_origin(UnknownType)
21
22
  if Origin is None:
22
23
  return {UnknownType}, set()
23
- if Origin is UnionType or Origin is Union:
24
- return deconstruct_type(UnknownType), set()
24
+ if Origin in TYPES_LIKE_UNION:
25
+ deconstructed = deconstruct_flat_type(UnknownType)
26
+ maybe_list_types = [
27
+ subtypes for subtypes in deconstructed if get_origin(subtypes) is list
28
+ ]
29
+ if len(maybe_list_types) == 1:
30
+ list_based_type = maybe_list_types[0]
31
+ non_list_types = deconstructed - {list_based_type}
32
+ main_type, subtype = extract_types_from_generic_alias(list_based_type)
33
+ return non_list_types | main_type, subtype
34
+ return deconstructed, set()
25
35
 
26
36
  type_args = get_args(UnknownType)
27
37
  if len(type_args) != 1:
@@ -31,7 +41,7 @@ def extract_types_from_generic_alias(UnknownType: Type) -> Tuple[Set[Type], Set[
31
41
  )
32
42
 
33
43
  Generic: Type = type_args[0]
34
- type_set = deconstruct_type(Generic)
44
+ type_set = deconstruct_flat_type(Generic)
35
45
  if len(type_set) == 1:
36
46
  return {Origin}, type_set
37
47
  if len(type_set) != 2:
@@ -46,16 +56,20 @@ def extract_types_from_generic_alias(UnknownType: Type) -> Tuple[Set[Type], Set[
46
56
  return {Origin}, type_set
47
57
 
48
58
 
49
- def deconstruct_type(UnknownType: Type) -> Set[Type]:
59
+ def deconstruct_flat_type(UnknownType: Type) -> Set[Type]:
50
60
  Origin: Type | None = get_origin(UnknownType)
51
- if UnknownType is None:
61
+ if UnknownType in TYPES_LIKE_NONE:
52
62
  return {NoneType}
53
63
  if Origin is None:
54
64
  return {UnknownType}
55
- if not (Origin is UnionType or Origin is Union):
56
- return {Origin}
57
- type_args = get_args(UnknownType)
58
- return set(type_args)
65
+ if Origin in TYPES_LIKE_UNION:
66
+ type_args = get_args(UnknownType)
67
+ return set(type_args)
68
+ raise TypeError(
69
+ f"Unknown type {UnknownType} is not a Union or optional type, "
70
+ "only only those types and their syntactic sugar are supported "
71
+ "for flat deconstruction."
72
+ )
59
73
 
60
74
 
61
75
  def get_main_type(type_set: Set[Type]) -> Type:
jmux/types.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from enum import Enum
2
- from typing import Set
2
+ from types import NoneType, UnionType
3
+ from typing import List, Set, Union
3
4
 
4
5
 
5
6
  class State(Enum):
@@ -56,3 +57,7 @@ JSON_FALSE = "false"
56
57
  JSON_TRUE = "true"
57
58
  JSON_NULL = "null"
58
59
  JSON_WHITESPACE = set(" \t\n\r")
60
+
61
+ TYPES_LIKE_UNION = {UnionType, Union}
62
+ TYPES_LIKE_NONE = {NoneType, None}
63
+ TYPES_LIKE_LIST = {List, list}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jmux
3
- Version: 0.0.3
3
+ Version: 0.0.4
4
4
  Summary: JMux: A Python package for demultiplexing a JSON string into multiple awaitable variables.
5
5
  Author-email: "Johannes A.I. Unruh" <johannes@unruh.ai>
6
6
  License: MIT License
@@ -0,0 +1,13 @@
1
+ jmux/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ jmux/awaitable.py,sha256=gceBygIf3fAIWLsN1lWxsz9ExWNasDuk1WaGz8d9FAc,8427
3
+ jmux/decoder.py,sha256=Y6KVryRDLvGV5nBsneXpTvC0WUGhR5Z89Dvqz4HMAgg,1562
4
+ jmux/demux.py,sha256=g9DkMc9sLs31nuVwb7jbGZAjs0KulsKDGfOIM87NuWQ,36317
5
+ jmux/error.py,sha256=VZJYivt8RPfjcF2bs-T7_UkH3dVA3xH-xGbZggQV14k,4665
6
+ jmux/helpers.py,sha256=zOlw1Yk7-sdKAeasswRRcuUOTEBAUbymoAGwBTMaOjg,2902
7
+ jmux/pda.py,sha256=81gnh0eWGsgd_SrHkqjRQy_KkOSlBf5nor7pqKGgYjw,791
8
+ jmux/types.py,sha256=CJhFS9RVgR0cDBNJR8ROAFnxzG4YTYpNZ90hyD2SxsY,1389
9
+ jmux-0.0.4.dist-info/licenses/LICENSE,sha256=y0qnwaAe4bEqzNPyq4M_VZA2I2mQly8MawajyZhqw0k,1169
10
+ jmux-0.0.4.dist-info/METADATA,sha256=vVREVDZvfTC0KadGCOrw7RrSGxXdmZZNHJ8K-Zr3ZLM,13330
11
+ jmux-0.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
12
+ jmux-0.0.4.dist-info/top_level.txt,sha256=TF2N6kHqLghfOkCiNlCueMDX4l5rPn_5MSPNtYrS1-o,5
13
+ jmux-0.0.4.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- jmux/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- jmux/awaitable.py,sha256=gceBygIf3fAIWLsN1lWxsz9ExWNasDuk1WaGz8d9FAc,8427
3
- jmux/decoder.py,sha256=Y6KVryRDLvGV5nBsneXpTvC0WUGhR5Z89Dvqz4HMAgg,1562
4
- jmux/demux.py,sha256=0dKIQibyDQkijuIcT53Za1L2DZbC_Ll0_1mhroqHg9I,35516
5
- jmux/error.py,sha256=VZJYivt8RPfjcF2bs-T7_UkH3dVA3xH-xGbZggQV14k,4665
6
- jmux/helpers.py,sha256=DQyPeuwx3AR65aHrET7xpSbfTLTC7ji3CyicyEhqJpk,2226
7
- jmux/pda.py,sha256=81gnh0eWGsgd_SrHkqjRQy_KkOSlBf5nor7pqKGgYjw,791
8
- jmux/types.py,sha256=5Ox01s-rGMQ1xGX8NRNBlyWtxvGKwnt9bjexXdESSYc,1233
9
- jmux-0.0.3.dist-info/licenses/LICENSE,sha256=y0qnwaAe4bEqzNPyq4M_VZA2I2mQly8MawajyZhqw0k,1169
10
- jmux-0.0.3.dist-info/METADATA,sha256=ZSi9Y5l5FsyaJuTW3N2K199mCNfkMe8_FLmxjIEfSAA,13330
11
- jmux-0.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
12
- jmux-0.0.3.dist-info/top_level.txt,sha256=TF2N6kHqLghfOkCiNlCueMDX4l5rPn_5MSPNtYrS1-o,5
13
- jmux-0.0.3.dist-info/RECORD,,
File without changes