omlish 0.0.0.dev453__py3-none-any.whl → 0.0.0.dev455__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 (56) hide show
  1. omlish/__about__.py +2 -2
  2. omlish/collections/identity.py +1 -0
  3. omlish/dataclasses/__init__.py +2 -0
  4. omlish/funcs/guard.py +27 -16
  5. omlish/lang/__init__.py +1 -0
  6. omlish/lang/functions.py +2 -2
  7. omlish/lang/iterables.py +8 -0
  8. omlish/lite/attrops.py +2 -0
  9. omlish/lite/dataclasses.py +30 -0
  10. omlish/marshal/__init__.py +16 -11
  11. omlish/marshal/base/contexts.py +33 -20
  12. omlish/marshal/base/funcs.py +8 -11
  13. omlish/marshal/base/options.py +8 -0
  14. omlish/marshal/base/types.py +38 -14
  15. omlish/marshal/composite/iterables.py +33 -20
  16. omlish/marshal/composite/literals.py +16 -18
  17. omlish/marshal/composite/mappings.py +36 -23
  18. omlish/marshal/composite/maybes.py +29 -19
  19. omlish/marshal/composite/newtypes.py +16 -16
  20. omlish/marshal/composite/optionals.py +14 -14
  21. omlish/marshal/composite/special.py +15 -15
  22. omlish/marshal/composite/unions/__init__.py +0 -0
  23. omlish/marshal/composite/unions/literals.py +93 -0
  24. omlish/marshal/composite/unions/primitives.py +103 -0
  25. omlish/marshal/factories/invalidate.py +18 -68
  26. omlish/marshal/factories/method.py +26 -0
  27. omlish/marshal/factories/moduleimport/factories.py +15 -56
  28. omlish/marshal/factories/multi.py +13 -25
  29. omlish/marshal/factories/recursive.py +42 -56
  30. omlish/marshal/factories/typecache.py +20 -77
  31. omlish/marshal/factories/typemap.py +42 -43
  32. omlish/marshal/objects/dataclasses.py +129 -106
  33. omlish/marshal/objects/marshal.py +18 -14
  34. omlish/marshal/objects/namedtuples.py +48 -42
  35. omlish/marshal/objects/unmarshal.py +19 -15
  36. omlish/marshal/polymorphism/marshal.py +9 -11
  37. omlish/marshal/polymorphism/unions.py +17 -11
  38. omlish/marshal/polymorphism/unmarshal.py +9 -10
  39. omlish/marshal/singular/enums.py +14 -18
  40. omlish/marshal/standard.py +8 -8
  41. omlish/marshal/trivial/any.py +1 -1
  42. omlish/marshal/trivial/forbidden.py +21 -26
  43. omlish/os/forkhooks.py +4 -0
  44. omlish/specs/jsonrpc/_marshal.py +33 -24
  45. omlish/specs/openapi/_marshal.py +30 -21
  46. omlish/typedvalues/marshal.py +83 -57
  47. {omlish-0.0.0.dev453.dist-info → omlish-0.0.0.dev455.dist-info}/METADATA +1 -1
  48. {omlish-0.0.0.dev453.dist-info → omlish-0.0.0.dev455.dist-info}/RECORD +52 -52
  49. omlish/funcs/match.py +0 -229
  50. omlish/marshal/composite/unions.py +0 -213
  51. omlish/marshal/factories/match.py +0 -34
  52. omlish/marshal/factories/simple.py +0 -28
  53. {omlish-0.0.0.dev453.dist-info → omlish-0.0.0.dev455.dist-info}/WHEEL +0 -0
  54. {omlish-0.0.0.dev453.dist-info → omlish-0.0.0.dev455.dist-info}/entry_points.txt +0 -0
  55. {omlish-0.0.0.dev453.dist-info → omlish-0.0.0.dev455.dist-info}/licenses/LICENSE +0 -0
  56. {omlish-0.0.0.dev453.dist-info → omlish-0.0.0.dev455.dist-info}/top_level.txt +0 -0
@@ -3,63 +3,62 @@ import typing as ta
3
3
  from ... import check
4
4
  from ... import dataclasses as dc
5
5
  from ... import reflect as rfl
