omlish 0.0.0.dev41__py3-none-any.whl → 0.0.0.dev42__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.
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev41'
2
- __revision__ = '9666ba3a1c40b95074dfcacd1c0c87ca5e267d6f'
1
+ __version__ = '0.0.0.dev42'
2
+ __revision__ = '244991d9a1d24de7e5b30d0e1ea7cfd341a223ab'
3
3
 
4
4
 
5
5
  #
@@ -92,6 +92,7 @@ from .utils import ( # noqa
92
92
  chain_metadata,
93
93
  deep_replace,
94
94
  field_modifier,
95
+ fields_dict,
95
96
  maybe_post_init,
96
97
  opt_repr,
97
98
  update_class_metadata,
@@ -21,4 +21,5 @@ TODO:
21
21
  - proto/jsonschema gen
22
22
  - enums
23
23
  - nodal
24
+ - embedding? forward kwargs in general? or only for replace?
24
25
  """
@@ -36,6 +36,10 @@ def opt_repr(o: ta.Any) -> str | None:
36
36
  #
37
37
 
38
38
 
39
+ def fields_dict(cls_or_instance: ta.Any) -> dict[str, dc.Field]:
40
+ return {f.name: f for f in dc.fields(cls_or_instance)}
41
+
42
+
39
43
  class field_modifier: # noqa
40
44
  def __init__(self, fn: ta.Callable[[dc.Field], dc.Field]) -> None:
41
45
  super().__init__()
@@ -57,6 +57,7 @@ from .helpers import ( # noqa
57
57
  from .objects import ( # noqa
58
58
  FieldInfo,
59
59
  FieldMetadata,
60
+ FieldOptions,
60
61
  ObjectMarshaler,
61
62
  ObjectMetadata,
62
63
  ObjectUnmarshaler,
@@ -13,8 +13,11 @@ from .base import Unmarshaler
13
13
  from .base import UnmarshalerFactory
14
14
  from .naming import Naming
15
15
  from .naming import translate_name
16
+ from .objects import DEFAULT_FIELD_OPTIONS
17
+ from .objects import FIELD_OPTIONS_KWARGS
16
18
  from .objects import FieldInfo
17
19
  from .objects import FieldMetadata
20
+ from .objects import FieldOptions
18
21
  from .objects import ObjectMarshaler
19
22
  from .objects import ObjectMetadata
20
23
  from .objects import ObjectUnmarshaler
@@ -43,6 +46,11 @@ def get_field_infos(
43
46
  for k, v in dc.asdict(dc_md.field_defaults).items()
44
47
  if v is not None
45
48
  }
49
+ fo_defaults = {
50
+ k: v
51
+ for k, v in fi_defaults.pop('options').items()
52
+ if v != getattr(DEFAULT_FIELD_OPTIONS, k)
53
+ }
46
54
 
47
55
  type_hints = ta.get_type_hints(ty)
48
56
 
@@ -53,8 +61,10 @@ def get_field_infos(
53
61
  else:
54
62
  um_name = field.name
55
63
 
56
- kw = dict(fi_defaults)
57
- kw.update(
64
+ fi_kw = dict(fi_defaults)
65
+ fo_kw = dict(fo_defaults)
66
+
67
+ fi_kw.update(
58
68
  name=field.name,
59
69
  type=type_hints[field.name],
60
70
  metadata=FieldMetadata(),
@@ -63,20 +73,35 @@ def get_field_infos(
63
73
  unmarshal_names=[um_name],
64
74
  )
65
75
 
76
+ has_set_name = False
66
77
  if (fmd := field.metadata.get(FieldMetadata)) is not None:
67
- kw.update(
78
+ fi_kw.update(
68
79
  metadata=fmd,
69
- omit_if=fmd.omit_if,
70
- default=fmd.default,
71
80
  )
72
81
 
82
+ for fo_k in FIELD_OPTIONS_KWARGS:
83
+ if (fo_v := getattr(fmd.options, fo_k)) != getattr(DEFAULT_FIELD_OPTIONS, fo_k):
84
+ fo_kw[fo_k] = fo_v
85
+
73
86
  if fmd.name is not None:
74
- kw.update(
87
+ has_set_name = True
88
+ fi_kw.update(
75
89
  marshal_name=fmd.name,
76
90
  unmarshal_names=col.unique([fmd.name, *(fmd.alts or ())]),
77
91
  )
78
92
 
79
- ret.append(FieldInfo(**kw))
93
+ if fo_kw.get('embed') and not has_set_name:
94
+ fi_kw.update(
95
+ marshal_name=fi_kw['marshal_name'] + '_',
96
+ unmarshal_names=[n + '_' for n in fi_kw['unmarshal_names']],
97
+ )
98
+
99
+ ret.append(
100
+ FieldInfo(
101
+ options=FieldOptions(**fo_kw),
102
+ **fi_kw,
103
+ ),
104
+ )
80
105
 
81
106
  return ret
82
107
 
@@ -123,23 +148,51 @@ class DataclassUnmarshalerFactory(UnmarshalerFactory):
123
148
  def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
124
149
  ty = check.isinstance(rty, type)
125
150
  check.state(dc.is_dataclass(ty))
126
-
127
151
  dc_md = get_dataclass_metadata(ty)
128
152
 
129
153
  d: dict[str, tuple[FieldInfo, Unmarshaler]] = {}
130
154
  defaults: dict[str, ta.Any] = {}
155
+ embeds: dict[str, type] = {}
156
+ embeds_by_unmarshal_name: dict[str, tuple[str, str]] = {}
157
+
158
+ def add_field(fi: FieldInfo, *, prefixes: ta.Iterable[str] = ('',)) -> ta.Iterable[str]:
159
+ ret: list[str] = []
160
+
161
+ if fi.options.embed:
162
+ e_ty = check.isinstance(fi.type, type)
163
+ check.state(dc.is_dataclass(e_ty))
164
+ # e_dc_md = get_dataclass_metadata(e_ty)
165
+
166
+ embeds[fi.name] = e_ty
167
+ for e_fi in get_field_infos(e_ty, ctx.options):
168
+ e_ns = add_field(e_fi, prefixes=[p + ep for p in prefixes for ep in fi.unmarshal_names])
169
+ embeds_by_unmarshal_name.update({e_f: (fi.name, e_fi.name) for e_f in e_ns})
170
+ ret.extend(e_ns)
171
+
172
+ else:
173
+ tup = (fi, _make_field_obj(ctx, fi.type, fi.metadata.unmarshaler, fi.metadata.unmarshaler_factory))
174
+
175
+ for pfx in prefixes:
176
+ for un in fi.unmarshal_names:
177
+ un = pfx + un
178
+ if un in d:
179
+ raise KeyError(f'Duplicate fields for name {un!r}: {fi.name!r}, {d[un][0].name!r}')
180
+ d[un] = tup
181
+ ret.append(un)
182
+
183
+ if fi.options.default.present:
184
+ defaults[fi.name] = fi.options.default.must()
185
+
186
+ return ret
187
+
131
188
  for fi in get_field_infos(ty, ctx.options):
132
- tup = (fi, _make_field_obj(ctx, fi.type, fi.metadata.unmarshaler, fi.metadata.unmarshaler_factory))
133
- for un in fi.unmarshal_names:
134
- if un in d:
135
- raise KeyError(f'Duplicate fields for name {un!r}: {fi.name!r}, {d[un][0].name!r}')
136
- d[un] = tup
137
- if fi.default.present:
138
- defaults[fi.name] = fi.default.must()
189
+ add_field(fi)
139
190
 
140
191
  return ObjectUnmarshaler(
141
192
  ty,
142
193
  d,
143
194
  unknown_field=dc_md.unknown_field,
144
195
  defaults=defaults,
196
+ embeds=embeds,
197
+ embeds_by_unmarshal_name=embeds_by_unmarshal_name,
145
198
  )
omlish/marshal/helpers.py CHANGED
@@ -12,10 +12,7 @@ def update_field_metadata(**kwargs: ta.Any) -> dc.field_modifier:
12
12
  @dc.field_modifier
13
13
  def inner(f: dc.Field) -> dc.Field:
14
14
  return dc.update_field_metadata(f, {
15
- FieldMetadata: dc.replace(
16
- f.metadata.get(FieldMetadata, FieldMetadata()),
17
- **kwargs,
18
- ),
15
+ FieldMetadata: f.metadata.get(FieldMetadata, FieldMetadata()).update(**kwargs),
19
16
  })
20
17
  return inner
21
18
 
@@ -26,10 +23,7 @@ def update_fields_metadata(
26
23
  ) -> ta.Callable[[type[T]], type[T]]:
27
24
  def inner(a: str, f: dc.Field) -> dc.Field:
28
25
  return dc.update_field_metadata(f, {
29
- FieldMetadata: dc.replace(
30
- f.metadata.get(FieldMetadata, FieldMetadata()),
31
- **kwargs,
32
- ),
26
+ FieldMetadata: f.metadata.get(FieldMetadata, FieldMetadata()).update(**kwargs),
33
27
  })
34
28
 
35
29
  return dc.update_fields(inner, fields)
omlish/marshal/objects.py CHANGED
@@ -1,11 +1,9 @@
1
1
  """
2
2
  TODO:
3
3
  - cfg naming
4
- - adapters for dataclasses / namedtuples / user objects (as confitured)
4
+ - adapters for dataclasses / namedtuples / user objects (as configured)
5
5
  - mro-merge ObjectMetadata
6
6
  - key ordering override - like slice, -1 means last
7
- - dedupe omit_if/default fields on Metadata/Info - FieldOptions prob
8
- - impl merge, update helpers:update_fields_metadata
9
7
  """
10
8
  import collections.abc
11
9
  import typing as ta
@@ -27,13 +25,25 @@ from .values import Value
27
25
  ##
28
26
 
29
27
 
28
+ @dc.dataclass(frozen=True, kw_only=True)
29
+ class FieldOptions:
30
+ omit_if: ta.Callable[[ta.Any], bool] | None = None
31
+
32
+ default: lang.Maybe[ta.Any] = dc.xfield(default=lang.empty(), check_type=lang.Maybe)
33
+
34
+ embed: bool = False
35
+
36
+
37
+ DEFAULT_FIELD_OPTIONS = FieldOptions()
38
+ FIELD_OPTIONS_KWARGS: frozenset[str] = frozenset(dc.fields_dict(FieldOptions).keys())
39
+
40
+
30
41
  @dc.dataclass(frozen=True, kw_only=True)
31
42
  class FieldMetadata:
32
43
  name: str | None = None
33
44
  alts: ta.Iterable[str] | None = None
34
45
 
35
- omit_if: ta.Callable[[ta.Any], bool] | None = None
36
- default: lang.Maybe[ta.Any] = dc.xfield(lang.empty(), check_type=lang.Maybe)
46
+ options: FieldOptions = DEFAULT_FIELD_OPTIONS
37
47
 
38
48
  marshaler: Marshaler | None = None
39
49
  marshaler_factory: MarshalerFactory | None = None
@@ -41,6 +51,15 @@ class FieldMetadata:
41
51
  unmarshaler: Unmarshaler | None = None
42
52
  unmarshaler_factory: UnmarshalerFactory | None = None
43
53
 
54
+ def update(self, **kwargs: ta.Any) -> 'FieldMetadata':
55
+ okw = {k: v for k, v in kwargs.items() if k in FIELD_OPTIONS_KWARGS}
56
+ mkw = {k: v for k, v in kwargs.items() if k not in FIELD_OPTIONS_KWARGS}
57
+ return dc.replace(
58
+ self,
59
+ **(dict(options=dc.replace(self.options, **okw)) if okw else {}),
60
+ **mkw,
61
+ )
62
+
44
63
 
45
64
  @dc.dataclass(frozen=True, kw_only=True)
46
65
  class ObjectMetadata:
@@ -64,8 +83,7 @@ class FieldInfo:
64
83
 
65
84
  metadata: FieldMetadata = FieldMetadata()
66
85
 
67
- omit_if: ta.Callable[[ta.Any], bool] | None = None
68
- default: lang.Maybe[ta.Any] = lang.empty()
86
+ options: FieldOptions = FieldOptions()
69
87
 
70
88
 
71
89
  ##
@@ -80,12 +98,21 @@ class ObjectMarshaler(Marshaler):
80
98
  unknown_field: str | None = None
81
99
 
82
100
  def marshal(self, ctx: MarshalContext, o: ta.Any) -> Value:
83
- ret = {}
101
+ ret: dict[str, ta.Any] = {}
84
102
  for fi, m in self.fields:
85
103
  v = getattr(o, fi.name)
86
- if fi.omit_if is not None and fi.omit_if(v):
104
+
105
+ if fi.options.omit_if is not None and fi.options.omit_if(v):
87
106
  continue
88
- ret[fi.marshal_name] = m.marshal(ctx, v)
107
+
108
+ mv = m.marshal(ctx, v)
109
+
110
+ if fi.options.embed:
111
+ for ek, ev in check.isinstance(mv, collections.abc.Mapping).items():
112
+ ret[fi.marshal_name + check.non_empty_str(ek)] = ev # type: ignore
113
+
114
+ else:
115
+ ret[fi.marshal_name] = mv
89
116
 
90
117
  if self.unknown_field is not None:
91
118
  if (ukf := getattr(o, self.unknown_field)):
@@ -135,8 +162,12 @@ class ObjectUnmarshaler(Unmarshaler):
135
162
  _: dc.KW_ONLY
136
163
 
137
164
  unknown_field: str | None = None
165
+
138
166
  defaults: ta.Mapping[str, ta.Any] | None = None
139
167
 
168
+ embeds: ta.Mapping[str, type] | None = None
169
+ embeds_by_unmarshal_name: ta.Mapping[str, tuple[str, str]] | None = None
170
+
140
171
  def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any:
141
172
  ma = check.isinstance(v, collections.abc.Mapping)
142
173
 
@@ -144,6 +175,8 @@ class ObjectUnmarshaler(Unmarshaler):
144
175
  kw: dict[str, ta.Any] = {}
145
176
  ukf: dict[str, ta.Any] | None = None
146
177
 
178
+ ekws: dict[str, dict[str, ta.Any]] = {en: {} for en in self.embeds or ()}
179
+
147
180
  if self.unknown_field is not None:
148
181
  kw[self.unknown_field] = ukf = {}
149
182
 
@@ -159,10 +192,20 @@ class ObjectUnmarshaler(Unmarshaler):
159
192
  continue
160
193
  raise
161
194
 
162
- if fi.name in kw:
163
- raise KeyError(f'Duplicate keys for field {fi.name!r}: {ks!r}')
195
+ if self.embeds_by_unmarshal_name and (en := self.embeds_by_unmarshal_name.get(ks)):
196
+ tkw, tk = ekws[en[0]], en[1]
197
+ else:
198
+ tkw, tk = kw, fi.name
199
+
200
+ if tk in tkw:
201
+ raise KeyError(f'Duplicate keys for field {tk!r}: {ks!r}')
202
+
203
+ tkw[tk] = u.unmarshal(ctx, mv)
164
204
 
165
- kw[fi.name] = u.unmarshal(ctx, mv)
205
+ for em, ecls in self.embeds.items() if self.embeds else ():
206
+ ekw = ekws[em]
207
+ ev = ecls(**ekw)
208
+ kw[em] = ev
166
209
 
167
210
  if self.defaults:
168
211
  for dk, dv in self.defaults.items():
@@ -194,9 +237,9 @@ class SimpleObjectUnmarshalerFactory(UnmarshalerFactory):
194
237
  }
195
238
 
196
239
  defaults = {
197
- fi.name: fi.default.must()
240
+ fi.name: fi.options.default.must()
198
241
  for fi in flds
199
- if fi.default.present
242
+ if fi.options.default.present
200
243
  }
201
244
 
202
245
  return ObjectUnmarshaler(
@@ -0,0 +1,5 @@
1
+ from . import marshal # noqa
2
+
3
+ from .openapi import ( # noqa
4
+ Openapi,
5
+ )
@@ -0,0 +1,63 @@
1
+ import typing as ta
2
+
3
+ from ... import check
4
+ from ... import dataclasses as dc
5
+ from ... import lang
6
+ from ... import marshal as msh
7
+ from ... import matchfns as mfs
8
+ from ... import reflect as rfl
9
+ from .openapi import Reference
10
+
11
+
12
+ #
13
+
14
+
15
+ def _reference_union_arg(rty: rfl.Type) -> rfl.Type | None:
16
+ if isinstance(rty, rfl.Union) and len(rty.args) == 2 and Reference in rty.args:
17
+ return check.single(a for a in rty.args if a is not Reference)
18
+ else:
19
+ return None
20
+
21
+
22
+ @dc.dataclass(frozen=True)
23
+ class _ReferenceUnionMarshaler(msh.Marshaler):
24
+ m: msh.Marshaler
25
+ r: msh.Marshaler
26
+
27
+ def marshal(self, ctx: msh.MarshalContext, o: ta.Any) -> msh.Value:
28
+ if isinstance(o, Reference):
29
+ return self.r.marshal(ctx, o)
30
+ else:
31
+ return self.m.marshal(ctx, o)
32
+
33
+
34
+ class _ReferenceUnionMarshalerFactory(msh.MarshalerFactoryMatchClass):
35
+ @mfs.simple(lambda _, ctx, rty: _reference_union_arg(rty) is not None)
36
+ def _build(self, ctx: msh.MarshalContext, rty: rfl.Type) -> msh.Marshaler:
37
+ return _ReferenceUnionMarshaler(ctx.make(check.not_none(_reference_union_arg(rty))), ctx.make(Reference))
38
+
39
+
40
+ @dc.dataclass(frozen=True)
41
+ class _ReferenceUnionUnmarshaler(msh.Unmarshaler):
42
+ u: msh.Unmarshaler
43
+ r: msh.Unmarshaler
44
+
45
+ def unmarshal(self, ctx: msh.UnmarshalContext, v: msh.Value) -> ta.Any:
46
+ if not isinstance(v, ta.Mapping):
47
+ raise TypeError(v)
48
+ elif '$ref' in v:
49
+ return self.r.unmarshal(ctx, v)
50
+ else:
51
+ return self.u.unmarshal(ctx, v) # noqa
52
+
53
+
54
+ class _ReferenceUnionUnmarshalerFactory(msh.UnmarshalerFactoryMatchClass):
55
+ @mfs.simple(lambda _, ctx, rty: _reference_union_arg(rty) is not None)
56
+ def _build(self, ctx: msh.UnmarshalContext, rty: rfl.Type) -> msh.Unmarshaler:
57
+ return _ReferenceUnionUnmarshaler(ctx.make(check.not_none(_reference_union_arg(rty))), ctx.make(Reference))
58
+
59
+
60
+ @lang.static_init
61
+ def _install_standard_marshalling() -> None:
62
+ msh.STANDARD_MARSHALER_FACTORIES[0:0] = [_ReferenceUnionMarshalerFactory()]
63
+ msh.STANDARD_UNMARSHALER_FACTORIES[0:0] = [_ReferenceUnionUnmarshalerFactory()]
@@ -0,0 +1,443 @@
1
+ """
2
+ https://swagger.io/specification/
3
+ """
4
+ import typing as ta
5
+
6
+ from ... import check
7
+ from ... import dataclasses as dc
8
+ from ... import marshal as msh
9
+ from ...formats import json
10
+
11
+
12
+ ##
13
+
14
+
15
+ # https://swagger.io/specification/#security-requirement-object
16
+ SecurityRequirement: ta.TypeAlias = ta.Mapping[str, ta.Sequence[str]]
17
+
18
+
19
+ @dc.dataclass(frozen=True)
20
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
21
+ class OauthFlow:
22
+ """https://swagger.io/specification/#oauth-flow-object"""
23
+
24
+ authorization_url: str
25
+ token_url: str
26
+ scopes: ta.Mapping[str, str]
27
+ refresh_ur: str | None = None
28
+
29
+
30
+ @dc.dataclass(frozen=True)
31
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
32
+ class OauthFlows:
33
+ """https://swagger.io/specification/#oauth-flows-object"""
34
+
35
+ implicit: OauthFlow | None = None
36
+ password: OauthFlow | None = None
37
+ client_credentials: OauthFlow | None = None
38
+ authorization_code: OauthFlow | None = None
39
+
40
+
41
+ @dc.dataclass(frozen=True)
42
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
43
+ @msh.update_fields_metadata(['in_'], name='in')
44
+ class SecurityScheme:
45
+ """https://swagger.io/specification/#security-scheme-object"""
46
+
47
+ type: str
48
+ scheme: str
49
+ name: str | None = None
50
+ in_: str | None = None
51
+ description: str | None = None
52
+ bearer_format: str | None = None
53
+ flows: OauthFlows | None = None
54
+ open_id_connect_url: str | None = None
55
+
56
+
57
+ @dc.dataclass(frozen=True)
58
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
59
+ class Xml:
60
+ """https://swagger.io/specification/#xml-object"""
61
+
62
+ name: str | None = None
63
+ namespace: str | None = None
64
+ prefix: str | None = None
65
+ attribute: bool | None = None
66
+ wrapped: bool | None = None
67
+
68
+
69
+ @dc.dataclass(frozen=True)
70
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
71
+ class Discriminator:
72
+ """https://swagger.io/specification/#discriminator-object"""
73
+
74
+ property_name: str
75
+ mapping: ta.Mapping[str, str] | None = None
76
+
77
+
78
+ @dc.dataclass(frozen=True)
79
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL, unknown_field='x')
80
+ class Schema:
81
+ """https://swagger.io/specification/#schema-object"""
82
+
83
+ discriminator: Discriminator | None = None
84
+ xml: Xml | None = None
85
+ external_docs: ta.Optional['ExternalDocumentation'] = None
86
+ example: ta.Any | None = None
87
+
88
+ # FIXME: HACK: this is a jsonschema lol - these are just hacked on
89
+
90
+ type: str | None = None
91
+ format: str | None = None
92
+ one_of: ta.Any = None
93
+ all_of: ta.Any = None
94
+ default: ta.Any = None
95
+ enum: ta.Any = None
96
+ items: ta.Any = None
97
+ required: ta.Any = None
98
+ properties: ta.Any = None
99
+ description: str | None = None
100
+ title: str | None = None
101
+ deprecated: bool | None = None
102
+ nullable: bool | None = None
103
+ additional_properties: ta.Any = None
104
+
105
+ #
106
+
107
+ x: ta.Mapping[str, ta.Any] | None = None
108
+
109
+ @dc.init
110
+ def _check_x(self) -> None:
111
+ for k in self.x or {}:
112
+ check.arg(k.startswith('x-'))
113
+
114
+
115
+ @dc.dataclass(frozen=True)
116
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
117
+ @msh.update_fields_metadata(['ref'], name='$ref')
118
+ class Reference:
119
+ """https://swagger.io/specification/#reference-object"""
120
+
121
+ ref: str
122
+ summary: str | None = None
123
+ description: str | None = None
124
+
125
+
126
+ @dc.dataclass(frozen=True)
127
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
128
+ class Tag:
129
+ """https://swagger.io/specification/#tag-object"""
130
+
131
+ name: str
132
+ description: str | None = None
133
+ external_docs: ta.Optional['ExternalDocumentation'] = None
134
+
135
+
136
+ @dc.dataclass(frozen=True)
137
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
138
+ @msh.update_fields_metadata(['common'], embed=True, name='')
139
+ class Header:
140
+ """https://swagger.io/specification/#header-object"""
141
+
142
+ # TODO: marshal embedding, shared with Parameter
143
+ common: 'ParameterCommon'
144
+
145
+
146
+ @dc.dataclass(frozen=True)
147
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
148
+ class Link:
149
+ """https://swagger.io/specification/#link-object"""
150
+
151
+ operation_ref: str | None = None
152
+ operation_id: str | None = None
153
+ parameters: ta.Mapping[str, ta.Any] | None = None
154
+ request_body: ta.Any = None
155
+ description: str | None = None
156
+ server: ta.Optional['Server'] = None
157
+
158
+
159
+ @dc.dataclass(frozen=True)
160
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
161
+ class Example:
162
+ """https://swagger.io/specification/#example-object"""
163
+
164
+ summary: str | None = None
165
+ description: str | None = None
166
+ value: ta.Any | None = None
167
+ external_value: str | None = None
168
+
169
+
170
+ # https://swagger.io/specification/#callback-object
171
+ Callback: ta.TypeAlias = ta.Mapping[str, ta.Union['PathItem', Reference]]
172
+
173
+
174
+ @dc.dataclass(frozen=True)
175
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
176
+ class Response:
177
+ """https://swagger.io/specification/#response-object"""
178
+
179
+ description: str
180
+ headers: ta.Mapping[str, Header | Reference] | None = None
181
+ content: ta.Mapping[str, 'MediaType'] | None = None
182
+ links: ta.Mapping[str, Link | Reference] | None = None
183
+
184
+
185
+ # https://swagger.io/specification/#responses-object
186
+ Responses: ta.TypeAlias = ta.Mapping[str, Response | Reference]
187
+
188
+
189
+ @dc.dataclass(frozen=True)
190
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
191
+ class Encoding:
192
+ """https://swagger.io/specification/#encoding-object"""
193
+
194
+ content_type: str | None = None
195
+ headers: ta.Mapping[str, Header | Reference] | None = None
196
+ style: str | None = None
197
+ explode: bool | None = None
198
+ allow_reserved: bool | None = None
199
+
200
+
201
+ @dc.dataclass(frozen=True)
202
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
203
+ class MediaType:
204
+ """https://swagger.io/specification/#media-type-object"""
205
+
206
+ schema: Schema | Reference | None = None
207
+ example: ta.Any = None
208
+ examples: ta.Mapping[str, Example | Reference] | None = None
209
+ encoding: ta.Mapping[str, Encoding] | None = None
210
+
211
+
212
+ @dc.dataclass(frozen=True)
213
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
214
+ class RequestBody:
215
+ """https://swagger.io/specification/#request-body-object"""
216
+
217
+ content: ta.Mapping[str, MediaType]
218
+ description: str | None = None
219
+ required: bool | None = None
220
+
221
+
222
+ @dc.dataclass(frozen=True)
223
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
224
+ class ParameterCommon:
225
+ description: str | None = None
226
+ required: bool | None = None
227
+ deprecated: bool | None = None
228
+ allow_empty_value: bool | None = None
229
+
230
+ style: str | None = None
231
+ explode: bool | None = None
232
+ allow_reserved: bool | None = None
233
+ schema: Schema | None = None
234
+ example: ta.Any = None
235
+ examples: ta.Mapping[str, Example | Reference] | None = None
236
+
237
+ content: ta.Mapping[str, MediaType] | None = None
238
+
239
+ # style: ta.Any = None
240
+ matrix: ta.Any = None
241
+ label: ta.Any = None
242
+ form: ta.Any = None
243
+ simple: ta.Any = None
244
+ space_delimited: ta.Any = None
245
+ pipe_delimited: ta.Any = None
246
+ deep_object: ta.Any = None
247
+
248
+
249
+ @dc.dataclass(frozen=True)
250
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
251
+ @msh.update_fields_metadata(['in_'], name='in')
252
+ @msh.update_fields_metadata(['common'], embed=True, name='')
253
+ class Parameter:
254
+ """https://swagger.io/specification/#parameter-object"""
255
+
256
+ name: str
257
+ in_: str
258
+
259
+ common: ParameterCommon
260
+
261
+
262
+ @dc.dataclass(frozen=True)
263
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
264
+ class ExternalDocumentation:
265
+ """https://swagger.io/specification/#external-documentation-object"""
266
+
267
+ url: str
268
+ description: str | None = None
269
+
270
+
271
+ @dc.dataclass(frozen=True)
272
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL, unknown_field='x')
273
+ class Operation:
274
+ """https://swagger.io/specification/#operation-object"""
275
+
276
+ tags: ta.Sequence[str] | None = None
277
+ summary: str | None = None
278
+ description: str | None = None
279
+ external_docs: ExternalDocumentation | None = None
280
+ operation_id: str | None = None
281
+ parameters: ta.Sequence[Parameter | Reference] | None = None
282
+ request_body: RequestBody | Reference | None = None
283
+ responses: Responses | None = None
284
+ callbacks: ta.Mapping[str, Callback | Reference] | None = None
285
+ deprecated: bool | None = None
286
+ security: ta.Sequence[SecurityRequirement] | None = None
287
+ servers: ta.Sequence['Server'] | None = None
288
+
289
+ #
290
+
291
+ x: ta.Mapping[str, ta.Any] | None = None
292
+
293
+ @dc.init
294
+ def _check_x(self) -> None:
295
+ for k in self.x or {}:
296
+ check.arg(k.startswith('x-'))
297
+
298
+
299
+ @dc.dataclass(frozen=True)
300
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
301
+ @msh.update_fields_metadata(['ref'], name='$ref')
302
+ class PathItem:
303
+ """https://swagger.io/specification/#path-item-object"""
304
+
305
+ ref: str | None = None
306
+ summary: str | None = None
307
+ description: str | None = None
308
+ get: Operation | None = None
309
+ put: Operation | None = None
310
+ post: Operation | None = None
311
+ delete: Operation | None = None
312
+ options: Operation | None = None
313
+ head: Operation | None = None
314
+ patch: Operation | None = None
315
+ trace: Operation | None = None
316
+ servers: ta.Sequence['Server'] | None = None
317
+ parameters: ta.Sequence[Parameter | Reference] | None = None
318
+
319
+
320
+ # https://swagger.io/specification/#paths-object
321
+ Paths: ta.TypeAlias = ta.Mapping[str, PathItem]
322
+
323
+
324
+ @dc.dataclass(frozen=True)
325
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
326
+ class Components:
327
+ """https://swagger.io/specification/#components-object"""
328
+
329
+ schemas: ta.Mapping[str, Schema] | None = None
330
+ responses: ta.Mapping[str, Response | Reference] | None = None
331
+ parameters: ta.Mapping[str, Parameter | Reference] | None = None
332
+ examples: ta.Mapping[str, Example | Reference] | None = None
333
+ requestbodies: ta.Mapping[str, RequestBody | Reference] | None = None
334
+ headers: ta.Mapping[str, Header | Reference] | None = None
335
+ security_schemes: ta.Mapping[str, SecurityScheme | Reference] | None = None
336
+ links: ta.Mapping[str, Link | Reference] | None = None
337
+ callbacks: ta.Mapping[str, Callback | Reference] | None = None
338
+ path_items: ta.Mapping[str, PathItem | Reference] | None = None
339
+
340
+
341
+ @dc.dataclass(frozen=True)
342
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
343
+ class ServerVariable:
344
+ """https://swagger.io/specification/#server-variable-object"""
345
+
346
+ default: str
347
+ enum: ta.Sequence[str] | None = None
348
+ description: str | None = None
349
+
350
+
351
+ @dc.dataclass(frozen=True)
352
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
353
+ class Server:
354
+ """https://swagger.io/specification/#server-object"""
355
+
356
+ url: str
357
+ description: str | None = None
358
+ variables: ta.Mapping[str, ServerVariable] | None = None
359
+
360
+
361
+ @dc.dataclass(frozen=True)
362
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
363
+ class License:
364
+ """https://swagger.io/specification/#license-object"""
365
+
366
+ name: str
367
+ identifier: str | None = None
368
+ url: str | None = None
369
+
370
+
371
+ @dc.dataclass(frozen=True)
372
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
373
+ class Contact:
374
+ """https://swagger.io/specification/#contact-object"""
375
+
376
+ name: str | None = None
377
+ url: str | None = None
378
+ email: str | None = None
379
+
380
+
381
+ @dc.dataclass(frozen=True)
382
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
383
+ class Info:
384
+ """https://swagger.io/specification/#info-object"""
385
+
386
+ title: str
387
+ version: str
388
+ summary: str | None = None
389
+ description: str | None = None
390
+ terms_of_service: str | None = None
391
+ contact: Contact | None = None
392
+ license: License | None = None
393
+
394
+
395
+ @dc.dataclass(frozen=True)
396
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL, unknown_field='x')
397
+ class Openapi:
398
+ """https://swagger.io/specification/#openapi-object"""
399
+
400
+ openapi: str
401
+ info: Info
402
+ json_schema_dialect: str | None = None
403
+ servers: ta.Sequence[Server] | None = None
404
+ paths: Paths | None = None
405
+ webhooks: ta.Mapping[str, PathItem | Reference] | None = None
406
+ components: Components | None = None
407
+ security: ta.Sequence[SecurityRequirement] | None = None
408
+ tags: ta.Sequence[Tag] | None = None
409
+ external_docs: ExternalDocumentation | None = None
410
+
411
+ #
412
+
413
+ x: ta.Mapping[str, ta.Any] | None = None
414
+
415
+ @dc.init
416
+ def _check_x(self) -> None:
417
+ for k in self.x or {}:
418
+ check.arg(k.startswith('x-'))
419
+
420
+
421
+ ##
422
+
423
+
424
+ def _main():
425
+ import argparse
426
+
427
+ parser = argparse.ArgumentParser()
428
+ parser.add_argument('schema_path')
429
+
430
+ args = parser.parse_args()
431
+
432
+ import yaml
433
+
434
+ with open(args.schema_path) as f:
435
+ doc = yaml.safe_load(f)
436
+
437
+ api = msh.unmarshal(doc, Openapi)
438
+
439
+ print(json.dumps_pretty(msh.marshal(api)))
440
+
441
+
442
+ if __name__ == '__main__':
443
+ _main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev41
3
+ Version: 0.0.0.dev42
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=FPSgLg_3QDVzKlNOuqV3dMqhja2KG_TUfKdGmHE8Eg4,803
2
- omlish/__about__.py,sha256=7FsOWhuWPr63aM9w6D-8mkjM3PSH0TI7v26pjaJDmCg,2919
2
+ omlish/__about__.py,sha256=S3UHQkiEUjKJjDzTX2TmymQCRF1d722ZAqV9yJ6o_GU,2919
3
3
  omlish/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  omlish/argparse.py,sha256=QUUS6sv2ftfcmhDj4qU429mEE_lfWa0UN5yYpPG65B0,6265
