omlish 0.0.0.dev6__py3-none-any.whl → 0.0.0.dev8__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 (108) hide show
  1. omlish/__about__.py +109 -5
  2. omlish/__init__.py +0 -8
  3. omlish/asyncs/__init__.py +0 -9
  4. omlish/asyncs/anyio.py +40 -0
  5. omlish/bootstrap.py +737 -0
  6. omlish/check.py +1 -1
  7. omlish/collections/__init__.py +4 -0
  8. omlish/collections/exceptions.py +2 -0
  9. omlish/collections/utils.py +38 -9
  10. omlish/configs/strings.py +2 -0
  11. omlish/dataclasses/__init__.py +7 -0
  12. omlish/dataclasses/impl/descriptors.py +95 -0
  13. omlish/dataclasses/impl/reflect.py +1 -1
  14. omlish/dataclasses/utils.py +23 -0
  15. omlish/{lang/datetimes.py → datetimes.py} +8 -4
  16. omlish/diag/procfs.py +1 -1
  17. omlish/diag/threads.py +131 -48
  18. omlish/docker.py +16 -1
  19. omlish/fnpairs.py +0 -4
  20. omlish/{serde → formats}/dotenv.py +3 -0
  21. omlish/{serde → formats}/yaml.py +2 -2
  22. omlish/graphs/trees.py +1 -1
  23. omlish/http/consts.py +6 -0
  24. omlish/http/sessions.py +2 -2
  25. omlish/inject/__init__.py +4 -0
  26. omlish/inject/binder.py +3 -3
  27. omlish/inject/elements.py +1 -1
  28. omlish/inject/impl/injector.py +57 -27
  29. omlish/inject/impl/origins.py +2 -0
  30. omlish/inject/origins.py +3 -0
  31. omlish/inject/utils.py +18 -0
  32. omlish/iterators.py +69 -2
  33. omlish/lang/__init__.py +16 -7
  34. omlish/lang/classes/restrict.py +10 -0
  35. omlish/lang/contextmanagers.py +1 -1
  36. omlish/lang/descriptors.py +3 -3
  37. omlish/lang/imports.py +67 -0
  38. omlish/lang/iterables.py +40 -0
  39. omlish/lang/maybes.py +3 -0
  40. omlish/lang/objects.py +38 -0
  41. omlish/lang/strings.py +25 -0
  42. omlish/lang/sys.py +9 -0
  43. omlish/lang/typing.py +37 -0
  44. omlish/lite/__init__.py +1 -0
  45. omlish/lite/cached.py +18 -0
  46. omlish/lite/check.py +29 -0
  47. omlish/lite/contextmanagers.py +18 -0
  48. omlish/lite/json.py +30 -0
  49. omlish/lite/logs.py +121 -0
  50. omlish/lite/marshal.py +318 -0
  51. omlish/lite/reflect.py +49 -0
  52. omlish/lite/runtime.py +18 -0
  53. omlish/lite/secrets.py +19 -0
  54. omlish/lite/strings.py +25 -0
  55. omlish/lite/subprocesses.py +112 -0
  56. omlish/logs/__init__.py +13 -9
  57. omlish/logs/configs.py +17 -22
  58. omlish/logs/formatters.py +3 -48
  59. omlish/marshal/__init__.py +28 -0
  60. omlish/marshal/any.py +5 -5
  61. omlish/marshal/base.py +27 -11
  62. omlish/marshal/base64.py +24 -9
  63. omlish/marshal/dataclasses.py +34 -28
  64. omlish/marshal/datetimes.py +74 -18
  65. omlish/marshal/enums.py +14 -8
  66. omlish/marshal/exceptions.py +11 -1
  67. omlish/marshal/factories.py +59 -74
  68. omlish/marshal/forbidden.py +35 -0
  69. omlish/marshal/global_.py +11 -4
  70. omlish/marshal/iterables.py +21 -24
  71. omlish/marshal/mappings.py +23 -26
  72. omlish/marshal/numbers.py +51 -0
  73. omlish/marshal/optionals.py +11 -12
  74. omlish/marshal/polymorphism.py +86 -21
  75. omlish/marshal/primitives.py +4 -5
  76. omlish/marshal/standard.py +13 -8
  77. omlish/marshal/uuids.py +4 -5
  78. omlish/matchfns.py +218 -0
  79. omlish/os.py +64 -0
  80. omlish/reflect/__init__.py +39 -0
  81. omlish/reflect/isinstance.py +38 -0
  82. omlish/reflect/ops.py +84 -0
  83. omlish/reflect/subst.py +110 -0
  84. omlish/reflect/types.py +275 -0
  85. omlish/secrets/__init__.py +18 -2
  86. omlish/secrets/crypto.py +132 -0
  87. omlish/secrets/marshal.py +36 -7
  88. omlish/secrets/openssl.py +207 -0
  89. omlish/secrets/secrets.py +260 -8
  90. omlish/secrets/subprocesses.py +42 -0
  91. omlish/sql/dbs.py +6 -5
  92. omlish/sql/exprs.py +12 -0
  93. omlish/sql/secrets.py +10 -0
  94. omlish/term.py +1 -1
  95. omlish/testing/pytest/plugins/switches.py +54 -19
  96. omlish/text/glyphsplit.py +5 -0
  97. omlish-0.0.0.dev8.dist-info/METADATA +50 -0
  98. {omlish-0.0.0.dev6.dist-info → omlish-0.0.0.dev8.dist-info}/RECORD +105 -78
  99. {omlish-0.0.0.dev6.dist-info → omlish-0.0.0.dev8.dist-info}/WHEEL +1 -1
  100. omlish/logs/filters.py +0 -11
  101. omlish/reflect.py +0 -470
  102. omlish-0.0.0.dev6.dist-info/METADATA +0 -34
  103. /omlish/{asyncs/futures.py → concurrent.py} +0 -0
  104. /omlish/{serde → formats}/__init__.py +0 -0
  105. /omlish/{serde → formats}/json.py +0 -0
  106. /omlish/{serde → formats}/props.py +0 -0
  107. {omlish-0.0.0.dev6.dist-info → omlish-0.0.0.dev8.dist-info}/LICENSE +0 -0
  108. {omlish-0.0.0.dev6.dist-info → omlish-0.0.0.dev8.dist-info}/top_level.txt +0 -0