6
- from ...funcs import match as mfs
7
- from ..base.contexts import MarshalContext
8
- from ..base.contexts import UnmarshalContext
6
+ from ..base.contexts import MarshalFactoryContext
7
+ from ..base.contexts import UnmarshalFactoryContext
9
8
  from ..base.types import Marshaler
10
9
  from ..base.types import MarshalerFactory
11
- from ..base.types import MarshalerMaker
12
10
  from ..base.types import Unmarshaler
13
11
  from ..base.types import UnmarshalerFactory
14
- from ..base.types import UnmarshalerMaker
15
-
16
-
17
- R = ta.TypeVar('R')
18
- C = ta.TypeVar('C')
19
12
 
20
13
 
21
14
  ##
22
15
 
23
16
 
24
17
  @dc.dataclass(frozen=True)
25
- class _TypeMapFactory(mfs.MatchFn[[C, rfl.Type], R]):
26
- m: ta.Mapping[rfl.Type, R] = dc.field(default_factory=dict)
27
-
28
- def __post_init__(self) -> None:
29
- for k in self.m:
30
- if not isinstance(k, rfl.TYPES):
31
- raise TypeError(k)
32
-
33
- def guard(self, ctx: C, rty: rfl.Type) -> bool:
34
- check.isinstance(rty, rfl.TYPES)
35
- return rty in self.m
36
-
37
- def fn(self, ctx: C, rty: rfl.Type) -> R:
18
+ class TypeMapMarshalerFactory(MarshalerFactory):
19
+ m: ta.Mapping[rfl.Type, Marshaler | MarshalerFactory] = dc.xfield(
20
+ default_factory=dict,
21
+ coerce=lambda d: {
22
+ check.isinstance(k, rfl.TYPES): check.isinstance(v, (Marshaler, UnmarshalerFactory))
23
+ for k, v in d.items()
24
+ },
25
+ )
26
+
27
+ def make_marshaler(self, ctx: MarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Marshaler] | None:
38
28
  check.isinstance(rty, rfl.TYPES)
39
29
  try:
40
- return self.m[rty]
30
+ m = self.m[rty]
41
31
  except KeyError:
42
- raise mfs.MatchGuardError(ctx, rty) # noqa
43
-
32
+ return None
44
33
 
45
- ##
46
-
47
-
48
- @dc.dataclass(frozen=True)
49
- class TypeMapMarshalerFactory(
50
- _TypeMapFactory[MarshalContext, Marshaler],
51
- MarshalerFactory,
52
- ):
53
- @property
54
- def make_marshaler(self) -> MarshalerMaker:
55
- return self # noqa
34
+ if isinstance(m, Marshaler):
35
+ return lambda: m
36
+ elif isinstance(m, MarshalerFactory):
37
+ return m.make_marshaler(ctx, rty)
38
+ else:
39
+ raise TypeError(m)
56
40
 
57
41
 
58
42
  @dc.dataclass(frozen=True)
59
- class TypeMapUnmarshalerFactory(
60
- _TypeMapFactory[UnmarshalContext, Unmarshaler],
61
- UnmarshalerFactory,
62
- ):
63
- @property
64
- def make_unmarshaler(self) -> UnmarshalerMaker:
65
- return self # noqa
43
+ class TypeMapUnmarshalerFactory(UnmarshalerFactory):
44
+ u: ta.Mapping[rfl.Type, Unmarshaler | UnmarshalerFactory] = dc.xfield(
45
+ default_factory=dict,
46
+ coerce=lambda d: {
47
+ check.isinstance(k, rfl.TYPES): check.isinstance(v, (Unmarshaler, UnmarshalerFactory))
48
+ for k, v in d.items()
49
+ },
50
+ )
51
+
52
+ def make_unmarshaler(self, ctx: UnmarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Unmarshaler] | None:
53
+ check.isinstance(rty, rfl.TYPES)
54
+ try:
55
+ u = self.u[rty]
56
+ except KeyError:
57
+ return None
58
+
59
+ if isinstance(u, Unmarshaler):
60
+ return lambda: u
61
+ elif isinstance(u, UnmarshalerFactory):
62
+ return u.make_unmarshaler(ctx, rty)
63
+ else:
64
+ raise TypeError(u)
@@ -11,13 +11,14 @@ from ... import dataclasses as dc
11
11
  from ... import lang
12
12
  from ... import reflect as rfl
13
13
  from ...lite import marshal as lm