5
5
  omlish/c3.py,sha256=4vogWgwPb8TbNS2KkZxpoWbwjj7MuHG2lQG-hdtkvjI,8062
@@ -64,10 +64,10 @@ omlish/configs/__init__.py,sha256=3uh09ezodTwkMI0nRmAMP0eEuJ_0VdF-LYyNmPjHiCE,77
64
64
  omlish/configs/classes.py,sha256=GLbB8xKjHjjoUQRCUQm3nEjM8z1qNTx9gPV7ODSt5dg,1317
65
65
  omlish/configs/flattening.py,sha256=AOlRpBHm449MxwMp3CiIRGunStOC1DUNs1f3CLou0wc,4731
66
66
  omlish/configs/strings.py,sha256=0brx1duL85r1GpfbNvbHcSvH4jWzutwuvMFXda9NeI0,2651
67
- omlish/dataclasses/__init__.py,sha256=L2kRMvsWgsennXVw7VgZdczCtdLsQzyPcMFit2rBpbA,1337
68
- omlish/dataclasses/utils.py,sha256=qBKHyW2LSjnJYfPSAExEsVZ5EdmDBfp4napAhfWkZFs,3221
67
+ omlish/dataclasses/__init__.py,sha256=LALx4dXSvBWAKzmZxb3rkNdoR7kOaXeGcVypTnECvcw,1354
68
+ omlish/dataclasses/utils.py,sha256=Ewd6w31uXy1sVUj59sxmXfGuI0CgsAbcfNXx0tsrNco,3347
69
69
  omlish/dataclasses/impl/LICENSE,sha256=Oy-B_iHRgcSZxZolbI4ZaEVdZonSaaqFNzv7avQdo78,13936
