omlish 0.0.0.dev19__py3-none-any.whl → 0.0.0.dev20__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 (32) 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/metadata.py +2 -1
  5. omlish/dataclasses/utils.py +45 -0
  6. omlish/fnpairs.py +1 -1
  7. omlish/formats/json.py +1 -0
  8. omlish/lang/descriptors.py +2 -0
  9. omlish/lite/logs.py +2 -2
  10. omlish/marshal/__init__.py +4 -0
  11. omlish/marshal/dataclasses.py +16 -3
  12. omlish/marshal/helpers.py +22 -0
  13. omlish/marshal/objects.py +33 -14
  14. omlish/multiprocessing.py +32 -0
  15. omlish/specs/__init__.py +0 -0
  16. omlish/specs/jsonschema/__init__.py +0 -0
  17. omlish/specs/jsonschema/keywords/__init__.py +42 -0
  18. omlish/specs/jsonschema/keywords/base.py +86 -0
  19. omlish/specs/jsonschema/keywords/core.py +26 -0
  20. omlish/specs/jsonschema/keywords/metadata.py +22 -0
  21. omlish/specs/jsonschema/keywords/parse.py +69 -0
  22. omlish/specs/jsonschema/keywords/render.py +47 -0
  23. omlish/specs/jsonschema/keywords/validation.py +68 -0
  24. omlish/specs/jsonschema/schemas/__init__.py +0 -0
  25. omlish/specs/jsonschema/schemas/draft202012/__init__.py +0 -0
  26. omlish/specs/jsonschema/schemas/draft202012/vocabularies/__init__.py +0 -0
  27. omlish/specs/jsonschema/types.py +10 -0
  28. {omlish-0.0.0.dev19.dist-info → omlish-0.0.0.dev20.dist-info}/METADATA +3 -3
  29. {omlish-0.0.0.dev19.dist-info → omlish-0.0.0.dev20.dist-info}/RECORD +32 -18
  30. {omlish-0.0.0.dev19.dist-info → omlish-0.0.0.dev20.dist-info}/LICENSE +0 -0
  31. {omlish-0.0.0.dev19.dist-info → omlish-0.0.0.dev20.dist-info}/WHEEL +0 -0
  32. {omlish-0.0.0.dev19.dist-info → omlish-0.0.0.dev20.dist-info}/top_level.txt +0 -0
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev19'
2
- __revision__ = '509bb451054d2d955e55cbcf1f271a9166a904eb'
1
+ __version__ = '0.0.0.dev20'
2
+ __revision__ = '56a3421b375112818e306f2c17d467c7aac1d26b'
3
3
 
4
4
 
5
5
  #
@@ -88,7 +88,7 @@ class Project(ProjectBase):
88
88
  'sqlx': [
89
89
  'sqlean.py ~= 3.45; python_version < "3.13"',
90
90
 
91
- 'duckdb ~= 1.0',
91
+ 'duckdb ~= 1.1',
92
92
  ],
93
93
 