14
- from ..base.contexts import MarshalContext
15
- from ..base.contexts import UnmarshalContext
14
+ from ..base.contexts import MarshalFactoryContext
15
+ from ..base.contexts import UnmarshalFactoryContext
16
+ from ..base.errors import UnhandledTypeError
16
17
  from ..base.options import Option
17
18
  from ..base.types import Marshaler
19
+ from ..base.types import MarshalerFactory
18
20
  from ..base.types import Unmarshaler
19
- from ..factories.simple import SimpleMarshalerFactory
20
- from ..factories.simple import SimpleUnmarshalerFactory
21
+ from ..base.types import UnmarshalerFactory
21
22
  from ..naming import Naming
22
23
  from ..naming import translate_name
23
24
  from .marshal import ObjectMarshaler
@@ -159,19 +160,37 @@ def get_dataclass_field_infos(
159
160
  return FieldInfos(ret)
160
161
 
161
162
 
162
- def _make_field_obj(
163
- ctx,
164
- ty,
165
- obj,
166
- fac,
167
- fac_attr,
163
+ ##
164
+
165
+
166
+ def _make_field_marshal_obj(
167
+ ctx: MarshalFactoryContext,
168
+ ty: ta.Any,
169
+ obj: Marshaler | None,
170
+ fac: MarshalerFactory | None,
168
171
  ):
169
172
  if obj is not None:
170
173
  return obj
171
174
  if fac is not None:
172
- return getattr(fac, fac_attr)(ctx, ty)
173
- return ctx.make(ty)
175
+ if (m := fac.make_marshaler(ctx, ty)) is None:
176
+ raise UnhandledTypeError(ty)
177
+ return m()
178
+ return ctx.make_marshaler(ty)
179
+
174
180
 
181
+ def _make_field_unmarshal_obj(
182
+ ctx: UnmarshalFactoryContext,
183
+ ty: ta.Any,
184
+ obj: Unmarshaler | None,
185
+ fac: UnmarshalerFactory | None,
186
+ ):
187
+ if obj is not None:
188
+ return obj
189
+ if fac is not None:
190
+ if (m := fac.make_unmarshaler(ctx, ty)) is None:
191
+ raise UnhandledTypeError(ty)
192
+ return m()
193
+ return ctx.make_unmarshaler(ty)
175
194
 
176
195
  ##
177
196
 
@@ -200,119 +219,123 @@ def _type_or_generic_base(rty: rfl.Type) -> type | None:
200
219
  return None
201
220
 
202
221
 
203
- class DataclassMarshalerFactory(AbstractDataclassFactory, SimpleMarshalerFactory):
204
- def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
205
- return (
222
+ class DataclassMarshalerFactory(AbstractDataclassFactory, MarshalerFactory):
223
+ def make_marshaler(self, ctx: MarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Marshaler] | None:
224
+ if not (
206
225
  (ty := _type_or_generic_base(rty)) is not None and
207
226
  dc.is_dataclass(ty) and
208
227
  not lang.is_abstract_class(ty)
209
- )
228
+ ):
229
+ return None
230
+
231
+ def inner() -> Marshaler:
232
+ ty = check.isinstance(_type_or_generic_base(rty), type)
233
+ check.state(dc.is_dataclass(ty))
234
+ check.state(not lang.is_abstract_class(ty))
235
+
236
+ dc_md = self._get_metadata(ty)
237
+ fis = self._get_field_infos(ty, ctx.options)
238
+
239
+ fields = [
240
+ (
241
+ fi,
242
+ _make_field_marshal_obj(
243
+ ctx,
244
+ fi.type,
245
+ fi.metadata.marshaler,
246
+ fi.metadata.marshaler_factory,
247
+ ),
248
+ )
249
+ for fi in fis
250
+ if fi.name not in dc_md.specials.set
251
+ ]
210
252
 
211
- def fn(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
212
- ty = check.isinstance(_type_or_generic_base(rty), type)
213
- check.state(dc.is_dataclass(ty))
214
- check.state(not lang.is_abstract_class(ty))
215
-
216
- dc_md = self._get_metadata(ty)
217
- fis = self._get_field_infos(ty, ctx.options)
218
-
219
- fields = [
220
- (
221
- fi,
222
- _make_field_obj(
223
- ctx,
224
- fi.type,
225
- fi.metadata.marshaler,
226
- fi.metadata.marshaler_factory,
227
- 'make_marshaler',
228
- ),
253
+ return ObjectMarshaler(
254
+ fields,
255
+ specials=dc_md.specials,
229
256
  )
230
- for fi in fis
231
- if fi.name not in dc_md.specials.set
232
- ]
233
257
 
234
- return ObjectMarshaler(
235
- fields,
236
- specials=dc_md.specials,
237
- )
258
+ return inner
238
259
 
239
260
 
240
261
  ##
241
262
 
242
263
 
243
- class DataclassUnmarshalerFactory(AbstractDataclassFactory, SimpleUnmarshalerFactory):
244
- def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
245
- return (
264
+ class DataclassUnmarshalerFactory(AbstractDataclassFactory, UnmarshalerFactory):
265
+ def make_unmarshaler(self, ctx: UnmarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Unmarshaler] | None:
266
+ if not (
246
267
  (ty := _type_or_generic_base(rty)) is not None and
247
268
  dc.is_dataclass(ty) and
248
269
  not lang.is_abstract_class(ty)
249
- )
270
+ ):
271
+ return None
250
272
 
251
- def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
252
- ty = check.isinstance(_type_or_generic_base(rty), type)
253
- check.state(dc.is_dataclass(ty))
254
- check.state(not lang.is_abstract_class(ty))
273
+ def inner() -> Unmarshaler:
274
+ ty = check.isinstance(_type_or_generic_base(rty), type)
275
+ check.state(dc.is_dataclass(ty))
276
+ check.state(not lang.is_abstract_class(ty))
255
277
 
256
- dc_md = self._get_metadata(ty)
257
- fis = self._get_field_infos(ty, ctx.options)
278
+ dc_md = self._get_metadata(ty)
279
+ fis = self._get_field_infos(ty, ctx.options)
258
280
 
259
- d: dict[str, tuple[FieldInfo, Unmarshaler]] = {}
260
- defaults: dict[str, ta.Any] = {}
261
- embeds: dict[str, type] = {}
262
- embeds_by_unmarshal_name: dict[str, tuple[str, str]] = {}
281
+ d: dict[str, tuple[FieldInfo, Unmarshaler]] = {}
282
+ defaults: dict[str, ta.Any] = {}
283
+ embeds: dict[str, type] = {}
284
+ embeds_by_unmarshal_name: dict[str, tuple[str, str]] = {}
263
285
 
264
- def add_field(fi: FieldInfo, *, prefixes: ta.Iterable[str] = ('',)) -> ta.Iterable[str]:
265
- ret: list[str] = []
286
+ def add_field(fi: FieldInfo, *, prefixes: ta.Iterable[str] = ('',)) -> ta.Iterable[str]:
287
+ ret: list[str] = []
266
288
 
267
- if fi.options.embed:
268
- e_ty = check.isinstance(fi.type, type)
269
- check.state(dc.is_dataclass(e_ty))
270
- e_dc_md = get_dataclass_metadata(e_ty)
271
- if e_dc_md.specials.set:
272
- raise Exception(f'Embedded fields cannot have specials: {e_ty}')
289
+ if fi.options.embed:
290
+ e_ty = check.isinstance(fi.type, type)
291
+ check.state(dc.is_dataclass(e_ty))
292
+ e_dc_md = get_dataclass_metadata(e_ty)
293
+ if e_dc_md.specials.set:
294
+ raise Exception(f'Embedded fields cannot have specials: {e_ty}')
273
295
 
274
- embeds[fi.name] = e_ty
275
- for e_fi in self._get_field_infos(e_ty, ctx.options):
276
- e_ns = add_field(e_fi, prefixes=[p + ep for p in prefixes for ep in fi.unmarshal_names])
277
- embeds_by_unmarshal_name.update({e_f: (fi.name, e_fi.name) for e_f in e_ns})
278
- ret.extend(e_ns)
296
+ embeds[fi.name] = e_ty
297
+ for e_fi in self._get_field_infos(e_ty, ctx.options):
298
+ e_ns = add_field(e_fi, prefixes=[p + ep for p in prefixes for ep in fi.unmarshal_names])
299
+ embeds_by_unmarshal_name.update({e_f: (fi.name, e_fi.name) for e_f in e_ns})
300
+ ret.extend(e_ns)
279
301
 
280
- else:
281
- tup = (
282
- fi,
283
- _make_field_obj(
284
- ctx,
285
- fi.type,
286
- fi.metadata.unmarshaler,
287
- fi.metadata.unmarshaler_factory,
288
- 'make_unmarshaler',
289
- ),
290
- )
302
+ else:
303
+ tup = (
304
+ fi,
305
+ _make_field_unmarshal_obj(
306
+ ctx,
307
+ fi.type,
308
+ fi.metadata.unmarshaler,
309
+ fi.metadata.unmarshaler_factory,
310
+ ),
311
+ )
291
312
 
292
- for pfx in prefixes:
293
- for un in fi.unmarshal_names:
294
- un = pfx + un
295
- if un in d:
296
- raise KeyError(f'Duplicate fields for name {un!r}: {fi.name!r}, {d[un][0].name!r}')
297
- d[un] = tup
298
- ret.append(un)
299
-
300
- if fi.options.default.present:
301
- defaults[fi.name] = fi.options.default.must()
302
-
303
- return ret
304
-
305
- for fi in fis:
306
- if fi.name in dc_md.specials.set:
307
- continue
308
- add_field(fi)
309
-
310
- return ObjectUnmarshaler(
311
- ty,
312
- d,
313
- specials=dc_md.specials,
314
- defaults=defaults,
315
- embeds=embeds,
316
- embeds_by_unmarshal_name=embeds_by_unmarshal_name,
317
- ignore_unknown=dc_md.ignore_unknown,
318
- )
313
+ for pfx in prefixes:
314
+ for un in fi.unmarshal_names:
315
+ un = pfx + un
316
+ if un in d:
317
+ raise KeyError(f'Duplicate fields for name {un!r}: {fi.name!r}, {d[un][0].name!r}')
318
+ d[un] = tup
319
+ ret.append(un)
320
+
321
+ if fi.options.default.present:
322
+ defaults[fi.name] = fi.options.default.must()
323
+
324
+ return ret
325
+
326
+ for fi in fis:
327
+ if fi.name in dc_md.specials.set:
328
+ continue
329
+ add_field(fi)
330
+
331
+ return ObjectUnmarshaler(
332
+ ty,
333
+ d,
334
+ specials=dc_md.specials,
335
+ defaults=defaults,
336
+ embeds=embeds,
337
+ embeds_by_unmarshal_name=embeds_by_unmarshal_name,
338
+ ignore_unknown=dc_md.ignore_unknown,
339
+ )
340
+
341
+ return inner
@@ -5,9 +5,10 @@ from ... import check
5
5
  from ... import dataclasses as dc
6
6
  from ... import reflect as rfl
7
7
  from ..base.contexts import MarshalContext
8
+ from ..base.contexts import MarshalFactoryContext
8
9
  from ..base.types import Marshaler
10
+ from ..base.types import MarshalerFactory
9
11
  from ..base.values import Value
10
- from ..factories.simple import SimpleMarshalerFactory
11
12
  from .metadata import FieldInfo
12
13
  from .metadata import FieldInfos
13
14
  from .metadata import ObjectSpecials
@@ -29,12 +30,12 @@ class ObjectMarshaler(Marshaler):
29
30
  @classmethod
30
31
  def make(
31
32
  cls,
32
- ctx: MarshalContext,
33
+ ctx: MarshalFactoryContext,
33
34
  fis: FieldInfos,
34
35
  **kwargs: ta.Any,
35
36
  ) -> Marshaler:
36
37
  fields = [
37
- (fi, ctx.make(fi.type))
38
+ (fi, ctx.make_marshaler(fi.type))
38
39
  for fi in fis
39
40
  ]
40
41
 
@@ -86,23 +87,26 @@ class ObjectMarshaler(Marshaler):
86
87
 
87
88
 
88
89
  @dc.dataclass(frozen=True)
89
- class SimpleObjectMarshalerFactory(SimpleMarshalerFactory):
90
+ class SimpleObjectMarshalerFactory(MarshalerFactory):
90
91
  dct: ta.Mapping[type, ta.Sequence[FieldInfo]]
91
92
 
92
93
  _: dc.KW_ONLY
93
94
 
94
95
  specials: ObjectSpecials = ObjectSpecials()
95
96
 
96
- def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
97
- return isinstance(rty, type) and rty in self.dct
97
+ def make_marshaler(self, ctx: MarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Marshaler] | None:
98
+ if not (isinstance(rty, type) and rty in self.dct):
99
+ return None
98
100
 
99
- def fn(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
100
- ty = check.isinstance(rty, type)
101
+ def inner() -> Marshaler:
102
+ ty = check.isinstance(rty, type)
101
103
 
102
- fis = FieldInfos(self.dct[ty])
104
+ fis = FieldInfos(self.dct[ty])
103
105
 
104
- return ObjectMarshaler.make(
105
- ctx,
106
- fis,
107
- specials=self.specials,
108
- )
106
+ return ObjectMarshaler.make(
107
+ ctx,
108
+ fis,
109
+ specials=self.specials,
110
+ )
111
+
112
+ return inner
@@ -5,13 +5,13 @@ from ... import check
5
5
  from ... import collections as col
6
6
  from ... import lang
7
7
  from ... import reflect as rfl
8
- from ..base.contexts import MarshalContext
9
- from ..base.contexts import UnmarshalContext
8
+ from ..base.contexts import MarshalFactoryContext
9
+ from ..base.contexts import UnmarshalFactoryContext
10
10
  from ..base.options import Option
11
11
  from ..base.types import Marshaler
12
+ from ..base.types import MarshalerFactory
12
13
  from ..base.types import Unmarshaler
13
- from ..factories.simple import SimpleMarshalerFactory
14
- from ..factories.simple import SimpleUnmarshalerFactory
14
+ from ..base.types import UnmarshalerFactory
15
15
  from .marshal import ObjectMarshaler
16
16
  from .metadata import FieldInfo
17
17
  from .metadata import FieldInfos
@@ -55,57 +55,63 @@ def get_namedtuple_field_infos(
55
55
  ##
56
56
 
57
57
 
58
- class NamedtupleMarshalerFactory(SimpleMarshalerFactory):
59
- def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
60
- return _is_namedtuple(rty)
58
+ class NamedtupleMarshalerFactory(MarshalerFactory):
59
+ def make_marshaler(self, ctx: MarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Marshaler] | None:
60
+ if not _is_namedtuple(rty):
61
+ return None
61
62
 
62
- def fn(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
63
- check.state(_is_namedtuple(rty))
64
- ty = check.isinstance(rty, type)
65
- check.state(not lang.is_abstract_class(ty))
63
+ def inner() -> Marshaler:
64
+ check.state(_is_namedtuple(rty))
65
+ ty = check.isinstance(rty, type)
66
+ check.state(not lang.is_abstract_class(ty))
66
67
 
67
- fis = get_namedtuple_field_infos(ty, ctx.options)
68
+ fis = get_namedtuple_field_infos(ty, ctx.options)
68
69
 
69
- fields = [
70
- (fi, ctx.make(fi.type))
71
- for fi in fis
72
- ]
70
+ fields = [
71
+ (fi, ctx.make_marshaler(fi.type))
72
+ for fi in fis
73
+ ]
73
74
 
74
- return ObjectMarshaler(
75
- fields,
76
- )
75
+ return ObjectMarshaler(
76
+ fields,
77
+ )
78
+
79
+ return inner
77
80
 
78
81
 
79
82
  ##
80
83
 
81
84
 
82
- class NamedtupleUnmarshalerFactory(SimpleUnmarshalerFactory):
83
- def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
84
- return _is_namedtuple(rty)
85
+ class NamedtupleUnmarshalerFactory(UnmarshalerFactory):
86
+ def make_unmarshaler(self, ctx: UnmarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Unmarshaler] | None:
87
+ if not _is_namedtuple(rty):
88
+ return None
89
+
90
+ def inner() -> Unmarshaler:
91
+ check.state(_is_namedtuple(rty))
92
+ ty = check.isinstance(rty, type)
93
+ check.state(not lang.is_abstract_class(ty))
85
94
 
86
- def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
87
- check.state(_is_namedtuple(rty))
88
- ty = check.isinstance(rty, type)
89
- check.state(not lang.is_abstract_class(ty))
95
+ fis = get_namedtuple_field_infos(ty, ctx.options)
90
96
 
91
- fis = get_namedtuple_field_infos(ty, ctx.options)
97
+ d: dict[str, tuple[FieldInfo, Unmarshaler]] = {}
98
+ defaults: dict[str, ta.Any] = {}
92
99
 
93
- d: dict[str, tuple[FieldInfo, Unmarshaler]] = {}
94
- defaults: dict[str, ta.Any] = {}
100
+ for fi in fis:
101
+ tup = (fi, ctx.make_unmarshaler(fi.type))
95
102
 
96
- for fi in fis:
97
- tup = (fi, ctx.make(fi.type))
103
+ for un in fi.unmarshal_names:
104
+ if un in d:
105
+ raise KeyError(f'Duplicate fields for name {un!r}: {fi.name!r}, {d[un][0].name!r}')
106
+ d[un] = tup
98
107
 
99
- for un in fi.unmarshal_names:
100
- if un in d:
101
- raise KeyError(f'Duplicate fields for name {un!r}: {fi.name!r}, {d[un][0].name!r}')
102
- d[un] = tup
108
+ if fi.options.default.present:
109
+ defaults[fi.name] = fi.options.default.must()
103
110
 
104
- if fi.options.default.present:
105
- defaults[fi.name] = fi.options.default.must()
111
+ return ObjectUnmarshaler(
112
+ ty,
113
+ d,
114
+ defaults=defaults,
115
+ )
106
116
 
107
- return ObjectUnmarshaler(
108
- ty,
109
- d,
110
- defaults=defaults,
111
- )
117
+ return inner
@@ -5,9 +5,10 @@ from ... import check
5
5
  from ... import dataclasses as dc
6
6
  from ... import reflect as rfl
7
7
  from ..base.contexts import UnmarshalContext
8
+ from ..base.contexts import UnmarshalFactoryContext
8
9
  from ..base.types import Unmarshaler
10
+ from ..base.types import UnmarshalerFactory
9
11
  from ..base.values import Value
10
- from ..factories.simple import SimpleUnmarshalerFactory
11
12
  from .metadata import FieldInfo
12
13
  from .metadata import FieldInfos
13
14
  from .metadata import ObjectSpecials
@@ -35,13 +36,13 @@ class ObjectUnmarshaler(Unmarshaler):
35
36
  @classmethod
36
37
  def make(
37
38
  cls,
38
- ctx: UnmarshalContext,
39
+ ctx: UnmarshalFactoryContext,
39
40
  factory: ta.Callable,
40
41
  fis: FieldInfos,
41
42
  **kwargs: ta.Any,
42
43
  ) -> Unmarshaler:
43
44
  fields_by_unmarshal_name = {
44
- n: (fi, ctx.make(fi.type))
45
+ n: (fi, ctx.make_unmarshaler(fi.type))
45
46
  for fi in fis
46
47
  for n in fi.unmarshal_names
47
48
  }
@@ -118,24 +119,27 @@ class ObjectUnmarshaler(Unmarshaler):
118
119
 
119
120
 
120
121
  @dc.dataclass(frozen=True)
121
- class SimpleObjectUnmarshalerFactory(SimpleUnmarshalerFactory):
122
+ class SimpleObjectUnmarshalerFactory(UnmarshalerFactory):
122
123
  dct: ta.Mapping[type, ta.Sequence[FieldInfo]]
123
124
 
124
125
  _: dc.KW_ONLY
125
126
 
126
127
  specials: ObjectSpecials = ObjectSpecials()
127
128
 
128
- def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
129
- return isinstance(rty, type) and rty in self.dct
129
+ def make_unmarshaler(self, ctx: UnmarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], Unmarshaler] | None:
130
+ if not (isinstance(rty, type) and rty in self.dct):
131
+ return None
130
132
 
131
- def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
132
- ty = check.isinstance(rty, type)
133
+ def inner() -> Unmarshaler:
134
+ ty = check.isinstance(rty, type)
133
135
 
134
- fis = FieldInfos(self.dct[ty])
136
+ fis = FieldInfos(self.dct[ty])
135
137
 
136
- return ObjectUnmarshaler.make(
137
- ctx,
138
- ty,
139
- fis,
140
- specials=self.specials,
141
- )
138
+ return ObjectUnmarshaler.make(
139
+ ctx,
140
+ ty,
141
+ fis,
142
+ specials=self.specials,
143
+ )
144
+
145
+ return inner