70
- omlish/dataclasses/impl/__init__.py,sha256=rQJRcE9fVsGEvRUOZ18r4DE0xiLbSRjspyJRXgbLS3I,348
70
+ omlish/dataclasses/impl/__init__.py,sha256=0hgOm3_u3yimjEkB-9fRMqX9rAW6kQC5t8XL0lpvffQ,410
71
71
  omlish/dataclasses/impl/api.py,sha256=6QtUAbP3DQZw8WEkgpsYfYKTn4CQBV9v0z2B4fTcR80,6405
72
72
  omlish/dataclasses/impl/as_.py,sha256=CD-t7hkC1EP2F_jvZKIA_cVoDuwZ-Ln_xC4fJumPYX0,2598
73
73
  omlish/dataclasses/impl/copy.py,sha256=Tn8_n6Vohs-w4otbGdubBEvhd3TsSTaM3EfNGdS2LYo,591
@@ -219,24 +219,24 @@ omlish/logs/formatters.py,sha256=q79nMnR2mRIStPyGrydQHpYTXgC5HHptt8lH3W2Wwbs,671
219
219
  omlish/logs/handlers.py,sha256=nyuFgmO05By_Xwq7es58ClzS51-F53lJL7gD0x5IqAg,228
220
220
  omlish/logs/noisy.py,sha256=Ubc-eTH6ZbGYsLfUUi69JAotwuUwzb-SJBeGo_0dIZI,348