omlish/marshal/enums.py CHANGED
@@ -22,10 +22,13 @@ class EnumMarshaler(Marshaler):
22
22
 
23
23
 
24
24
  class EnumMarshalerFactory(MarshalerFactory):
25
- def __call__(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler | None:
26
- if isinstance(rty, type) and issubclass(rty, enum.Enum):
27
- return EnumMarshaler(rty)
28
- return None
25
+ def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
26
+ return isinstance(rty, type) and issubclass(rty, enum.Enum)
27
+
28
+ def fn(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
29
+ ty = check.isinstance(rty, type)
30
+ check.state(issubclass(ty, enum.Enum))
31
+ return EnumMarshaler(ty)
29
32
 
30
33
 
31
34
  @dc.dataclass(frozen=True)
@@ -37,7 +40,10 @@ class EnumUnmarshaler(Unmarshaler):
37
40
 
38
41
 
39
42
  class EnumUnmarshalerFactory(UnmarshalerFactory):
40
- def __call__(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler | None:
41
- if isinstance(rty, type) and issubclass(rty, enum.Enum):
42
- return EnumUnmarshaler(rty)
43
- return None
43
+ def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
44
+ return isinstance(rty, type) and issubclass(rty, enum.Enum)
45
+
46
+ def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
47
+ ty = check.isinstance(rty, type)
48
+ check.state(issubclass(ty, enum.Enum))
49
+ return EnumUnmarshaler(ty)
@@ -1,7 +1,17 @@
1
1
  from .. import reflect as rfl
2
2
 
3
3
 
4
- class UnhandledTypeError(Exception):
4
+ class MarshalError(Exception):
5
+ pass
6
+
7
+
8
+ class UnhandledTypeError(MarshalError):
9
+ @property
10
+ def rty(self) -> rfl.Type:
11
+ return self.args[0]
12
+
13
+
14
+ class ForbiddenTypeError(MarshalError):
5
15
  @property
6
16
  def rty(self) -> rfl.Type:
7
17
  return self.args[0]
@@ -1,86 +1,105 @@
1
- import abc
2
1
  import dataclasses as dc
3
- import enum
4
2
  import threading
5
3
  import typing as ta
6
4
 
5
+ from .. import check
6
+ from .. import matchfns as mfs
7
7
  from .. import reflect as rfl
8
8
 
9
9
 
10
10
  R = ta.TypeVar('R')
11
11
  C = ta.TypeVar('C')
12
- A = ta.TypeVar('A')
13
-
14
-
15
- ##
16
-
17
-
18
- class Factory(abc.ABC, ta.Generic[R, C, A]):
19
- @abc.abstractmethod
20
- def __call__(self, ctx: C, arg: A) -> R | None:
21
- raise NotImplementedError
22
12
 
23
13
 
24
14
  ##
25
15
 
26
16
 
27
17
  @dc.dataclass(frozen=True)
28
- class FuncFactory(ta.Generic[R, C, A]):
29
- fn: ta.Callable[[C, A], R | None]
30
-
31
- def __call__(self, ctx: C, arg: A) -> R | None:
32
- return self.fn(ctx, arg)
33
-
34
-
35
- ##
18
+ class TypeMapFactory(mfs.MatchFn[[C, rfl.Type], R]):
19
+ m: ta.Mapping[rfl.Type, R] = dc.field(default_factory=dict)
36
20
 
21
+ def __post_init__(self) -> None:
22
+ for k in self.m:
23
+ if not isinstance(k, rfl.TYPES):
24
+ raise TypeError(k)
37
25
 
38
- @dc.dataclass(frozen=True)
39
- class TypeMapFactory(Factory[R, C, rfl.Type]):
40
- m: ta.Mapping[rfl.Type, R] = dc.field(default_factory=dict)
26
+ def guard(self, ctx: C, rty: rfl.Type) -> bool:
27
+ check.isinstance(rty, rfl.TYPES)
28
+ return rty in self.m
41
29
 
42
- def __call__(self, ctx: C, rty: rfl.Type) -> R | None:
43
- return self.m.get(rty)
30
+ def fn(self, ctx: C, rty: rfl.Type) -> R:
31
+ check.isinstance(rty, rfl.TYPES)
32
+ try:
33
+ return self.m[rty]
34
+ except KeyError:
35
+ raise mfs.MatchGuardError(ctx, rty) # noqa
44
36
 
45
37
 
46
38
  ##
47
39
 
48
40
 
49
- class TypeCacheFactory(Factory[R, C, rfl.Type]):
50
- def __init__(self, f: Factory[R, C, rfl.Type]) -> None:
41
+ class TypeCacheFactory(mfs.MatchFn[[C, rfl.Type], R]):
42
+ def __init__(self, f: mfs.MatchFn[[C, rfl.Type], R]) -> None:
51
43
  super().__init__()
52
44
  self._f = f
53
45
  self._dct: dict[rfl.Type, R | None] = {}
54
46
  self._mtx = threading.RLock()
55
47
 
56
- def __call__(self, ctx: C, rty: rfl.Type) -> R | None:
57
- try:
58
- return self._dct[rty]
59
- except KeyError:
60
- pass
48
+ def guard(self, ctx: C, rty: rfl.Type) -> bool:
49
+ check.isinstance(rty, rfl.TYPES)
61
50
  with self._mtx:
62
51
  try:
63
- return self._dct[rty]
52
+ e = self._dct[rty]
64
53
  except KeyError:
65
- ret = self._dct[rty] = self._f(ctx, rty)
66
- return ret
54
+ if self._f.guard(ctx, rty):
55
+ return True
56
+ else:
57
+ self._dct[rty] = None
58
+ return False
59
+ else:
60
+ return e is not None
61
+
62
+ def fn(self, ctx: C, rty: rfl.Type) -> R:
63
+ check.isinstance(rty, rfl.TYPES)
64
+ with self._mtx:
65
+ try:
66
+ e = self._dct[rty]
67
+ except KeyError:
68
+ try:
69
+ ret = self._f(ctx, rty)
70
+ except mfs.MatchGuardError:
71
+ self._dct[rty] = None
72
+ raise
73
+ else:
74
+ self._dct[rty] = ret
75
+ return ret
76
+ else:
77
+ if e is None:
78
+ raise mfs.MatchGuardError(ctx, rty)
79
+ else:
80
+ return e
67
81
 
68
82
 
69
83
  ##
70
84
 
71
85
 
72
- class RecursiveTypeFactory(Factory[R, C, rfl.Type]):
86
+ class RecursiveTypeFactory(mfs.MatchFn[[C, rfl.Type], R]):
73
87
  def __init__(
74
88
  self,
75
- f: Factory[R, C, rfl.Type],
76
- prx: ta.Callable[[], tuple[R | None, ta.Callable[[R | None], None]]],
89
+ f: mfs.MatchFn[[C, rfl.Type], R],
90
+ prx: ta.Callable[[], tuple[R, ta.Callable[[R], None]]],
77
91
  ) -> None:
78
92
  super().__init__()
79
93
  self._f = f
80
94
  self._prx = prx
81
- self._dct: dict[rfl.Type, R | None] = {}
95
+ self._dct: dict[rfl.Type, R] = {}
96
+
97
+ def guard(self, ctx: C, rty: rfl.Type) -> bool:
98
+ check.isinstance(rty, rfl.TYPES)
99
+ return self._f.guard(ctx, rty)
82
100
 
83
- def __call__(self, ctx: C, rty: rfl.Type) -> R | None:
101
+ def fn(self, ctx: C, rty: rfl.Type) -> R:
102
+ check.isinstance(rty, rfl.TYPES)
84
103
  try:
85
104
  return self._dct[rty]
86
105
  except KeyError:
@@ -93,37 +112,3 @@ class RecursiveTypeFactory(Factory[R, C, rfl.Type]):
93
112
  return r
94
113
  finally:
95
114
  del self._dct[rty]
96
-
97
-
98
- ##
99
-
100
-
101
- class CompositeFactory(Factory[R, C, A]):
102
- class Strategy(enum.Enum):
103
- FIRST = enum.auto()
104
- ONE = enum.auto()
105
-
106
- def __init__(self, *fs: Factory[R, C, A], strategy: Strategy = Strategy.FIRST) -> None:
107
- super().__init__()
108
- self._fs = fs
109
- self._st = strategy
110
-
111
- def __call__(self, ctx: C, arg: A) -> R | None:
112
- w: list[R] = []
113
- for c in self._fs:
114
- if (r := c(ctx, arg)) is None:
115
- continue
116
- if self._st is CompositeFactory.Strategy.FIRST:
117
- return r
118
- w.append(r)
119
-
120
- if not w:
121
- return None
122
-
123
- if self._st is CompositeFactory.Strategy.ONE:
124
- if len(w) == 1:
125
- return w[0]
126
-
127
- raise TypeError(f'multiple implementations: {arg} {w}')
128
-
129
- raise TypeError(f'unknown composite strategy: {self._st}')
@@ -0,0 +1,35 @@
1
+ import dataclasses as dc
2
+ import typing as ta
3
+
4
+ from .. import matchfns as mfs
5
+ from .. import reflect as rfl
6
+ from .base import MarshalContext
7
+ from .base import Marshaler
8
+ from .base import UnmarshalContext
9
+ from .base import Unmarshaler
10
+ from .exceptions import ForbiddenTypeError
11
+
12
+
13
+ C = ta.TypeVar('C')
14
+ R = ta.TypeVar('R')
15
+
16
+
17
+ @dc.dataclass(frozen=True)
18
+ class ForbiddenTypeFactory(mfs.MatchFn[[C, rfl.Type], R]):
19
+ rtys: ta.AbstractSet[rfl.Type]
20
+
21
+ def guard(self, ctx: C, rty: rfl.Type) -> bool:
22
+ return rty in self.rtys
23
+
24
+ def fn(self, ctx: C, rty: rfl.Type) -> R:
25
+ raise ForbiddenTypeError(rty)
26
+
27
+
28
+ @dc.dataclass(frozen=True)
29
+ class ForbiddenTypeMarshalerFactory(ForbiddenTypeFactory[MarshalContext, Marshaler]):
30
+ pass
31
+
32
+
33
+ @dc.dataclass(frozen=True)
34
+ class ForbiddenTypeUnmarshalerFactory(ForbiddenTypeFactory[UnmarshalContext, Unmarshaler]):
35
+ pass
omlish/marshal/global_.py CHANGED
@@ -1,7 +1,10 @@
1
1
  import typing as ta
2
2
 
3
+ from .. import lang
3
4
  from .base import MarshalContext
5
+ from .base import MarshalerFactory
4
6
  from .base import UnmarshalContext
7
+ from .base import UnmarshalerFactory
5
8
  from .registries import Registry
6
9
  from .standard import new_standard_marshaler_factory
7
10
  from .standard import new_standard_unmarshaler_factory
@@ -20,20 +23,24 @@ GLOBAL_REGISTRY = Registry()
20
23
  ##
21
24
 
22
25
 
23
- GLOBAL_MARSHALER_FACTORY = new_standard_marshaler_factory()
26
+ @lang.cached_function(lock=True)
27
+ def global_marshaler_factory() -> MarshalerFactory:
28
+ return new_standard_marshaler_factory()
24
29
 
25
30
 
26
31
  def marshal(obj: ta.Any, ty: type | None = None, **kwargs: ta.Any) -> Value:
27
- mc = MarshalContext(GLOBAL_REGISTRY, factory=GLOBAL_MARSHALER_FACTORY, **kwargs)
32
+ mc = MarshalContext(GLOBAL_REGISTRY, factory=global_marshaler_factory(), **kwargs)
28
33
  return mc.make(ty if ty is not None else type(obj)).marshal(mc, obj)
29
34
 
30
35
 
31
36
  ##
32
37
 
33
38
 
34
- GLOBAL_UNMARSHALER_FACTORY = new_standard_unmarshaler_factory()
39
+ @lang.cached_function(lock=True)
40
+ def global_unmarshaler_factory() -> UnmarshalerFactory:
41
+ return new_standard_unmarshaler_factory()
35
42
 
36
43
 
37
44
  def unmarshal(v: Value, ty: type[T], **kwargs: ta.Any) -> T:
38
- uc = UnmarshalContext(GLOBAL_REGISTRY, factory=GLOBAL_UNMARSHALER_FACTORY, **kwargs)
45
+ uc = UnmarshalContext(GLOBAL_REGISTRY, factory=global_unmarshaler_factory(), **kwargs)
39
46
  return uc.make(ty).unmarshal(uc, v)
@@ -4,13 +4,14 @@ import functools
4
4
  import typing as ta
5
5
 
6
6
  from .. import check
7
+ from .. import matchfns as mfs
7
8
  from .. import reflect as rfl
8
9
  from .base import MarshalContext
9
10
  from .base import Marshaler
10
- from .base import MarshalerFactory
11
+ from .base import MarshalerFactoryMatchClass
11
12
  from .base import UnmarshalContext
12
13
  from .base import Unmarshaler
13
- from .base import UnmarshalerFactory
14
+ from .base import UnmarshalerFactoryMatchClass
14
15
  from .values import Value
15
16
 
16
17
 
@@ -22,17 +23,15 @@ class IterableMarshaler(Marshaler):
22
23
  return list(map(functools.partial(self.e.marshal, ctx), o))
23
24
 
24
25
 
25
- class IterableMarshalerFactory(MarshalerFactory):
26
- def __call__(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler | None:
27
- if isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Iterable):
28
- if (e := ctx.make(check.single(rty.args))) is None:
29
- return None # type: ignore
30
- return IterableMarshaler(e)
31
- if isinstance(rty, type) and issubclass(rty, collections.abc.Iterable):
32
- if (e := ctx.make(ta.Any)) is None:
33
- return None # type: ignore
34
- return IterableMarshaler(e)
35
- return None
26
+ class IterableMarshalerFactory(MarshalerFactoryMatchClass):
27
+ @mfs.simple(lambda _, ctx, rty: isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Iterable))
28
+ def _build_generic(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
29
+ gty = check.isinstance(rty, rfl.Generic)
30
+ return IterableMarshaler(ctx.make(check.single(gty.args)))
31
+
32
+ @mfs.simple(lambda _, ctx, rty: isinstance(rty, type) and issubclass(rty, collections.abc.Iterable))
33
+ def _build_concrete(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
34
+ return IterableMarshaler(ctx.make(ta.Any))
36
35
 
37
36
 
38
37
  @dc.dataclass(frozen=True)
@@ -44,14 +43,12 @@ class IterableUnmarshaler(Unmarshaler):
44
43
  return self.ctor(map(functools.partial(self.e.unmarshal, ctx), check.isinstance(v, collections.abc.Iterable)))
45
44
 
46
45
 
47
- class IterableUnmarshalerFactory(UnmarshalerFactory):
48
- def __call__(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler | None:
49
- if isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Iterable):
50
- if (e := ctx.make(check.single(rty.args))) is None:
51
- return None # type: ignore
52
- return IterableUnmarshaler(rty.cls, e) # noqa
53
- if isinstance(rty, type) and issubclass(rty, collections.abc.Iterable):
54
- if (e := ctx.make(ta.Any)) is None:
55
- return None # type: ignore
56
- return IterableUnmarshaler(rty, e) # noqa
57
- return None
46
+ class IterableUnmarshalerFactory(UnmarshalerFactoryMatchClass):
47
+ @mfs.simple(lambda _, ctx, rty: isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Iterable))
48
+ def _build_generic(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
49
+ gty = check.isinstance(rty, rfl.Generic)
50
+ return IterableUnmarshaler(gty.cls, ctx.make(check.single(gty.args)))
51
+
52
+ @mfs.simple(lambda _, ctx, rty: isinstance(rty, type) and issubclass(rty, collections.abc.Iterable))
53
+ def _build_concrete(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
54
+ return IterableUnmarshaler(check.isinstance(rty, type), ctx.make(ta.Any))
@@ -3,13 +3,14 @@ import dataclasses as dc
3
3
  import typing as ta
4
4
 
5
5
  from .. import check
6
+ from .. import matchfns as mfs
6
7
  from .. import reflect as rfl
7
8
  from .base import MarshalContext
8
9
  from .base import Marshaler
9
- from .base import MarshalerFactory
10
+ from .base import MarshalerFactoryMatchClass
10
11
  from .base import UnmarshalContext
11
12
  from .base import Unmarshaler
12
- from .base import UnmarshalerFactory
13
+ from .base import UnmarshalerFactoryMatchClass
13
14
  from .values import Value
14
15
 
15
16
 
@@ -25,18 +26,16 @@ class MappingMarshaler(Marshaler):
25
26
  }
26
27
 
27
28
 
28
- class MappingMarshalerFactory(MarshalerFactory):
29
- def __call__(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler | None:
30
- if isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Mapping):
31
- kt, vt = rty.args
32
- if (ke := ctx.make(kt)) is None or (ve := ctx.make(vt)) is None:
33
- return None # type: ignore
34
- return MappingMarshaler(ke, ve)
35
- if isinstance(rty, type) and issubclass(rty, collections.abc.Mapping):
36
- if (e := ctx.make(ta.Any)) is None:
37
- return None # type: ignore
38
- return MappingMarshaler(e, e)
39
- return None
29
+ class MappingMarshalerFactory(MarshalerFactoryMatchClass):
30
+ @mfs.simple(lambda _, ctx, rty: isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Mapping))
31
+ def _build_generic(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
32
+ gty = check.isinstance(rty, rfl.Generic)
33
+ kt, vt = gty.args
34
+ return MappingMarshaler(ctx.make(kt), ctx.make(vt))
35
+
36
+ @mfs.simple(lambda _, ctx, rty: isinstance(rty, type) and issubclass(rty, collections.abc.Mapping))
37
+ def _build_concrete(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
38
+ return MappingMarshaler(a := ctx.make(ta.Any), a)
40
39
 
41
40
 
42
41
  @dc.dataclass(frozen=True)
@@ -52,15 +51,13 @@ class MappingUnmarshaler(Unmarshaler):
52
51
  return self.ctor(dct)
53
52
 
54
53
 
55
- class MappingUnmarshalerFactory(UnmarshalerFactory):
56
- def __call__(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler | None:
57
- if isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Mapping):
58
- kt, vt = rty.args
59
- if (ke := ctx.make(kt)) is None or (ve := ctx.make(vt)) is None:
60
- return None # type: ignore
61
- return MappingUnmarshaler(rty.cls, ke, ve)
62
- if isinstance(rty, type) and issubclass(rty, collections.abc.Mapping):
63
- if (e := ctx.make(ta.Any)) is None:
64
- return None # type: ignore
65
- return MappingUnmarshaler(rty, e, e)
66
- return None
54
+ class MappingUnmarshalerFactory(UnmarshalerFactoryMatchClass):
55
+ @mfs.simple(lambda _, ctx, rty: isinstance(rty, rfl.Generic) and issubclass(rty.cls, collections.abc.Mapping))
56
+ def _build_generic(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
57
+ gty = check.isinstance(rty, rfl.Generic)
58
+ kt, vt = gty.args
59
+ return MappingUnmarshaler(gty.cls, ctx.make(kt), ctx.make(vt))
60
+
61
+ @mfs.simple(lambda _, ctx, rty: isinstance(rty, type) and issubclass(rty, collections.abc.Mapping))
62
+ def _build_concrete(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
63
+ return MappingUnmarshaler(check.isinstance(rty, type), a := ctx.make(ta.Any), a)
@@ -0,0 +1,51 @@
1
+ import decimal
2
+ import fractions
3
+ import typing as ta
4
+
5
+ from .. import check
6
+ from .base import MarshalContext
7
+ from .base import Marshaler
8
+ from .base import TypeMapMarshalerFactory
9
+ from .base import TypeMapUnmarshalerFactory
10
+ from .base import UnmarshalContext
11
+ from .base import Unmarshaler
12
+ from .values import Value
13
+
14
+
15
+ class ComplexMarshalerUnmarshaler(Marshaler, Unmarshaler):
16
+ def marshal(self, ctx: MarshalContext, o: complex) -> Value:
17
+ return [o.real, o.imag]
18
+
19
+ def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any:
20
+ real, imag = check.isinstance(v, list)
21
+ return complex(real, imag) # type: ignore
22
+
23
+
24
+ class DecimalMarshalerUnmarshaler(Marshaler, Unmarshaler):
25
+ def marshal(self, ctx: MarshalContext, o: decimal.Decimal) -> Value:
26
+ return str(o)
27
+
28
+ def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any:
29
+ return decimal.Decimal(check.isinstance(v, str))
30
+
31
+
32
+ class FractionMarshalerUnmarshaler(Marshaler, Unmarshaler):
33
+ def marshal(self, ctx: MarshalContext, o: fractions.Fraction) -> Value:
34
+ return [o.numerator, o.denominator]
35
+
36
+ def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any:
37
+ num, denom = check.isinstance(v, list)
38
+ return fractions.Fraction(num, denom) # type: ignore
39
+
40
+
41
+ NUMBERS_MARSHALER_FACTORY = TypeMapMarshalerFactory({
42
+ complex: ComplexMarshalerUnmarshaler(),
43
+ decimal.Decimal: DecimalMarshalerUnmarshaler(),
44
+ fractions.Fraction: FractionMarshalerUnmarshaler(),
45
+ })
46
+
47
+ NUMBERS_UNMARSHALER_FACTORY = TypeMapUnmarshalerFactory({
48
+ complex: ComplexMarshalerUnmarshaler(),
49
+ decimal.Decimal: DecimalMarshalerUnmarshaler(),
50
+ fractions.Fraction: FractionMarshalerUnmarshaler(),
51
+ })
@@ -1,6 +1,7 @@
1
1
  import dataclasses as dc
2
2
  import typing as ta
3
3
 
4
+ from .. import check
4
5
  from .. import reflect as rfl
5
6
  from .base import MarshalContext
6
7
  from .base import Marshaler
@@ -22,12 +23,11 @@ class OptionalMarshaler(Marshaler):
22
23
 
23
24
 
24
25
  class OptionalMarshalerFactory(MarshalerFactory):
25
- def __call__(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler | None:
26
- if isinstance(rty, rfl.Union) and rty.is_optional:
27
- if (e := ctx.make(rty.without_none())) is None:
28
- return None # type: ignore
29
- return OptionalMarshaler(e)
30
- return None
26
+ def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
27
+ return isinstance(rty, rfl.Union) and rty.is_optional
28
+
29
+ def fn(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
30
+ return OptionalMarshaler(ctx.make(check.isinstance(rty, rfl.Union).without_none()))
31
31
 
32
32
 
33
33
  @dc.dataclass(frozen=True)
@@ -41,9 +41,8 @@ class OptionalUnmarshaler(Unmarshaler):
41
41
 
42
42
 
43
43
  class OptionalUnmarshalerFactory(UnmarshalerFactory):
44
- def __call__(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler | None:
45
- if isinstance(rty, rfl.Union) and rty.is_optional:
46
- if (e := ctx.make(rty.without_none())) is None:
47
- return None # type: ignore
48
- return OptionalUnmarshaler(e)
49
- return None
44
+ def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
45
+ return isinstance(rty, rfl.Union) and rty.is_optional
46
+
47
+ def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
48
+ return OptionalUnmarshaler(ctx.make(check.isinstance(rty, rfl.Union).without_none()))