omlish 0.0.0.dev19__py3-none-any.whl → 0.0.0.dev21__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 (42) hide show
  1. omlish/__about__.py +3 -3
  2. omlish/asyncs/anyio.py +13 -6
  3. omlish/dataclasses/__init__.py +2 -0
  4. omlish/dataclasses/impl/fields.py +51 -0
  5. omlish/dataclasses/impl/frozen.py +38 -0
  6. omlish/dataclasses/impl/main.py +24 -88
  7. omlish/dataclasses/impl/metadata.py +2 -1
  8. omlish/dataclasses/impl/processing.py +10 -2
  9. omlish/dataclasses/utils.py +45 -0
  10. omlish/fnpairs.py +1 -1
  11. omlish/formats/json.py +1 -0
  12. omlish/lang/classes/simple.py +3 -0
  13. omlish/lang/clsdct.py +2 -0
  14. omlish/lang/contextmanagers.py +41 -0
  15. omlish/lang/descriptors.py +8 -0
  16. omlish/lang/objects.py +4 -0
  17. omlish/lang/resolving.py +9 -0
  18. omlish/lite/logs.py +2 -2
  19. omlish/lite/marshal.py +4 -2
  20. omlish/marshal/__init__.py +4 -0
  21. omlish/marshal/dataclasses.py +16 -3
  22. omlish/marshal/helpers.py +22 -0
  23. omlish/marshal/objects.py +33 -14
  24. omlish/multiprocessing.py +36 -4
  25. omlish/specs/__init__.py +0 -0
  26. omlish/specs/jsonschema/__init__.py +0 -0
  27. omlish/specs/jsonschema/keywords/__init__.py +42 -0
  28. omlish/specs/jsonschema/keywords/base.py +86 -0
  29. omlish/specs/jsonschema/keywords/core.py +26 -0
  30. omlish/specs/jsonschema/keywords/metadata.py +22 -0
  31. omlish/specs/jsonschema/keywords/parse.py +69 -0
  32. omlish/specs/jsonschema/keywords/render.py +47 -0
  33. omlish/specs/jsonschema/keywords/validation.py +68 -0
  34. omlish/specs/jsonschema/schemas/__init__.py +0 -0
  35. omlish/specs/jsonschema/schemas/draft202012/__init__.py +0 -0
  36. omlish/specs/jsonschema/schemas/draft202012/vocabularies/__init__.py +0 -0
  37. omlish/specs/jsonschema/types.py +21 -0
  38. {omlish-0.0.0.dev19.dist-info → omlish-0.0.0.dev21.dist-info}/METADATA +3 -3
  39. {omlish-0.0.0.dev19.dist-info → omlish-0.0.0.dev21.dist-info}/RECORD +42 -28
  40. {omlish-0.0.0.dev19.dist-info → omlish-0.0.0.dev21.dist-info}/LICENSE +0 -0
  41. {omlish-0.0.0.dev19.dist-info → omlish-0.0.0.dev21.dist-info}/WHEEL +0 -0
  42. {omlish-0.0.0.dev19.dist-info → omlish-0.0.0.dev21.dist-info}/top_level.txt +0 -0