221
221
  omlish/logs/utils.py,sha256=MgGovbP0zUrZ3FGD3qYNQWn-l0jy0Y0bStcQvv5BOmQ,391
222
- omlish/marshal/__init__.py,sha256=B2NLwxUuhvD17FfbipXmYo9yZmpBFcHqGPp-r8_Vuvs,1951
222
+ omlish/marshal/__init__.py,sha256=_vx3txFh09j3SoxSfSS4AwwnDa9jgAWzXYjluIitGWA,1969
223
223
  omlish/marshal/any.py,sha256=e82OyYK3Emm1P1ClnsnxP7fIWC2iNVyW0H5nK4mLmWM,779
224
224
  omlish/marshal/base.py,sha256=ceNjOFxCwLNuxkmxofk3Sh5rE39JGxnc4giN7NNr9kA,6121
225
225
  omlish/marshal/base64.py,sha256=F-3ogJdcFCtWINRgJgWT0rErqgx6f4qahhcg8OrkqhE,1089
226
- omlish/marshal/dataclasses.py,sha256=TdpTCqAHs9hrTyUG__goSRcm848LZznJcDG4WY5wbgM,4109
226
+ omlish/marshal/dataclasses.py,sha256=B21hFukA9pNhp-PzBWG7rxz_F5BjNzMxU3nFURRrGXo,6037
227
227
  omlish/marshal/datetimes.py,sha256=0ffg8cEvx9SMKIXZGD9b7MqpLfmgw0uKKdn6YTfoqok,3714