94
94
  'testing': [
omlish/asyncs/anyio.py CHANGED
@@ -23,6 +23,7 @@ async def killer(shutdown: anyio.Event, sleep_s: float) -> None:
23
23
  shutdown.set()
24
24
 
25
25
  """ # noqa
26
+ import dataclasses as dc
26
27
  import signal
27
28
  import typing as ta
28
29
 
@@ -44,6 +45,12 @@ StapledByteStream: ta.TypeAlias = anyio.streams.stapled.StapledByteStream
44
45
  StapledObjectStream: ta.TypeAlias = anyio.streams.stapled.StapledObjectStream
45
46
 
46
47
 
48
+ @dc.dataclass(eq=False)
49
+ class MemoryStapledObjectStream(StapledObjectStream[T]):
50
+ send_stream: MemoryObjectSendStream[T]
51
+ receive_stream: MemoryObjectReceiveStream[T]
52
+
53
+
47
54
  ##
48
55
 
49
56
 
@@ -143,8 +150,8 @@ def split_memory_object_streams(
143
150
  return tup
144
151
 
145
152
 
146
- def create_stapled_memory_object_stream(max_buffer_size: float = 0) -> StapledObjectStream:
147
- return StapledObjectStream(*anyio.create_memory_object_stream(max_buffer_size))
153
+ def create_stapled_memory_object_stream(max_buffer_size: float = 0) -> MemoryStapledObjectStream:
154
+ return MemoryStapledObjectStream(*anyio.create_memory_object_stream(max_buffer_size))
148
155
 
149
156
 
150
157
  # FIXME: https://github.com/python/mypy/issues/15238
@@ -158,9 +165,9 @@ def create_memory_object_stream[T](max_buffer_size: float = 0) -> tuple[
158
165
 
159
166
  def staple_memory_object_stream(
160
167
  *args: anyio.create_memory_object_stream[T],
161
- ) -> StapledObjectStream[T]:
168
+ ) -> MemoryStapledObjectStream[T]:
162
169
  send, receive = args
163
- return StapledObjectStream(
170
+ return MemoryStapledObjectStream(
164
171
  check.isinstance(send, MemoryObjectSendStream), # type: ignore
165
172
  check.isinstance(receive, MemoryObjectReceiveStream), # type: ignore
166
173
  )
@@ -168,9 +175,9 @@ def staple_memory_object_stream(
168
175
 
169
176
  # FIXME: https://github.com/python/mypy/issues/15238
170
177
  # FIXME: https://youtrack.jetbrains.com/issues?q=tag:%20%7BPEP%20695%7D
171
- def staple_memory_object_stream2[T](max_buffer_size: float = 0) -> StapledObjectStream[T]:
178
+ def staple_memory_object_stream2[T](max_buffer_size: float = 0) -> MemoryStapledObjectStream[T]:
172
179
  send, receive = anyio.create_memory_object_stream[T](max_buffer_size)
173
- return StapledObjectStream(
180
+ return MemoryStapledObjectStream(
174
181
  check.isinstance(send, MemoryObjectSendStream), # type: ignore
175
182
  check.isinstance(receive, MemoryObjectReceiveStream), # type: ignore
176
183
  )
@@ -96,4 +96,6 @@ from .utils import ( # noqa
96
96
  opt_repr,
97
97
  update_field_extras,
98
98
  update_field_metadata,
99
+ update_fields,
100
+ update_fields_metadata,
99
101
  )
@@ -70,5 +70,6 @@ class Init(lang.Marker):
70
70
  pass
71
71
 
72
72
 
73
- def init(fn: ta.Callable[..., None]) -> None:
73
+ def init(fn: ta.Callable):
74
74
  _append_cls_md(Init, fn)
75
+ return fn
@@ -12,6 +12,9 @@ from .impl.params import get_field_extras
12
12
  T = ta.TypeVar('T')
13
13
 
14
14
 
15
+ #
16
+
17
+
15
18
  def maybe_post_init(sup: ta.Any) -> bool:
16
19
  try:
17
20
  fn = sup.__post_init__
@@ -21,10 +24,16 @@ def maybe_post_init(sup: ta.Any) -> bool:
21
24
  return True
22
25
 
23
26
 
27
+ #
28
+
29
+
24
30
  def opt_repr(o: ta.Any) -> str | None:
25
31
  return repr(o) if o is not None else None
26
32
 
27
33
 
34
+ #
35
+
36
+
28
37
  class field_modifier: # noqa
29
38
  def __init__(self, fn: ta.Callable[[dc.Field], dc.Field]) -> None:
30
39
  super().__init__()
@@ -58,6 +67,42 @@ def update_field_extras(f: dc.Field, *, unless_non_default: bool = False, **kwar
58
67
  })
59
68
 
60
69
 
70
+ def update_fields(
71
+ fn: ta.Callable[[str, dc.Field], dc.Field],
72
+ fields: ta.Iterable[str] | None = None,
73
+ ) -> ta.Callable[[type[T]], type[T]]:
74
+ def inner(cls):
75
+ if fields is None:
76
+ for a, v in list(cls.__dict__.items()):
77
+ if isinstance(v, dc.Field):
78
+ setattr(cls, a, fn(a, v))
79
+
80
+ else:
81
+ for a in fields:
82
+ v = cls.__dict__[a]
83
+ if not isinstance(v, dc.Field):
84
+ v = dc.field(default=v)
85
+ setattr(cls, a, fn(a, v))
86
+
87
+ return cls
88
+
89
+ check.not_isinstance(fields, str)
90
+ return inner
91
+
92
+
93
+ def update_fields_metadata(
94
+ nmd: ta.Mapping,
95
+ fields: ta.Iterable[str] | None = None,
96
+ ) -> ta.Callable[[type[T]], type[T]]:
97
+ def inner(a: str, f: dc.Field) -> dc.Field:
98
+ return update_field_metadata(f, nmd)
99
+
100
+ return update_fields(inner, fields)
101
+
102
+
103
+ #
104
+
105
+
61
106
  def deep_replace(o: T, *args: str | ta.Callable[[ta.Any], ta.Mapping[str, ta.Any]]) -> T:
62
107
  if not args:
63
108
  return o
omlish/fnpairs.py CHANGED
@@ -449,7 +449,7 @@ class Toml(ObjectStr_):
449
449
  #
450
450
 
451
451
 
452
- @_register_extension('cpkl')
452
+ @_register_extension('clpkl')
453
453
  @dc.dataclass(frozen=True)
454
454
  class Cloudpickle(ObjectBytes_):
455
455
  protocol: int | None = None
omlish/formats/json.py CHANGED
@@ -13,6 +13,7 @@ from .. import lang
13
13
  if ta.TYPE_CHECKING:
14
14
  import orjson as _orjson
15
15
  import ujson as _ujson
16
+
16
17
  else:
17
18
  _orjson = lang.proxy_import('orjson')
18
19
  _ujson = lang.proxy_import('ujson')
@@ -125,11 +125,13 @@ class _decorator_descriptor: # noqa
125
125
 
126
126
  def __get__(self, instance, owner=None):
127
127
  fn = self._fn.__get__(instance, owner)
128
+
128
129
  if self._md or instance is not None:
129
130
  @functools.wraps(fn)
130
131
  def inner(*args, **kwargs):
131
132
  return self._wrapper(fn, *args, **kwargs)
132
133
  return inner
134
+
133
135
  else:
134
136
  @functools.wraps(fn)
135
137
  def outer(this, *args, **kwargs):
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'),
@@ -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,6 +19,32 @@ 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
50
  fds: ta.AbstractSet[int] | None = None
@@ -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,10 @@
1
+ import enum
2
+
3
+
4
+ class JsonType(enum.Enum):
5
+ NULL = enum.auto()
6
+ BOOLEAN = enum.auto()
7
+ OBJECT = enum.auto()
8
+ ARRAY = enum.auto()
9
+ NUMBER = enum.auto()
10
+ STRING = enum.auto()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev19
3
+ Version: 0.0.0.dev20
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -31,7 +31,7 @@ Requires-Dist: pg8000 ~=1.31 ; extra == 'all'
31
31
  Requires-Dist: pymysql ~=1.1 ; extra == 'all'
32
32
  Requires-Dist: aiomysql ~=0.2 ; extra == 'all'
33
33
  Requires-Dist: aiosqlite ~=0.20 ; extra == 'all'
34
- Requires-Dist: duckdb ~=1.0 ; extra == 'all'
34
+ Requires-Dist: duckdb ~=1.1 ; extra == 'all'
35
35
  Requires-Dist: pytest ~=8.0 ; extra == 'all'
36
36
  Requires-Dist: greenlet ~=3.0 ; (python_version < "3.13") and extra == 'all'
37
37
  Requires-Dist: trio-asyncio ~=0.15 ; (python_version < "3.13") and extra == 'all'
@@ -73,7 +73,7 @@ Requires-Dist: sqlalchemy[asyncio] ~=2.0 ; (python_version < "3.13") and extra =
73
73
  Requires-Dist: asyncpg ~=0.29 ; (python_version < "3.13") and extra == 'sql'
74
74
  Requires-Dist: sqlalchemy ~=2.0 ; (python_version ~= "3.13") and extra == 'sql'
75
75
  Provides-Extra: sqlx
76
- Requires-Dist: duckdb ~=1.0 ; extra == 'sqlx'
76
+ Requires-Dist: duckdb ~=1.1 ; extra == 'sqlx'
77
77
  Requires-Dist: sqlean.py ~=3.45 ; (python_version < "3.13") and extra == 'sqlx'
78
78
  Provides-Extra: testing
79
79
  Requires-Dist: pytest ~=8.0 ; extra == 'testing'
@@ -1,4 +1,4 @@
1
- omlish/__about__.py,sha256=kx3qJfIi26XfEn1ZQ0smsjstKTHlUyhhgu2INCz-Mw4,2451
1
+ omlish/__about__.py,sha256=aaPwsrPbNAl3zQVkQ9n1ZeBGXbeIaqkXe-qJK-YhMn4,2451
2
2
  omlish/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  omlish/argparse.py,sha256=QRQmX9G0-L_nATkFtGHvpd4qrpYzKATdjuFLbBqzJPM,6224
4
4
  omlish/c3.py,sha256=W5EwYx9Por3rWYLkKUitJ6OoRMLLgVTfLTyroOz41Y0,8047
@@ -9,19 +9,19 @@ omlish/datetimes.py,sha256=HajeM1kBvwlTa-uR1TTZHmZ3zTPnnUr1uGGQhiO1XQ0,2152
9
9
  omlish/defs.py,sha256=T3bq_7h_tO3nDB5RAFBn7DkdeQgqheXzkFColbOHZko,4890
10
10
  omlish/docker.py,sha256=5WyXJyFwqIJJ11QWwPIjHjDHnsaOVZszZAjyTvg3xew,4693
11
11
  omlish/dynamic.py,sha256=35C_cCX_Vq2HrHzGk5T-zbrMvmUdiIiwDzDNixczoDo,6541
12
- omlish/fnpairs.py,sha256=tKX991ICOtBgWJrZsuSVryzVOmNkCobwUXyk4uNpsCY,10600
12
+ omlish/fnpairs.py,sha256=hVuLqQFdRNNze3FYH2cAQO3GC7nK5yQP_GWPUSbL7nE,10601
13
13
  omlish/iterators.py,sha256=GGLC7RIT86uXMjhIIIqnff_Iu5SI_b9rXYywYGFyzmo,7292
14
14
  omlish/libc.py,sha256=u0481imCiTFqP_e-v9g0pD-0WD249j5vYzhtn-fnNkY,15308
15
15
  omlish/matchfns.py,sha256=o2evI7q0CAMHR8RQ_Jks6L0UoNpEDltnLjOiamJDtmU,6155
16
16
  omlish/math.py,sha256=AVqp5Y8yxKA-wO0BgrzaxA0Ga3PZiCXnYcwivMneC-0,3804
17
- omlish/multiprocessing.py,sha256=J5ywVOl6TcWXCzgmnNUrD-gaXLZd9ozLTqfz66jiCfs,4685
17
+ omlish/multiprocessing.py,sha256=Jbcr71p_HlsM5rIFl9obzSv9TCusPhxciwpBXLTYpmc,5179
18
18
  omlish/os.py,sha256=cz4nL2ujaxH_-XRq3JUD8af8mSe1JXGPIoXP9XAEd0M,2607
19
19
  omlish/runmodule.py,sha256=PWvuAaJ9wQQn6bx9ftEL3_d04DyotNn8dR_twm2pgw0,700
20
20
  omlish/stats.py,sha256=uqjN-focDVssFZMagj22HqmyJ1TBO4Wt-XnHp8-EtVw,9927
21
21
  omlish/sync.py,sha256=AqwIfIuCMVHLwlJUa7dmaSjfA4sM5AYPCD5-nsz3XVQ,1516
22
22
  omlish/term.py,sha256=NEmxqAhicyInGtmFamZAizI2xdu819MzFYPEp0Fx97M,6111
23
23
  omlish/asyncs/__init__.py,sha256=uUz9ziKh4_QrgmdhKFMgq6j7mFbiZd3LiogguDCQsGI,587
24
- omlish/asyncs/anyio.py,sha256=6AruTkEXa3e4G1jgxfkYE36dQu8ihbh13u4up7OUW5k,7788
24
+ omlish/asyncs/anyio.py,sha256=Hqdi1iCopKoaAWGx-AYTRLEwnavLWx1esfJISK1IVF0,8024
25
25
  omlish/asyncs/asyncio.py,sha256=JfM59QgB3asgEbrps0zoVbNjWD4kL2XdsEkRMEIoFos,971
26
26
  omlish/asyncs/asyncs.py,sha256=Tf7ZodTGepkM7HAuFcDNh9lLzzrMw6rELWvopGmFkh4,2035
27
27
  omlish/asyncs/bridge.py,sha256=AabrRVz5k75dTB59M70DWkl6JrLusjhpvsaj5jld9io,8151
@@ -60,8 +60,8 @@ omlish/configs/__init__.py,sha256=3uh09ezodTwkMI0nRmAMP0eEuJ_0VdF-LYyNmPjHiCE,77
60
60
  omlish/configs/classes.py,sha256=GLbB8xKjHjjoUQRCUQm3nEjM8z1qNTx9gPV7ODSt5dg,1317
61
61
  omlish/configs/flattening.py,sha256=AOlRpBHm449MxwMp3CiIRGunStOC1DUNs1f3CLou0wc,4731
62
62
  omlish/configs/strings.py,sha256=0brx1duL85r1GpfbNvbHcSvH4jWzutwuvMFXda9NeI0,2651
63
- omlish/dataclasses/__init__.py,sha256=FMR2hCAsOxRTjT2asnuyEGkA4OXI7J3N8Wy7cyPiogs,1247
64
- omlish/dataclasses/utils.py,sha256=G1nSlvdwsb9SV1AsJRfXrCO0sAnqMcj7LjGdVSWi2AA,1852
63
+ omlish/dataclasses/__init__.py,sha256=pnIEFM-2aWoN9yLLThAzVJUT7YzdGaAcfqWUVGF9WDk,1294
64
+ omlish/dataclasses/utils.py,sha256=G5kRFc6QPNQLdehsdH-2m6Fjba_k1OFjx72zSHtfccI,2781
65
65
  omlish/dataclasses/impl/__init__.py,sha256=rQJRcE9fVsGEvRUOZ18r4DE0xiLbSRjspyJRXgbLS3I,348
66
66
  omlish/dataclasses/impl/api.py,sha256=NOuw5dlwztG5AA_gwdF2PJAqvouMdbUf1Si80G26qAY,6244
67
67
  omlish/dataclasses/impl/as_.py,sha256=CD-t7hkC1EP2F_jvZKIA_cVoDuwZ-Ln_xC4fJumPYX0,2598
@@ -75,7 +75,7 @@ omlish/dataclasses/impl/init.py,sha256=yw9iInFHaR_TFWRzsryr8vgStHMQwqubL-s7pY5k1
75
75
  omlish/dataclasses/impl/internals.py,sha256=LTCqGT8AhyGTWwioGrBpTJzDzPvAtizQKb0NBNKcNs0,2989
76
76
  omlish/dataclasses/impl/main.py,sha256=vAVdqb4cgvyssA7rCFl9i2D0MAffX45YtQbGQ2te5_c,5189
77
77
  omlish/dataclasses/impl/metaclass.py,sha256=dlQEIN9MHBirll7Nx3StpzxYxXjrqxJ-QsorMcCNt7w,2828
78
- omlish/dataclasses/impl/metadata.py,sha256=HGqAtMcP422X4kPx93btcxE-n4guBFm6-N-S4nobgk4,1407
78
+ omlish/dataclasses/impl/metadata.py,sha256=binwdG0cyKdGEypRKYTbcDyH_be4aTaSnVnK2V5rwB8,1402
79
79
  omlish/dataclasses/impl/order.py,sha256=zWvWDkSTym8cc7vO1cLHqcBhhjOlucHOCUVJcdh4jt0,1369
80
80
  omlish/dataclasses/impl/params.py,sha256=M-xg9IeFftVy795oqlp7Yw8jE-7wG2K4vaFhXHKmL1k,2538
81
81
  omlish/dataclasses/impl/processing.py,sha256=iUvaNwRAt8rQsLIIkvRye5rfk6xhgR35EbcdwZCZec8,366
@@ -103,7 +103,7 @@ omlish/dispatch/functions.py,sha256=S8ElsLi6DKxTdtFGigWaF0vAquwy2sK-3f4iRLaYq70,
103
103
  omlish/dispatch/methods.py,sha256=XHjwwC9Gn4iDWxbyLAcbdSwRgVaq-8Bnn5cAwf5oZdA,5403
104
104
  omlish/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
105
105
  omlish/formats/dotenv.py,sha256=UjZl3gac-0U24sDjCCGMcCqO1UCWG2Zs8PZ4JdAg2YE,17348
106
- omlish/formats/json.py,sha256=SgPpt-j0yLQDJaw_TkF471G2NrJPiTkhl8_Src81HGg,1016
106
+ omlish/formats/json.py,sha256=61XG6rveb3SSXmYrKvUmRdaVDyMD6C-7yVqXBBMu8t8,1017
107
107
  omlish/formats/props.py,sha256=diYjZDsG1s50ImJhkpeinMwjr8952nIVI-0gYhBIvCY,18897
108
108
  omlish/formats/yaml.py,sha256=5gvH-e9fVn6C-xpcagnrtM0bSLOsuk3sAQT-aaE4PD4,6653
109
109
  omlish/graphs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -159,7 +159,7 @@ omlish/lang/cached.py,sha256=0gjdxVELu69oRQ3kqSV3cGIHg6Nf4pcCIIRTEU52tCc,7607
159
159
  omlish/lang/clsdct.py,sha256=LXwLCULeI_8Np-7-pZkyNAHpUZLcQiBEQiHwKYQ0WRo,1742
160
160
  omlish/lang/cmp.py,sha256=5vbzWWbqdzDmNKAGL19z6ZfUKe5Ci49e-Oegf9f4BsE,1346
161
161
  omlish/lang/contextmanagers.py,sha256=C6FU_1ftMvp_Zbz9ixf_HsCSYQot_TZ7mpZBJEBc3Xc,8333
162
- omlish/lang/descriptors.py,sha256=taJzRU-lKszfaIH847UEE9Kb_krhCqPGYdu7CTBXr5Q,5900
162
+ omlish/lang/descriptors.py,sha256=J0oRWys9smHp9UYeearBATA7u-6t5pDnQgPfWiXN3B4,5902
163
163
  omlish/lang/exceptions.py,sha256=qJBo3NU1mOWWm-NhQUHCY5feYXR3arZVyEHinLsmRH4,47
164
164
  omlish/lang/functions.py,sha256=T4nPl46EHHGjMkz3FTRjsVhS9Y8HKcwM0jROU6_-Rv0,3619
165
165
  omlish/lang/imports.py,sha256=04ugFC8NI5sbL7NH4V0r0q_nFsP_AMkHLz697CVkMtQ,6274
@@ -189,7 +189,7 @@ omlish/lite/cached.py,sha256=dUm647FbIsoxWT23XUFM51F7i-C2Buxr5b5zzgbCtQI,546
189
189
  omlish/lite/check.py,sha256=DR3Zj-7o4Y7pNheln68nN_BdX9zaotGQ2y8v97GDiWQ,535
190
190
  omlish/lite/contextmanagers.py,sha256=HnQJiyrOmSvTL22XRJrFl5CLpCyHD9fsntEUAr9G-60,427
191
191
  omlish/lite/json.py,sha256=7-02Ny4fq-6YAu5ynvqoijhuYXWpLmfCI19GUeZnb1c,740
192
- omlish/lite/logs.py,sha256=Sd5Uw_napxHpq4S-8OYgRdMYqECL7OWicQXsfCjdEuo,2646
192
+ omlish/lite/logs.py,sha256=qgP0pNjkoG5gjQ6xuXM7qpsLwZX3WbS7WaZlDqQKry4,2641
193
193
  omlish/lite/marshal.py,sha256=5uwri-KzPiktnbYORkGXcJ4kulZvp_nS4MxPsU1Y-G0,8608
194
194
  omlish/lite/reflect.py,sha256=9QYJwdINraq1JNMEgvoqeSlVvRRgOXpxAkpgX8EgRXc,1307
195
195
  omlish/lite/runtime.py,sha256=VUhmNQvwf8QzkWSKj4Q0ReieJA_PzHaJNRBivfTseow,452
@@ -202,23 +202,24 @@ omlish/logs/configs.py,sha256=VfZjhyA4CeMYNhlsv5oD2IZ6Iv4d_lbUgZzcnLAkxNA,1052
202
202
  omlish/logs/formatters.py,sha256=AFs9C6-qrFkgXZ0nL39wih_LGck1Tc79alvGyibBdQo,720
203
203
  omlish/logs/noisy.py,sha256=8JORjI1dH38yU2MddM54OB6qt32Xozfocdb88vY4wro,335
204
204
  omlish/logs/utils.py,sha256=MgGovbP0zUrZ3FGD3qYNQWn-l0jy0Y0bStcQvv5BOmQ,391
205
- omlish/marshal/__init__.py,sha256=ggU_UVW-CyXZnsaGwx-Cxj4PUAY0QsXmaXVLFO8T4rQ,1453
205
+ omlish/marshal/__init__.py,sha256=Co5UUlCyd3eDToQTOvTURmCICKzuULnR5B6ylgb2nFM,1515
206
206
  omlish/marshal/any.py,sha256=e82OyYK3Emm1P1ClnsnxP7fIWC2iNVyW0H5nK4mLmWM,779
207
207
  omlish/marshal/base.py,sha256=EIgrqsQ1OQ4mVUMuDH5zRBCwJpn8ijVS98Nmoka_Mrs,6025
208
208
  omlish/marshal/base64.py,sha256=Q3ibujdhgFgDaeHahSe7WdcqvOyalWigwUlV-U-2ckQ,1018
209
- omlish/marshal/dataclasses.py,sha256=PMAwBblcmCNVifDul0Vl09saBZ3BYEOIOJABZ2zc4Bg,3667
209
+ omlish/marshal/dataclasses.py,sha256=cqHfzkQe7T-A_eAx-xZm6AIOZLnqE-alMUXMtRTYxuI,3910
210
210
  omlish/marshal/datetimes.py,sha256=0ffg8cEvx9SMKIXZGD9b7MqpLfmgw0uKKdn6YTfoqok,3714
211
211
  omlish/marshal/enums.py,sha256=-0fKutBbyz8ygEaA0_P_8IOJrI9jMGigmnPbutV9Bg4,1464
212
212
  omlish/marshal/exceptions.py,sha256=jwQWn4LcPnadT2KRI_1JJCOSkwWh0yHnYK9BmSkNN4U,302
213
213
  omlish/marshal/factories.py,sha256=UV2Svjok-lTWsRqKGh-CeuAhvlohw9uJe7ZLyoKMvTM,2968
214
214
  omlish/marshal/forbidden.py,sha256=BNshzm4lN5O8sUZ1YvxrSYq3WPklq9NMQCRZ7RC3DLM,865
215
215
  omlish/marshal/global_.py,sha256=8XCjPcIjA65StESshzNfQiSyuckVaEiiWROmC3qk0mo,1117
216
+ omlish/marshal/helpers.py,sha256=L9U40zeo4pKsNtohKaLHHiGz-aitOFQI1Xwii1ZsLOE,545
216
217
  omlish/marshal/iterables.py,sha256=6I_ZdJemLSQtJ4J5NrB9wi-eyxiJZS61HzHXp1yeiX8,2592
217
218
  omlish/marshal/mappings.py,sha256=zhLtyot7tzQtBNj7C4RBxjMELxA5r2q2Mth8Br7xkFs,2803
218
219
  omlish/marshal/maybes.py,sha256=tKkVsJATERgbVcEfBnsHBK_2_LCQIVyBzca-cA-9KH0,2112
219
220
  omlish/marshal/naming.py,sha256=UCviMAXTTUpW1lyAGymybGP2rFUAW44P1X0zrIVbvi4,464
220
221
  omlish/marshal/numbers.py,sha256=oY_yMNJEnJhjfLh89gpPXvKqeUyhQcaTcQB6ecyHiG8,1704
221
- omlish/marshal/objects.py,sha256=JD7GBY1hYUyRpCufpxDAwgII98ewzGDct2IF0HxgFN0,2767
222
+ omlish/marshal/objects.py,sha256=T3prlm0HESpOLXHWcdsYekfmythJRZQOvaoL0is8J_o,3097
222
223
  omlish/marshal/optionals.py,sha256=r0XB5rqfasvgZJNrKYd6Unq2U4nHt3JURi26j0dYHlw,1499
223
224
  omlish/marshal/polymorphism.py,sha256=gyvNYUAkmQVhWrcXBLzXINxqx6RHyulf9n16Iv38PFI,5597
224
225
  omlish/marshal/primitives.py,sha256=-gLR_RTPTM8YP6t6PL5sNM1lAtwcZDHc9lxkimoQ9jw,1097
@@ -239,6 +240,19 @@ omlish/secrets/openssl.py,sha256=nAA_wxk86G92B7027AoAlAiFliMjxLVs7xlaOAFGayE,622
239
240
  omlish/secrets/passwords.py,sha256=3r-vEK6Gp6aq4L5Csnd06QnrjO9xfzHJP-g_7I9W_ao,4101
240
241
  omlish/secrets/secrets.py,sha256=hFN82uYiBVx8YSE86leWNxb4IRp3qdwZPOi4w04h8u0,6855
241
242
  omlish/secrets/subprocesses.py,sha256=EcnKlHHtnUMHGrBWXDfu8tv28wlgZx4P4GOiuPW9Vo8,1105
243
+ omlish/specs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
244
+ omlish/specs/jsonschema/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
245
+ omlish/specs/jsonschema/types.py,sha256=aJrZq2oqjmbGa32YnWyGFPbEyNfm0YO9ixNy7qU-snE,189
246
+ omlish/specs/jsonschema/keywords/__init__.py,sha256=Zt2g1BXd654uU2AQ5P7_-x2Wrtf6cNbP9mxI1wGN4wo,596
247
+ omlish/specs/jsonschema/keywords/base.py,sha256=HA4OvsdvAl4o5JgJI_tuaHBGURjxT5QeFN6_ja5Z5xQ,1957
248
+ omlish/specs/jsonschema/keywords/core.py,sha256=irJ5Rreg9hTeXu3FovSdzhFb3vUly-hYQVYflEgST3g,368
249
+ omlish/specs/jsonschema/keywords/metadata.py,sha256=55jlunXosdcM1IQ256KfUIEwFzDPah448ZOeQhJE1c8,316
250
+ omlish/specs/jsonschema/keywords/parse.py,sha256=3XzHE5gN6usVUMw2DrvbNZrGDYdM8EefVyfQ0cEMKEA,1936
251
+ omlish/specs/jsonschema/keywords/render.py,sha256=enGkw1S8Zxfn1IBkKqZTK81yG21MVKk54B8rqiTY30c,1236
252
+ omlish/specs/jsonschema/keywords/validation.py,sha256=ghewVUSG5qPYnmexy1Ylc-XouiQXobsXLYvkaGgZLqg,1322
253
+ omlish/specs/jsonschema/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
254
+ omlish/specs/jsonschema/schemas/draft202012/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
255
+ omlish/specs/jsonschema/schemas/draft202012/vocabularies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
242
256
  omlish/sql/__init__.py,sha256=pLv2EQmAwcGXN0Y3AE8K5JjyN_4GaHRMHyExaDFm5yU,181
243
257
  omlish/sql/_abc.py,sha256=HLhnnLZ7l0r_N99I-RCqJe6VHth-9iluU7cR-7-5jfs,1519
244
258
  omlish/sql/asyncs.py,sha256=Wye3dwh7oZEGYz2Y4DZQSHtW4xjI2AH5qjW-BSS2IfU,3688
@@ -271,8 +285,8 @@ omlish/text/delimit.py,sha256=ubPXcXQmtbOVrUsNh5gH1mDq5H-n1y2R4cPL5_DQf68,4928
271
285
  omlish/text/glyphsplit.py,sha256=Ug-dPRO7x-OrNNr8g1y6DotSZ2KH0S-VcOmUobwa4B0,3296
272
286
  omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
273
287
  omlish/text/parts.py,sha256=KGgo0wHOIMVMZtDso-rhSWKAcAkYAH2IGpg9tULabu8,6505
274
- omlish-0.0.0.dev19.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
275
- omlish-0.0.0.dev19.dist-info/METADATA,sha256=WKLfItwNq64Fe1ej7iTmG2WBv0VczhZNwn_9mAVyTPY,3718
276
- omlish-0.0.0.dev19.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
277
- omlish-0.0.0.dev19.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
278
- omlish-0.0.0.dev19.dist-info/RECORD,,
288
+ omlish-0.0.0.dev20.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
289
+ omlish-0.0.0.dev20.dist-info/METADATA,sha256=BBHZ7D3ieK9P1cgSW23jGabeqRXDnzccCc8Tsby0J5Y,3718
290
+ omlish-0.0.0.dev20.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
291
+ omlish-0.0.0.dev20.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
292
+ omlish-0.0.0.dev20.dist-info/RECORD,,