omlish/lite/logs.py CHANGED
@@ -69,8 +69,8 @@ class JsonLogFormatter(logging.Formatter):
69
69
  STANDARD_LOG_FORMAT_PARTS = [
70
70
  ('asctime', '%(asctime)-15s'),
71
71
  ('process', 'pid=%(process)-6s'),
72
- ('thread', 'tid=%(thread)-10x'),
73
- ('levelname', '%(levelname)-8s'),
72
+ ('thread', 'tid=%(thread)x'),
73
+ ('levelname', '%(levelname)s'),
74
74
  ('name', '%(name)s'),
75
75
  ('separator', '::'),
76
76
  ('message', '%(message)s'),
omlish/lite/marshal.py CHANGED
@@ -232,8 +232,10 @@ _OBJ_MARSHALER_GENERIC_MAPPING_TYPES: ta.Dict[ta.Any, type] = {
232
232
 
233
233
  _OBJ_MARSHALER_GENERIC_ITERABLE_TYPES: ta.Dict[ta.Any, type] = {
234
234
  **{t: t for t in (list, tuple, set, frozenset)},
235
- **{t: frozenset for t in (collections.abc.Set, collections.abc.MutableSet)},
236
- **{t: tuple for t in (collections.abc.Sequence, collections.abc.MutableSequence)},
235
+ collections.abc.Set: frozenset,
236
+ collections.abc.MutableSet: set,
237
+ collections.abc.Sequence: tuple,
238
+ collections.abc.MutableSequence: list,
237
239
  }
238
240
 
239
241
 
@@ -48,6 +48,10 @@ from .global_ import ( # noqa
48
48
  unmarshal,
49
49
  )
50
50
 
51
+ from .helpers import ( # noqa
52
+ update_fields_metadata,
53
+ )
54
+
51
55
  from .objects import ( # noqa
52
56
  FieldMetadata,
53
57
  ObjectMetadata,
@@ -31,10 +31,19 @@ def get_dataclass_metadata(ty: type) -> ObjectMetadata:
31
31
  ) or ObjectMetadata()
32
32
 
33
33
 
34
- def get_field_infos(ty: type, opts: col.TypeMap[Option] = col.TypeMap()) -> ta.Sequence[FieldInfo]:
34
+ def get_field_infos(
35
+ ty: type,
36
+ opts: col.TypeMap[Option] = col.TypeMap(),
37
+ ) -> ta.Sequence[FieldInfo]:
35
38
  dc_md = get_dataclass_metadata(ty)
36
39
  dc_naming = dc_md.field_naming or opts.get(Naming)
37
40
 
41
+ fi_defaults = {
42
+ k: v
43
+ for k, v in dc.asdict(dc_md.field_defaults).items()
44
+ if v is not None
45
+ }
46
+
38
47
  type_hints = ta.get_type_hints(ty)
39
48
 
40
49
  ret: list[FieldInfo] = []
@@ -44,7 +53,8 @@ def get_field_infos(ty: type, opts: col.TypeMap[Option] = col.TypeMap()) -> ta.S
44
53
  else:
45
54
  um_name = field.name
46
55
 
47
- kw = dict(
56
+ kw = dict(fi_defaults)
57
+ kw.update(
48
58
  name=field.name,
49
59
  type=type_hints[field.name],
50
60
  metadata=FieldMetadata(),
@@ -54,7 +64,10 @@ def get_field_infos(ty: type, opts: col.TypeMap[Option] = col.TypeMap()) -> ta.S
54
64
  )
55
65
 
56
66
  if (fmd := field.metadata.get(FieldMetadata)) is not None:
57
- kw.update(metadata=fmd)
67
+ kw.update(
68
+ metadata=fmd,
69
+ omit_if=fmd.omit_if,
70
+ )
58
71
 
59
72
  if fmd.name is not None:
60
73
  kw.update(
@@ -0,0 +1,22 @@
1
+ import typing as ta
2
+
3
+ from .. import dataclasses as dc
4
+ from .objects import FieldMetadata
5
+
6
+
7
+ T = ta.TypeVar('T')
8
+
9
+
10
+ def update_fields_metadata(
11
+ fields: ta.Iterable[str] | None = None,
12
+ **kwargs: ta.Any,
13
+ ) -> ta.Callable[[type[T]], type[T]]:
14
+ def inner(a: str, f: dc.Field) -> dc.Field:
15
+ return dc.update_field_metadata(f, {
16
+ FieldMetadata: dc.replace(
17
+ f.metadata.get(FieldMetadata, FieldMetadata()),
18
+ **kwargs,
19
+ ),
20
+ })
21
+
22
+ return dc.update_fields(inner, fields)
omlish/marshal/objects.py CHANGED
@@ -22,18 +22,13 @@ from .values import Value
22
22
  ##
23
23
 
24
24
 
25
- @dc.dataclass(frozen=True)
26
- class ObjectMetadata:
27
- field_naming: Naming | None = None
28
-
29
- unknown_field: str | None = None
30
-
31
-
32
- @dc.dataclass(frozen=True)
25
+ @dc.dataclass(frozen=True, kw_only=True)
33
26
  class FieldMetadata:
34
27
  name: str | None = None
35
28
  alts: ta.Iterable[str] | None = None
36
29
 
30
+ omit_if: ta.Callable[[ta.Any], bool] | None = None
31
+
37
32
  marshaler: Marshaler | None = None
38
33
  marshaler_factory: MarshalerFactory | None = None
39
34
 
@@ -41,18 +36,30 @@ class FieldMetadata:
41
36
  unmarshaler_factory: UnmarshalerFactory | None = None
42
37
 
43
38
 
39
+ @dc.dataclass(frozen=True, kw_only=True)
40
+ class ObjectMetadata:
41
+ field_naming: Naming | None = None
42
+
43
+ unknown_field: str | None = None
44
+
45
+ field_defaults: FieldMetadata = FieldMetadata()
46
+
47
+
44
48
  ##
45
49
 
46
50
 
47
- @dc.dataclass(frozen=True)
51
+ @dc.dataclass(frozen=True, kw_only=True)
48
52
  class FieldInfo:
49
53
  name: str
50
54
  type: ta.Any
51
- metadata: FieldMetadata
52
55
 
53
56
  marshal_name: str
54
57
  unmarshal_names: ta.Sequence[str]
55
58
 
59
+ metadata: FieldMetadata = FieldMetadata()
60
+
61
+ omit_if: ta.Callable[[ta.Any], bool] | None = None
62
+
56
63
 
57
64
  ##
58
65
 
@@ -63,15 +70,20 @@ class ObjectMarshaler(Marshaler):
63
70
  unknown_field: str | None = None
64
71
 
65
72
  def marshal(self, ctx: MarshalContext, o: ta.Any) -> Value:
66
- ret = {
67
- fi.marshal_name: m.marshal(ctx, getattr(o, fi.name))
68
- for fi, m in self.fields
69
- }
73
+ ret = {}
74
+ for fi, m in self.fields:
75
+ v = getattr(o, fi.name)
76
+ if fi.omit_if is not None and fi.omit_if(v):
77
+ continue
78
+ ret[fi.marshal_name] = m.marshal(ctx, v)
79
+
70
80
  if self.unknown_field is not None:
71
81
  if (ukf := getattr(o, self.unknown_field)):
72
82
  if (dks := set(ret) & set(ukf)):
73
83
  raise KeyError(f'Unknown field keys duplicate fields: {dks!r}')
84
+
74
85
  ret.update(ukf) # FIXME: marshal?
86
+
75
87
  return ret
76
88
 
77
89
 
@@ -89,18 +101,25 @@ class ObjectUnmarshaler(Unmarshaler):
89
101
  u: ta.Any
90
102
  kw: dict[str, ta.Any] = {}
91
103
  ukf: dict[str, ta.Any] | None = None
104
+
92
105
  if self.unknown_field is not None:
93
106
  kw[self.unknown_field] = ukf = {}
107
+
94
108
  for k, mv in ma.items():
95
109
  ks = check.isinstance(k, str)
110
+
96
111
  try:
97
112
  fi, u = self.fields_by_unmarshal_name[ks]
113
+
98
114
  except KeyError:
99
115
  if ukf is not None:
100
116
  ukf[ks] = mv # FIXME: unmarshal?
101
117
  continue
102
118
  raise
119
+
103
120
  if fi.name in kw:
104
121
  raise KeyError(f'Duplicate keys for field {fi.name!r}: {ks!r}')
122
+
105
123
  kw[fi.name] = u.unmarshal(ctx, mv)
124
+
106
125
  return self.cls(**kw)
omlish/multiprocessing.py CHANGED
@@ -9,6 +9,7 @@ import time
9
9
  import typing as ta
10
10
 
11
11
  from . import check
12
+ from . import lang
12
13
  from . import libc
13
14
 
14
15
 
@@ -18,21 +19,47 @@ T = ta.TypeVar('T')
18
19
  ##
19
20
 
20
21
 
22
+ @ta.runtime_checkable
23
+ class ValueProxy(ta.Protocol[T]):
24
+ # value = property(get, set)
25
+
26
+ def get(self) -> T:
27
+ ...
28
+
29
+ def set(self, value: T) -> None:
30
+ ...
31
+
32
+
33
+ @dc.dataclass()
34
+ @lang.protocol_check(ValueProxy)
35
+ class DummyValueProxy(ValueProxy[T]):
36
+ value: T
37
+
38
+ def get(self) -> T:
39
+ return self.value
40
+
41
+ def set(self, value: T) -> None:
42
+ self.value = value
43
+
44
+
45
+ ##
46
+
47
+
21
48
  @dc.dataclass(frozen=True, kw_only=True)
22
49
  class SpawnExtras:
23
- fds: ta.AbstractSet[int] | None = None
50
+ pass_fds: ta.AbstractSet[int] | None = None
24
51
  deathsig: int | None = None
25
52
 
26
53
 
27
54
  class ExtrasSpawnPosixPopen(mp.popen_spawn_posix.Popen):
28
55
  def __init__(self, process_obj: 'ExtrasSpawnProcess', *, extras: SpawnExtras) -> None:
29
56
  self.__extras = extras
30
- self.__extra_fds = extras.fds
57
+ self.__pass_fds = extras.pass_fds
31
58
  super().__init__(process_obj)
32
59
 
33
60
  def _launch(self, process_obj: 'ExtrasSpawnProcess') -> None:
34
- if self.__extra_fds:
35
- for fd in self.__extra_fds:
61
+ if self.__pass_fds:
62
+ for fd in self.__pass_fds:
36
63
  self.duplicate_for_child(fd)
37
64
  self._extra_fds = None
38
65
 
@@ -75,6 +102,11 @@ class Deathpact(abc.ABC):
75
102
  raise NotImplementedError
76
103
 
77
104
 
105
+ class NopDeathpact(Deathpact):
106
+ def poll(self) -> None:
107
+ pass
108
+
109
+
78
110
  #
79
111
 
80
112
 
File without changes
File without changes
@@ -0,0 +1,42 @@
1
+ from .base import ( # noqa
2
+ Keyword,
3
+ Keywords,
4
+ )
5
+
6
+ from .core import ( # noqa
7
+ CoreKeyword,
8
+ Id,
9
+ Ref,
10
+ SchemaKeyword,
11
+ )
12
+
13
+ from .metadata import ( # noqa
14
+ Description,
15
+ MetadataKeyword,
16
+ Title,
17
+ )
18
+
19
+ from .parse import ( # noqa
20
+ parse_keyword,
21
+ parse_keywords,
22
+ )
23
+
24
+ from .render import ( # noqa
25
+ render_keyword,
26
+ render_keywords,
27
+ )
28
+
29
+ from .validation import ( # noqa
30
+ ExclusiveMaximum,
31
+ ExclusiveMinimum,
32
+ Items,
33
+ MaxItems,
34
+ Maximum,
35
+ MinItems,
36
+ Minimum,
37
+ Properties,
38
+ Required,
39
+ Type,
40
+ UniqueItems,
41
+ ValidationKeyword,
42
+ )
@@ -0,0 +1,86 @@
1
+ import operator
2
+ import typing as ta
3
+
4
+ from omlish import cached
5
+ from omlish import check
6
+ from omlish import collections as col
7
+ from omlish import dataclasses as dc
8
+ from omlish import lang
9
+
10
+
11
+ KeywordT = ta.TypeVar('KeywordT', bound='Keyword')
12
+
13
+
14
+ ##
15
+
16
+
17
+ class Keyword(lang.Abstract, lang.PackageSealed):
18
+ tag: ta.ClassVar[str]
19
+
20
+ def __init_subclass__(cls, *, tag: str | None = None, **kwargs: ta.Any) -> None:
21
+ super().__init_subclass__(**kwargs)
22
+ check.not_in('tag', dir(cls))
23
+ if not lang.is_abstract_class(cls):
24
+ check.issubclass(cls, lang.Final)
25
+ cls.tag = check.non_empty_str(tag)
26
+ else:
27
+ check.none(tag)
28
+
29
+
30
+ ##
31
+
32
+
33
+ @dc.dataclass(frozen=True)
34
+ class Keywords(lang.Final):
35
+ lst: ta.Sequence[Keyword]
36
+
37
+ @cached.property
38
+ @dc.init
39
+ def by_type(self) -> ta.Mapping[type[Keyword], Keyword]:
40
+ return col.unique_map_by(type, self.lst, strict=True) # noqa
41
+
42
+ @cached.property
43
+ @dc.init
44
+ def by_tag(self) -> ta.Mapping[str, Keyword]:
45
+ return col.unique_map_by(operator.attrgetter('tag'), self.lst, strict=True) # noqa
46
+
47
+ def __getitem__(self, item: type[KeywordT] | str) -> KeywordT:
48
+ if isinstance(item, type):
49
+ return self.by_type[item] # noqa
50
+ elif isinstance(item, str):
51
+ return self.by_tag[item]
52
+ else:
53
+ raise TypeError(item)
54
+
55
+
56
+ ##
57
+
58
+
59
+ @dc.dataclass(frozen=True)
60
+ class BooleanKeyword(Keyword, lang.Abstract):
61
+ b: bool
62
+
63
+
64
+ @dc.dataclass(frozen=True)
65
+ class NumberKeyword(Keyword, lang.Abstract):
66
+ n: int | float
67
+
68
+
69
+ @dc.dataclass(frozen=True)
70
+ class StrKeyword(Keyword, lang.Abstract):
71
+ s: str
72
+
73
+
74
+ @dc.dataclass(frozen=True)
75
+ class StrOrStrsKeyword(Keyword, lang.Abstract):
76
+ ss: str | ta.Sequence[str]
77
+
78
+
79
+ @dc.dataclass(frozen=True)
80
+ class KeywordsKeyword(Keyword, lang.Abstract):
81
+ kw: Keywords
82
+
83
+
84
+ @dc.dataclass(frozen=True)
85
+ class StrToKeywordsKeyword(Keyword, lang.Abstract):
86
+ m: ta.Mapping[str, Keywords]
@@ -0,0 +1,26 @@
1
+ from omlish import lang
2
+
3
+ from .base import Keyword
4
+ from .base import StrKeyword
5
+
6
+
7
+ ##
8
+
9
+
10
+ class CoreKeyword(Keyword, lang.Abstract):
11
+ pass
12
+
13
+
14
+ ##
15
+
16
+
17
+ class Id(StrKeyword, CoreKeyword, lang.Final, tag='$id'):
18
+ pass
19
+
20
+
21
+ class SchemaKeyword(StrKeyword, CoreKeyword, lang.Final, tag='$schema'):
22
+ pass
23
+
24
+
25
+ class Ref(StrKeyword, CoreKeyword, lang.Final, tag='$ref'):
26
+ pass
@@ -0,0 +1,22 @@
1
+ from omlish import lang
2
+
3
+ from .base import Keyword
4
+ from .base import StrKeyword
5
+
6
+
7
+ ##
8
+
9
+
10
+ class MetadataKeyword(Keyword, lang.Abstract):
11
+ pass
12
+
13
+
14
+ ##
15
+
16
+
17
+ class Title(StrKeyword, MetadataKeyword, lang.Final, tag='title'):
18
+ pass
19
+
20
+
21
+ class Description(StrKeyword, MetadataKeyword, lang.Final, tag='description'):
22
+ pass
@@ -0,0 +1,69 @@
1
+ import operator
2
+ import typing as ta
3
+
4
+ from omlish import check
5
+ from omlish import collections as col
6
+ from omlish import lang
7
+
8
+ from . import core # noqa
9
+ from . import metadata # noqa
10
+ from . import validation # noqa
11
+ from .base import BooleanKeyword
12
+ from .base import Keyword
13
+ from .base import Keywords
14
+ from .base import KeywordsKeyword
15
+ from .base import NumberKeyword
16
+ from .base import StrKeyword
17
+ from .base import StrOrStrsKeyword
18
+ from .base import StrToKeywordsKeyword
19
+
20
+
21
+ KeywordT = ta.TypeVar('KeywordT', bound=Keyword)
22
+
23
+
24
+ ##
25
+
26
+
27
+ KEYWORD_TYPES_BY_TAG: ta.Mapping[str, type[Keyword]] = col.unique_map_by( # noqa
28
+ operator.attrgetter('tag'),
29
+ (cls for cls in lang.deep_subclasses(Keyword) if not lang.is_abstract_class(cls)),
30
+ strict=True,
31
+ )
32
+
33
+
34
+ def parse_keyword(cls: type[KeywordT], v: ta.Any) -> KeywordT:
35
+ if issubclass(cls, BooleanKeyword):
36
+ return cls(check.isinstance(v, bool)) # type: ignore
37
+
38
+ elif issubclass(cls, NumberKeyword):
39
+ return cls(check.isinstance(v, (int, float))) # type: ignore
40
+
41
+ elif issubclass(cls, StrKeyword):
42
+ return cls(check.isinstance(v, str)) # type: ignore
43
+
44
+ elif issubclass(cls, StrOrStrsKeyword):
45
+ ss: str | ta.Sequence[str]
46
+ if isinstance(v, str):
47
+ ss = v
48
+ elif isinstance(v, ta.Iterable):
49
+ ss = col.seq_of(check.of_isinstance(str))(v)
50
+ else:
51
+ raise TypeError(v)
52
+ return cls(ss) # type: ignore
53
+
54
+ elif issubclass(cls, KeywordsKeyword):
55
+ return cls(parse_keywords(v)) # type: ignore
56
+
57
+ elif issubclass(cls, StrToKeywordsKeyword):
58
+ return cls({k: parse_keywords(mv) for k, mv in v.items()}) # type: ignore
59
+
60
+ else:
61
+ raise TypeError(cls)
62
+
63
+
64
+ def parse_keywords(dct: ta.Mapping[str, ta.Any]) -> Keywords:
65
+ lst: list[Keyword] = []
66
+ for k, v in dct.items():
67
+ cls = KEYWORD_TYPES_BY_TAG[k]
68
+ lst.append(parse_keyword(cls, v))
69
+ return Keywords(lst)
@@ -0,0 +1,47 @@
1
+ import typing as ta
2
+
3
+ from .base import BooleanKeyword
4
+ from .base import Keyword
5
+ from .base import Keywords
6
+ from .base import KeywordsKeyword
7
+ from .base import NumberKeyword
8
+ from .base import StrKeyword
9
+ from .base import StrOrStrsKeyword
10
+ from .base import StrToKeywordsKeyword
11
+
12
+
13
+ def render_keyword(kw: Keyword) -> dict[str, ta.Any]:
14
+ if isinstance(kw, BooleanKeyword):
15
+ return {kw.tag: kw.b}
16
+
17
+ elif isinstance(kw, NumberKeyword):
18
+ return {kw.tag: kw.n}
19
+
20
+ elif isinstance(kw, StrKeyword):
21
+ return {kw.tag: kw.s}
22
+
23
+ elif isinstance(kw, StrOrStrsKeyword):
24
+ if isinstance(kw.ss, str):
25
+ return {kw.tag: kw.ss}
26
+ else:
27
+ return {kw.tag: list(kw.ss)}
28
+
29
+ elif isinstance(kw, KeywordsKeyword):
30
+ return {kw.tag: render_keywords(kw.kw)}
31
+
32
+ elif isinstance(kw, StrToKeywordsKeyword):
33
+ return {kw.tag: {k: render_keywords(v) for k, v in kw.m.items()}}
34
+
35
+ else:
36
+ raise TypeError(kw)
37
+
38
+
39
+ def render_keywords(kws: Keywords) -> dict[str, ta.Any]:
40
+ dct: dict[str, ta.Any] = {}
41
+ for kw in kws.lst:
42
+ kwd = render_keyword(kw)
43
+ [(tag, val)] = kwd.items()
44
+ if tag in dct:
45
+ raise KeyError(tag)
46
+ dct[tag] = val
47
+ return dct
@@ -0,0 +1,68 @@
1
+ from omlish import lang
2
+
3
+ from .base import BooleanKeyword
4
+ from .base import Keyword
5
+ from .base import KeywordsKeyword
6
+ from .base import NumberKeyword
7
+ from .base import StrOrStrsKeyword
8
+ from .base import StrToKeywordsKeyword
9
+
10
+
11
+ ##
12
+
13
+
14
+ class ValidationKeyword(Keyword, lang.Abstract):
15
+ pass
16
+
17
+
18
+ ##
19
+
20
+
21
+ class Type(StrOrStrsKeyword, ValidationKeyword, lang.Final, tag='type'):
22
+ pass
23
+
24
+
25
+ class Items(KeywordsKeyword, ValidationKeyword, lang.Final, tag='items'):
26
+ pass
27
+
28
+
29
+ class Required(StrOrStrsKeyword, ValidationKeyword, lang.Final, tag='required'):
30
+ pass
31
+
32
+
33
+ class Properties(StrToKeywordsKeyword, ValidationKeyword, lang.Final, tag='properties'):
34
+ pass
35
+
36
+
37
+ ##
38
+
39
+
40
+ class MaxItems(NumberKeyword, ValidationKeyword, lang.Final, tag='maxItems'):
41
+ pass
42
+
43
+
44
+ class MinItems(NumberKeyword, ValidationKeyword, lang.Final, tag='minItems'):
45
+ pass
46
+
47
+
48
+ class UniqueItems(BooleanKeyword, ValidationKeyword, lang.Final, tag='uniqueItems'):
49
+ pass
50
+
51
+
52
+ #
53
+
54
+
55
+ class Maximum(NumberKeyword, ValidationKeyword, lang.Final, tag='maximum'):
56
+ pass
57
+
58
+
59
+ class ExclusiveMaximum(NumberKeyword, ValidationKeyword, lang.Final, tag='exclusiveMaximum'):
60
+ pass
61
+
62
+
63
+ class Minimum(NumberKeyword, ValidationKeyword, lang.Final, tag='minimum'):
64
+ pass
65
+
66
+
67
+ class ExclusiveMinimum(NumberKeyword, ValidationKeyword, lang.Final, tag='exclusiveMinimum'):
68
+ pass
File without changes
@@ -0,0 +1,21 @@
1
+ import enum
2
+ import typing as ta
3
+
4
+
5
+ class JsonType(enum.Enum):
6
+ NULL = enum.auto()
7
+ BOOLEAN = enum.auto()
8
+ OBJECT = enum.auto()
9
+ ARRAY = enum.auto()
10
+ NUMBER = enum.auto()
11
+ STRING = enum.auto()
12
+
13
+
14
+ TYPE_SETS_BY_JSON_TYPE: ta.Mapping[JsonType, ta.AbstractSet[type]] = {
15
+ JsonType.NULL: {type(None)},
16
+ JsonType.BOOLEAN: {bool},
17
+ JsonType.OBJECT: {dict},
18
+ JsonType.ARRAY: {list},
19
+ JsonType.NUMBER: {int, float},
20
+ JsonType.STRING: {str},
21
+ }