228
228
  omlish/marshal/enums.py,sha256=-0fKutBbyz8ygEaA0_P_8IOJrI9jMGigmnPbutV9Bg4,1464
229
229
  omlish/marshal/exceptions.py,sha256=jwQWn4LcPnadT2KRI_1JJCOSkwWh0yHnYK9BmSkNN4U,302
230
230
  omlish/marshal/factories.py,sha256=UV2Svjok-lTWsRqKGh-CeuAhvlohw9uJe7ZLyoKMvTM,2968
231
231
  omlish/marshal/forbidden.py,sha256=BNshzm4lN5O8sUZ1YvxrSYq3WPklq9NMQCRZ7RC3DLM,865
232
232
  omlish/marshal/global_.py,sha256=8XCjPcIjA65StESshzNfQiSyuckVaEiiWROmC3qk0mo,1117
233
- omlish/marshal/helpers.py,sha256=YA0pNo-Fkc-_qKeoRNXUpP36js8oelU7uENkvoGD4hY,1206
233
+ omlish/marshal/helpers.py,sha256=-SOgYJmrURILHpPK6Wu3cCvhj8RJrqfJxuKhh9UMs7o,1102
234
234
  omlish/marshal/iterables.py,sha256=6I_ZdJemLSQtJ4J5NrB9wi-eyxiJZS61HzHXp1yeiX8,2592
235
235
  omlish/marshal/mappings.py,sha256=zhLtyot7tzQtBNj7C4RBxjMELxA5r2q2Mth8Br7xkFs,2803
236
236
  omlish/marshal/maybes.py,sha256=mgK3QsWHkXgRqo076KxYKH6elRxzJ_QDTodv93mgHR0,2198
237
237
  omlish/marshal/naming.py,sha256=lIklR_Od4x1ghltAgOzqcKhHs-leeSv2YmFhCHO7GIs,613
238
238
  omlish/marshal/numbers.py,sha256=oY_yMNJEnJhjfLh89gpPXvKqeUyhQcaTcQB6ecyHiG8,1704
239
- omlish/marshal/objects.py,sha256=E5wvH1o0_edsVJ4HoUuTqPfnb21ou08IdL442OQUOUw,5235
239
+ omlish/marshal/objects.py,sha256=fe4dPEF6BqZRWHSn3D7Z5JyLg-Z7JFPhBe2gjUGBefw,6543
240
240
  omlish/marshal/optionals.py,sha256=r0XB5rqfasvgZJNrKYd6Unq2U4nHt3JURi26j0dYHlw,1499
241
241
  omlish/marshal/polymorphism.py,sha256=doA8aLUhna6aco5b2Ok3jsem1V4NsF3rM5RTfJt0a7U,5708
242
242
  omlish/marshal/primitives.py,sha256=wcvcs5GH_TWVmzAszh3dvyKibJgBxnXke-AlAXiwrrI,1107
@@ -289,6 +289,9 @@ omlish/specs/jsonschema/schemas/draft202012/vocabularies/format.json,sha256=UOu_
289
289
  omlish/specs/jsonschema/schemas/draft202012/vocabularies/meta-data.json,sha256=j3bW4U9Bubku-TO3CM3FFEyLUmhlGtEZGEhfsXVPHHY,892
290
290
  omlish/specs/jsonschema/schemas/draft202012/vocabularies/unevaluated.json,sha256=Lb-8tzmUtnCwl2SSre4f_7RsIWgnhNL1pMpWH54tDLQ,506
291
291
  omlish/specs/jsonschema/schemas/draft202012/vocabularies/validation.json,sha256=cBCjHlQfMtK-ch4t40jfdcmzaHaj7TBId_wKvaHTelg,2834
292
+ omlish/specs/openapi/__init__.py,sha256=zilQhafjvteRDF_TUIRgF293dBC6g-TJChmUb6T9VBQ,77
293
+ omlish/specs/openapi/marshal.py,sha256=ob_qUbT9-de86KhPjFccl_NP0liQcXK7Ao-b5Hn0VQA,2133
294
+ omlish/specs/openapi/openapi.py,sha256=y4h04jeB7ORJSVrcy7apaBdpwLjIyscv1Ub5SderH2c,12682
292
295
  omlish/sql/__init__.py,sha256=TpZLsEJKJzvJ0eMzuV8hwOJJbkxBCV1RZPUMLAVB6io,173
293
296
  omlish/sql/_abc.py,sha256=HLhnnLZ7l0r_N99I-RCqJe6VHth-9iluU7cR-7-5jfs,1519
294
297
  omlish/sql/dbs.py,sha256=lpdFmm2vTwLoBiVYGj9yPsVcTEYYNCxlYZZpjfChzkY,1870
@@ -325,9 +328,9 @@ omlish/text/delimit.py,sha256=ubPXcXQmtbOVrUsNh5gH1mDq5H-n1y2R4cPL5_DQf68,4928
325
328
  omlish/text/glyphsplit.py,sha256=Ug-dPRO7x-OrNNr8g1y6DotSZ2KH0S-VcOmUobwa4B0,3296
326
329
  omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
327
330
  omlish/text/parts.py,sha256=7vPF1aTZdvLVYJ4EwBZVzRSy8XB3YqPd7JwEnNGGAOo,6495
328
- omlish-0.0.0.dev41.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
329
- omlish-0.0.0.dev41.dist-info/METADATA,sha256=r8Q2E7Ch6npanZY7xAo-quF5Z9R9OWkH6eY2JATdOfs,3817
330
- omlish-0.0.0.dev41.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
331
- omlish-0.0.0.dev41.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
332
- omlish-0.0.0.dev41.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
333
- omlish-0.0.0.dev41.dist-info/RECORD,,
331
+ omlish-0.0.0.dev42.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
332
+ omlish-0.0.0.dev42.dist-info/METADATA,sha256=D6POba16biuXPIbTw9Scr1y1L2x-sCf0TDK4R3ByhXI,3817
333
+ omlish-0.0.0.dev42.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
334
+ omlish-0.0.0.dev42.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
335
+ omlish-0.0.0.dev42.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
336
+ omlish-0.0.0.dev42.dist-info/RECORD,,