jentic-openapi-datamodels 1.0.0a11__py3-none-any.whl → 1.0.0a13__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.
- jentic/apitools/openapi/datamodels/low/context.py +21 -0
- jentic/apitools/openapi/datamodels/low/extractors.py +126 -0
- jentic/apitools/openapi/datamodels/low/fields.py +45 -0
- jentic/apitools/openapi/datamodels/low/model_builder.py +113 -0
- jentic/apitools/openapi/datamodels/low/py.typed +0 -0
- jentic/apitools/openapi/datamodels/low/sources.py +89 -0
- jentic/apitools/openapi/datamodels/low/v30/__init__.py +0 -28
- jentic/apitools/openapi/datamodels/low/v30/discriminator.py +53 -78
- jentic/apitools/openapi/datamodels/low/v30/external_documentation.py +47 -61
- jentic/apitools/openapi/datamodels/low/v30/oauth_flow.py +54 -123
- jentic/apitools/openapi/datamodels/low/v30/oauth_flows.py +102 -151
- jentic/apitools/openapi/datamodels/low/v30/reference.py +43 -44
- jentic/apitools/openapi/datamodels/low/v30/schema.py +316 -607
- jentic/apitools/openapi/datamodels/low/v30/security_requirement.py +82 -72
- jentic/apitools/openapi/datamodels/low/v30/security_scheme.py +94 -286
- jentic/apitools/openapi/datamodels/low/v30/tag.py +88 -119
- jentic/apitools/openapi/datamodels/low/v30/xml.py +46 -120
- jentic_openapi_datamodels-1.0.0a13.dist-info/METADATA +211 -0
- jentic_openapi_datamodels-1.0.0a13.dist-info/RECORD +23 -0
- jentic/apitools/openapi/datamodels/low/v30/specification_object.py +0 -217
- jentic_openapi_datamodels-1.0.0a11.dist-info/METADATA +0 -52
- jentic_openapi_datamodels-1.0.0a11.dist-info/RECORD +0 -18
- /jentic/apitools/openapi/datamodels/low/{v30/py.typed → __init__.py} +0 -0
- {jentic_openapi_datamodels-1.0.0a11.dist-info → jentic_openapi_datamodels-1.0.0a13.dist-info}/WHEEL +0 -0
- {jentic_openapi_datamodels-1.0.0a11.dist-info → jentic_openapi_datamodels-1.0.0a13.dist-info}/licenses/LICENSE +0 -0
- {jentic_openapi_datamodels-1.0.0a11.dist-info → jentic_openapi_datamodels-1.0.0a13.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,626 +1,335 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
from
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Any, TypeAlias, get_args
|
|
3
|
+
|
|
4
|
+
from ruamel import yaml
|
|
5
|
+
|
|
6
|
+
from jentic.apitools.openapi.datamodels.low.context import Context
|
|
7
|
+
from jentic.apitools.openapi.datamodels.low.extractors import extract_extension_fields
|
|
8
|
+
from jentic.apitools.openapi.datamodels.low.fields import fixed_field, fixed_fields
|
|
9
|
+
from jentic.apitools.openapi.datamodels.low.sources import (
|
|
10
|
+
FieldSource,
|
|
11
|
+
KeySource,
|
|
12
|
+
ValueSource,
|
|
13
|
+
YAMLInvalidValue,
|
|
14
|
+
YAMLValue,
|
|
15
|
+
)
|
|
13
16
|
from jentic.apitools.openapi.datamodels.low.v30.discriminator import Discriminator
|
|
17
|
+
from jentic.apitools.openapi.datamodels.low.v30.discriminator import build as build_discriminator
|
|
14
18
|
from jentic.apitools.openapi.datamodels.low.v30.external_documentation import (
|
|
15
19
|
ExternalDocumentation,
|
|
16
20
|
)
|
|
21
|
+
from jentic.apitools.openapi.datamodels.low.v30.external_documentation import (
|
|
22
|
+
build as build_external_documentation,
|
|
23
|
+
)
|
|
17
24
|
from jentic.apitools.openapi.datamodels.low.v30.reference import Reference
|
|
18
|
-
from jentic.apitools.openapi.datamodels.low.v30.
|
|
25
|
+
from jentic.apitools.openapi.datamodels.low.v30.reference import build as build_reference
|
|
19
26
|
from jentic.apitools.openapi.datamodels.low.v30.xml import XML
|
|
27
|
+
from jentic.apitools.openapi.datamodels.low.v30.xml import build as build_xml
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
__all__ = ["Schema", "NestedSchema", "build"]
|
|
20
31
|
|
|
21
32
|
|
|
22
|
-
|
|
33
|
+
# Type alias for nested schema references
|
|
34
|
+
# A schema node that can be nested within another schema, representing:
|
|
35
|
+
# - Schema: A valid schema object
|
|
36
|
+
# - Reference: A $ref reference to another schema
|
|
37
|
+
# - ValueSource[YAMLInvalidValue]: Invalid/malformed data preserved for validation
|
|
38
|
+
NestedSchema: TypeAlias = "Schema | Reference | ValueSource[YAMLInvalidValue]"
|
|
23
39
|
|
|
24
40
|
|
|
25
|
-
|
|
41
|
+
@dataclass(frozen=True, slots=True)
|
|
42
|
+
class Schema:
|
|
43
|
+
"""
|
|
44
|
+
Schema Object representation for OpenAPI 3.0.
|
|
45
|
+
|
|
46
|
+
The Schema Object allows the definition of input and output data types. These types can be
|
|
47
|
+
objects, but also primitives and arrays. This object is an extended subset of the JSON Schema
|
|
48
|
+
Specification Wright Draft 00.
|
|
49
|
+
|
|
50
|
+
Attributes:
|
|
51
|
+
root_node: The top-level node representing the entire Schema object in the original source file
|
|
52
|
+
|
|
53
|
+
# JSON Schema Core validation keywords
|
|
54
|
+
title: A title for the schema
|
|
55
|
+
multipleOf: A numeric instance is valid only if division by this value results in an integer
|
|
56
|
+
maximum: Upper limit for a numeric instance
|
|
57
|
+
exclusiveMaximum: If true, the value must be strictly less than maximum
|
|
58
|
+
minimum: Lower limit for a numeric instance
|
|
59
|
+
exclusiveMinimum: If true, the value must be strictly greater than minimum
|
|
60
|
+
maxLength: Maximum length of a string instance
|
|
61
|
+
minLength: Minimum length of a string instance
|
|
62
|
+
pattern: A string instance is valid if the regular expression matches the instance successfully
|
|
63
|
+
maxItems: Maximum number of items in an array instance
|
|
64
|
+
minItems: Minimum number of items in an array instance
|
|
65
|
+
uniqueItems: If true, array items must be unique
|
|
66
|
+
maxProperties: Maximum number of properties in an object instance
|
|
67
|
+
minProperties: Minimum number of properties in an object instance
|
|
68
|
+
required: List of required property names
|
|
69
|
+
enum: Fixed set of allowed values
|
|
70
|
+
|
|
71
|
+
# JSON Schema Type and Structure
|
|
72
|
+
type: Value type (string, number, integer, boolean, array, object)
|
|
73
|
+
allOf: Must be valid against all of the subschemas
|
|
74
|
+
oneOf: Must be valid against exactly one of the subschemas
|
|
75
|
+
anyOf: Must be valid against any of the subschemas
|
|
76
|
+
not_: Must not be valid against the given schema
|
|
77
|
+
items: Schema for array items (or array of schemas for tuple validation)
|
|
78
|
+
properties: Property name to schema mappings
|
|
79
|
+
additionalProperties: Schema for properties not defined in properties, or boolean to allow/disallow
|
|
80
|
+
|
|
81
|
+
# JSON Schema Metadata
|
|
82
|
+
description: A short description. CommonMark syntax MAY be used for rich text representation.
|
|
83
|
+
format: Additional format hint for the type (e.g., "email", "uuid", "uri", "date-time")
|
|
84
|
+
default: Default value
|
|
85
|
+
|
|
86
|
+
# OpenAPI-specific extensions
|
|
87
|
+
nullable: Allows sending a null value
|
|
88
|
+
discriminator: Adds support for polymorphism
|
|
89
|
+
readOnly: Relevant only for Schema "properties" definitions - sent in response but not in request
|
|
90
|
+
writeOnly: Relevant only for Schema "properties" definitions - sent in request but not in response
|
|
91
|
+
xml: Additional metadata for XML representations
|
|
92
|
+
externalDocs: Additional external documentation
|
|
93
|
+
example: Example of the media type
|
|
94
|
+
deprecated: Specifies that the schema is deprecated
|
|
95
|
+
|
|
96
|
+
extensions: Specification extensions (x-* fields)
|
|
26
97
|
"""
|
|
27
|
-
Represents a Schema Object from OpenAPI 3.0.4.
|
|
28
98
|
|
|
29
|
-
|
|
30
|
-
|
|
99
|
+
root_node: yaml.Node
|
|
100
|
+
|
|
101
|
+
# JSON Schema Core validation keywords
|
|
102
|
+
title: FieldSource[str] | None = fixed_field()
|
|
103
|
+
multipleOf: FieldSource[int | float] | None = fixed_field()
|
|
104
|
+
maximum: FieldSource[int | float] | None = fixed_field()
|
|
105
|
+
exclusiveMaximum: FieldSource[bool] | None = fixed_field()
|
|
106
|
+
minimum: FieldSource[int | float] | None = fixed_field()
|
|
107
|
+
exclusiveMinimum: FieldSource[bool] | None = fixed_field()
|
|
108
|
+
maxLength: FieldSource[int] | None = fixed_field()
|
|
109
|
+
minLength: FieldSource[int] | None = fixed_field()
|
|
110
|
+
pattern: FieldSource[str] | None = fixed_field()
|
|
111
|
+
maxItems: FieldSource[int] | None = fixed_field()
|
|
112
|
+
minItems: FieldSource[int] | None = fixed_field()
|
|
113
|
+
uniqueItems: FieldSource[bool] | None = fixed_field()
|
|
114
|
+
maxProperties: FieldSource[int] | None = fixed_field()
|
|
115
|
+
minProperties: FieldSource[int] | None = fixed_field()
|
|
116
|
+
required: FieldSource[list[str]] | None = fixed_field()
|
|
117
|
+
enum: FieldSource[list[YAMLValue]] | None = fixed_field()
|
|
118
|
+
|
|
119
|
+
# JSON Schema Type and Structure (nested schemas)
|
|
120
|
+
type: FieldSource[str] | None = fixed_field()
|
|
121
|
+
allOf: FieldSource[list[NestedSchema]] | None = fixed_field()
|
|
122
|
+
oneOf: FieldSource[list[NestedSchema]] | None = fixed_field()
|
|
123
|
+
anyOf: FieldSource[list[NestedSchema]] | None = fixed_field()
|
|
124
|
+
not_: FieldSource[NestedSchema] | None = fixed_field(metadata={"yaml_name": "not"})
|
|
125
|
+
items: FieldSource[NestedSchema] | None = fixed_field()
|
|
126
|
+
properties: FieldSource[dict[KeySource[str], ValueSource[NestedSchema]]] | None = fixed_field()
|
|
127
|
+
additionalProperties: FieldSource["bool | NestedSchema"] | None = fixed_field()
|
|
31
128
|
|
|
32
|
-
|
|
129
|
+
# JSON Schema Metadata
|
|
130
|
+
description: FieldSource[str] | None = fixed_field()
|
|
131
|
+
format: FieldSource[str] | None = fixed_field()
|
|
132
|
+
default: FieldSource[YAMLValue] | None = fixed_field()
|
|
133
|
+
|
|
134
|
+
# OpenAPI-specific extensions
|
|
135
|
+
nullable: FieldSource[bool] | None = fixed_field()
|
|
136
|
+
discriminator: FieldSource[Discriminator] | None = fixed_field()
|
|
137
|
+
readOnly: FieldSource[bool] | None = fixed_field()
|
|
138
|
+
writeOnly: FieldSource[bool] | None = fixed_field()
|
|
139
|
+
xml: FieldSource[XML] | None = fixed_field()
|
|
140
|
+
externalDocs: FieldSource[ExternalDocumentation] | None = fixed_field()
|
|
141
|
+
example: FieldSource[YAMLValue] | None = fixed_field()
|
|
142
|
+
deprecated: FieldSource[bool] | None = fixed_field()
|
|
143
|
+
|
|
144
|
+
extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def build(
|
|
148
|
+
root: yaml.Node, context: Context | None = None
|
|
149
|
+
) -> "Schema | ValueSource[YAMLInvalidValue]":
|
|
150
|
+
"""
|
|
151
|
+
Build a Schema object from a YAML node.
|
|
152
|
+
|
|
153
|
+
Preserves all source data as-is, regardless of type. This is a low-level/plumbing
|
|
154
|
+
model that provides complete source fidelity for inspection and validation.
|
|
155
|
+
|
|
156
|
+
Note: Schema is self-referential (can contain other Schema objects in allOf, oneOf, anyOf, not,
|
|
157
|
+
items, properties, additionalProperties). The builder handles nested Schema objects by preserving
|
|
158
|
+
them as raw YAML values, letting validation layers interpret them.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
root: The YAML node to parse (should be a MappingNode)
|
|
162
|
+
context: Optional parsing context. If None, a default context will be created.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
A Schema object if the node is valid, or a ValueSource containing
|
|
166
|
+
the invalid data if the root is not a MappingNode (preserving the invalid data
|
|
167
|
+
and its source location for validation).
|
|
33
168
|
|
|
34
169
|
Example:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
>>> schema.type
|
|
42
|
-
'string'
|
|
43
|
-
|
|
44
|
-
>>> # Object schema with properties
|
|
45
|
-
>>> schema = Schema({
|
|
46
|
-
... "type": "object",
|
|
47
|
-
... "required": ["name"],
|
|
48
|
-
... "properties": {
|
|
49
|
-
... "name": {"type": "string"},
|
|
50
|
-
... "age": {"type": "integer"}
|
|
51
|
-
... }
|
|
52
|
-
... })
|
|
53
|
-
>>> schema.required
|
|
54
|
-
['name']
|
|
55
|
-
|
|
56
|
-
>>> # Schema with discriminator
|
|
57
|
-
>>> schema = Schema({
|
|
58
|
-
... "discriminator": {
|
|
59
|
-
... "propertyName": "petType"
|
|
60
|
-
... }
|
|
61
|
-
... })
|
|
62
|
-
>>> schema.discriminator.property_name
|
|
63
|
-
'petType'
|
|
170
|
+
from ruamel.yaml import YAML
|
|
171
|
+
yaml = YAML()
|
|
172
|
+
root = yaml.compose("type: string\\nminLength: 1\\nmaxLength: 100")
|
|
173
|
+
schema = build(root)
|
|
174
|
+
assert schema.type.value == 'string'
|
|
175
|
+
assert schema.minLength.value == 1
|
|
64
176
|
"""
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
177
|
+
# Initialize context once at the beginning
|
|
178
|
+
if context is None:
|
|
179
|
+
context = Context()
|
|
180
|
+
|
|
181
|
+
if not isinstance(root, yaml.MappingNode):
|
|
182
|
+
# Preserve invalid root data instead of returning None
|
|
183
|
+
value = context.yaml_constructor.construct_object(root, deep=True)
|
|
184
|
+
return ValueSource(value=value, value_node=root)
|
|
185
|
+
|
|
186
|
+
# Build YAML name to Python field name mapping
|
|
187
|
+
_fixed_fields = fixed_fields(Schema)
|
|
188
|
+
yaml_to_field = {
|
|
189
|
+
f.metadata.get("yaml_name", fname): fname for fname, f in _fixed_fields.items()
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
# Accumulate all field values in a single pass
|
|
193
|
+
field_values: dict[str, Any] = {}
|
|
194
|
+
|
|
195
|
+
for key_node, value_node in root.value:
|
|
196
|
+
key = context.yaml_constructor.construct_yaml_str(key_node)
|
|
197
|
+
|
|
198
|
+
# Skip extension fields - handled separately at the end
|
|
199
|
+
if key.startswith("x-"):
|
|
200
|
+
continue
|
|
201
|
+
|
|
202
|
+
# Map YAML key to Python field name
|
|
203
|
+
field_name = yaml_to_field.get(key)
|
|
204
|
+
if not field_name:
|
|
205
|
+
continue
|
|
206
|
+
|
|
207
|
+
# Get field metadata
|
|
208
|
+
field_info = _fixed_fields[field_name]
|
|
209
|
+
field_type_args = set(get_args(field_info.type))
|
|
210
|
+
|
|
211
|
+
# Simple scalar fields (handled like build_model does)
|
|
212
|
+
if field_type_args & {
|
|
213
|
+
FieldSource[str],
|
|
214
|
+
FieldSource[bool],
|
|
215
|
+
FieldSource[int],
|
|
216
|
+
FieldSource[int | float],
|
|
217
|
+
FieldSource[YAMLValue],
|
|
218
|
+
FieldSource[list[str]],
|
|
219
|
+
FieldSource[list[YAMLValue]],
|
|
220
|
+
}:
|
|
221
|
+
value = context.yaml_constructor.construct_object(value_node, deep=True)
|
|
222
|
+
field_values[field_name] = FieldSource(
|
|
223
|
+
value=value, key_node=key_node, value_node=value_node
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
# Recursive schema list fields (allOf, oneOf, anyOf)
|
|
227
|
+
elif key in ("allOf", "oneOf", "anyOf"):
|
|
228
|
+
if isinstance(value_node, yaml.SequenceNode):
|
|
229
|
+
schemas = []
|
|
230
|
+
for item_node in value_node.value:
|
|
231
|
+
schema_or_ref = _build_schema_or_reference(item_node, context)
|
|
232
|
+
schemas.append(schema_or_ref)
|
|
233
|
+
field_values[field_name] = FieldSource(
|
|
234
|
+
value=schemas, key_node=key_node, value_node=value_node
|
|
235
|
+
)
|
|
236
|
+
else:
|
|
237
|
+
# Not a sequence - preserve as-is for validation
|
|
238
|
+
value = context.yaml_constructor.construct_object(value_node, deep=True)
|
|
239
|
+
field_values[field_name] = FieldSource(
|
|
240
|
+
value=value, key_node=key_node, value_node=value_node
|
|
241
|
+
)
|
|
242
|
+
# Recursive schema single fields (not, items)
|
|
243
|
+
elif key in ("not", "items"):
|
|
244
|
+
schema_or_ref = _build_schema_or_reference(value_node, context)
|
|
245
|
+
field_values[field_name] = FieldSource(
|
|
246
|
+
value=schema_or_ref, key_node=key_node, value_node=value_node
|
|
247
|
+
)
|
|
248
|
+
# additionalProperties (boolean | schema | reference)
|
|
249
|
+
elif key == "additionalProperties":
|
|
250
|
+
# Check if it's a boolean or a schema/reference
|
|
251
|
+
if (
|
|
252
|
+
isinstance(value_node, yaml.ScalarNode)
|
|
253
|
+
and value_node.tag == "tag:yaml.org,2002:bool"
|
|
254
|
+
):
|
|
255
|
+
value = context.yaml_constructor.construct_object(value_node)
|
|
256
|
+
field_values[field_name] = FieldSource(
|
|
257
|
+
value=value, key_node=key_node, value_node=value_node
|
|
258
|
+
)
|
|
259
|
+
else:
|
|
260
|
+
# It's a schema or reference
|
|
261
|
+
schema_or_ref = _build_schema_or_reference(value_node, context)
|
|
262
|
+
field_values[field_name] = FieldSource(
|
|
263
|
+
value=schema_or_ref, key_node=key_node, value_node=value_node
|
|
264
|
+
)
|
|
265
|
+
# properties (dict[KeySource[str], ValueSource[NestedSchema]])
|
|
266
|
+
elif key == "properties":
|
|
267
|
+
if isinstance(value_node, yaml.MappingNode):
|
|
268
|
+
properties_dict: dict[KeySource[str], ValueSource[NestedSchema]] = {}
|
|
269
|
+
for prop_key_node, prop_value_node in value_node.value:
|
|
270
|
+
prop_key = context.yaml_constructor.construct_yaml_str(prop_key_node)
|
|
271
|
+
# Recursively build each property schema
|
|
272
|
+
prop_schema_or_ref = _build_schema_or_reference(prop_value_node, context)
|
|
273
|
+
properties_dict[KeySource(value=prop_key, key_node=prop_key_node)] = (
|
|
274
|
+
ValueSource(value=prop_schema_or_ref, value_node=prop_value_node)
|
|
275
|
+
)
|
|
276
|
+
field_values[field_name] = FieldSource(
|
|
277
|
+
value=properties_dict, key_node=key_node, value_node=value_node
|
|
278
|
+
)
|
|
279
|
+
else:
|
|
280
|
+
# Not a mapping - preserve as-is for validation
|
|
281
|
+
value = context.yaml_constructor.construct_object(value_node, deep=True)
|
|
282
|
+
field_values[field_name] = FieldSource(
|
|
283
|
+
value=value, key_node=key_node, value_node=value_node
|
|
284
|
+
)
|
|
285
|
+
# Nested objects (discriminator, xml, externalDocs)
|
|
286
|
+
elif key == "discriminator":
|
|
287
|
+
field_values[field_name] = FieldSource(
|
|
288
|
+
value=build_discriminator(value_node, context=context),
|
|
289
|
+
key_node=key_node,
|
|
290
|
+
value_node=value_node,
|
|
291
|
+
)
|
|
292
|
+
elif key == "xml":
|
|
293
|
+
field_values[field_name] = FieldSource(
|
|
294
|
+
value=build_xml(value_node, context=context),
|
|
295
|
+
key_node=key_node,
|
|
296
|
+
value_node=value_node,
|
|
297
|
+
)
|
|
298
|
+
elif key == "externalDocs":
|
|
299
|
+
field_values[field_name] = FieldSource(
|
|
300
|
+
value=build_external_documentation(value_node, context=context),
|
|
301
|
+
key_node=key_node,
|
|
302
|
+
value_node=value_node,
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
# Build and return the Schema instance (single constructor call)
|
|
306
|
+
return Schema(
|
|
307
|
+
root_node=root,
|
|
308
|
+
**field_values,
|
|
309
|
+
extensions=extract_extension_fields(root, context),
|
|
109
310
|
)
|
|
110
311
|
|
|
111
|
-
def __init__(self, data: Mapping[str, Any] | None = None):
|
|
112
|
-
"""
|
|
113
|
-
Initialize a Schema object.
|
|
114
|
-
|
|
115
|
-
Automatically marshals nested objects (discriminator, xml, externalDocs, and nested schemas).
|
|
116
|
-
|
|
117
|
-
Args:
|
|
118
|
-
data: Optional mapping to initialize the object with
|
|
119
|
-
"""
|
|
120
|
-
super().__init__()
|
|
121
|
-
if data:
|
|
122
|
-
for key, value in data.items():
|
|
123
|
-
# Marshal specific nested objects
|
|
124
|
-
if (
|
|
125
|
-
key == "discriminator"
|
|
126
|
-
and isinstance(value, Mapping)
|
|
127
|
-
and not isinstance(value, Discriminator)
|
|
128
|
-
):
|
|
129
|
-
self[key] = Discriminator(value)
|
|
130
|
-
elif key == "xml" and isinstance(value, Mapping) and not isinstance(value, XML):
|
|
131
|
-
self[key] = XML(value)
|
|
132
|
-
elif (
|
|
133
|
-
key == "externalDocs"
|
|
134
|
-
and isinstance(value, Mapping)
|
|
135
|
-
and not isinstance(value, ExternalDocumentation)
|
|
136
|
-
):
|
|
137
|
-
self[key] = ExternalDocumentation(value)
|
|
138
|
-
# Unmarshal schema composition lists (allOf, oneOf, anyOf)
|
|
139
|
-
elif (
|
|
140
|
-
key in ("allOf", "oneOf", "anyOf")
|
|
141
|
-
and isinstance(value, Sequence)
|
|
142
|
-
and not isinstance(value, str)
|
|
143
|
-
):
|
|
144
|
-
self[key] = [self._unmarshal_schema_or_reference(item) for item in value]
|
|
145
|
-
# Unmarshal single schema fields (not, items)
|
|
146
|
-
elif key in ("not", "items") and isinstance(value, Mapping):
|
|
147
|
-
self[key] = self._unmarshal_schema_or_reference(value)
|
|
148
|
-
# Unmarshal properties dict
|
|
149
|
-
elif key == "properties" and isinstance(value, Mapping):
|
|
150
|
-
self[key] = {
|
|
151
|
-
k: self._unmarshal_schema_or_reference(v) for k, v in value.items()
|
|
152
|
-
}
|
|
153
|
-
# Unmarshal additionalProperties (can be bool or schema)
|
|
154
|
-
elif key == "additionalProperties":
|
|
155
|
-
if isinstance(value, bool):
|
|
156
|
-
self[key] = value
|
|
157
|
-
elif isinstance(value, Mapping):
|
|
158
|
-
self[key] = self._unmarshal_schema_or_reference(value)
|
|
159
|
-
else:
|
|
160
|
-
self[key] = self._copy_value(value)
|
|
161
|
-
else:
|
|
162
|
-
# Store as-is (with defensive copy)
|
|
163
|
-
self[key] = self._copy_value(value)
|
|
164
|
-
|
|
165
|
-
# JSON Schema Core - Metadata
|
|
166
|
-
@property
|
|
167
|
-
def title(self) -> str | None:
|
|
168
|
-
"""A title for the schema."""
|
|
169
|
-
return self.get("title")
|
|
170
|
-
|
|
171
|
-
@title.setter
|
|
172
|
-
def title(self, value: str | None) -> None:
|
|
173
|
-
if value is None:
|
|
174
|
-
self.pop("title", None)
|
|
175
|
-
else:
|
|
176
|
-
self["title"] = value
|
|
177
|
-
|
|
178
|
-
# JSON Schema Core - Numeric validation
|
|
179
|
-
@property
|
|
180
|
-
def multiple_of(self) -> float | int | None:
|
|
181
|
-
"""Value must be multiple of this number."""
|
|
182
|
-
return self.get("multipleOf")
|
|
183
|
-
|
|
184
|
-
@multiple_of.setter
|
|
185
|
-
def multiple_of(self, value: float | int | None) -> None:
|
|
186
|
-
if value is None:
|
|
187
|
-
self.pop("multipleOf", None)
|
|
188
|
-
else:
|
|
189
|
-
self["multipleOf"] = value
|
|
190
|
-
|
|
191
|
-
@property
|
|
192
|
-
def maximum(self) -> float | int | None:
|
|
193
|
-
"""Maximum value (inclusive)."""
|
|
194
|
-
return self.get("maximum")
|
|
195
|
-
|
|
196
|
-
@maximum.setter
|
|
197
|
-
def maximum(self, value: float | int | None) -> None:
|
|
198
|
-
if value is None:
|
|
199
|
-
self.pop("maximum", None)
|
|
200
|
-
else:
|
|
201
|
-
self["maximum"] = value
|
|
202
|
-
|
|
203
|
-
@property
|
|
204
|
-
def exclusive_maximum(self) -> float | int | None:
|
|
205
|
-
"""Maximum value (exclusive)."""
|
|
206
|
-
return self.get("exclusiveMaximum")
|
|
207
|
-
|
|
208
|
-
@exclusive_maximum.setter
|
|
209
|
-
def exclusive_maximum(self, value: float | int | None) -> None:
|
|
210
|
-
if value is None:
|
|
211
|
-
self.pop("exclusiveMaximum", None)
|
|
212
|
-
else:
|
|
213
|
-
self["exclusiveMaximum"] = value
|
|
214
|
-
|
|
215
|
-
@property
|
|
216
|
-
def minimum(self) -> float | int | None:
|
|
217
|
-
"""Minimum value (inclusive)."""
|
|
218
|
-
return self.get("minimum")
|
|
219
|
-
|
|
220
|
-
@minimum.setter
|
|
221
|
-
def minimum(self, value: float | int | None) -> None:
|
|
222
|
-
if value is None:
|
|
223
|
-
self.pop("minimum", None)
|
|
224
|
-
else:
|
|
225
|
-
self["minimum"] = value
|
|
226
|
-
|
|
227
|
-
@property
|
|
228
|
-
def exclusive_minimum(self) -> float | int | None:
|
|
229
|
-
"""Minimum value (exclusive)."""
|
|
230
|
-
return self.get("exclusiveMinimum")
|
|
231
|
-
|
|
232
|
-
@exclusive_minimum.setter
|
|
233
|
-
def exclusive_minimum(self, value: float | int | None) -> None:
|
|
234
|
-
if value is None:
|
|
235
|
-
self.pop("exclusiveMinimum", None)
|
|
236
|
-
else:
|
|
237
|
-
self["exclusiveMinimum"] = value
|
|
238
|
-
|
|
239
|
-
# String validation properties
|
|
240
|
-
@property
|
|
241
|
-
def max_length(self) -> int | None:
|
|
242
|
-
"""Maximum string length."""
|
|
243
|
-
return self.get("maxLength")
|
|
244
|
-
|
|
245
|
-
@max_length.setter
|
|
246
|
-
def max_length(self, value: int | None) -> None:
|
|
247
|
-
if value is None:
|
|
248
|
-
self.pop("maxLength", None)
|
|
249
|
-
else:
|
|
250
|
-
self["maxLength"] = value
|
|
251
|
-
|
|
252
|
-
@property
|
|
253
|
-
def min_length(self) -> int | None:
|
|
254
|
-
"""Minimum string length."""
|
|
255
|
-
return self.get("minLength")
|
|
256
|
-
|
|
257
|
-
@min_length.setter
|
|
258
|
-
def min_length(self, value: int | None) -> None:
|
|
259
|
-
if value is None:
|
|
260
|
-
self.pop("minLength", None)
|
|
261
|
-
else:
|
|
262
|
-
self["minLength"] = value
|
|
263
|
-
|
|
264
|
-
@property
|
|
265
|
-
def pattern(self) -> str | None:
|
|
266
|
-
"""Regular expression pattern for string validation."""
|
|
267
|
-
return self.get("pattern")
|
|
268
|
-
|
|
269
|
-
@pattern.setter
|
|
270
|
-
def pattern(self, value: str | None) -> None:
|
|
271
|
-
if value is None:
|
|
272
|
-
self.pop("pattern", None)
|
|
273
|
-
else:
|
|
274
|
-
self["pattern"] = value
|
|
275
|
-
|
|
276
|
-
# Array validation properties
|
|
277
|
-
@property
|
|
278
|
-
def max_items(self) -> int | None:
|
|
279
|
-
"""Maximum number of array items."""
|
|
280
|
-
return self.get("maxItems")
|
|
281
|
-
|
|
282
|
-
@max_items.setter
|
|
283
|
-
def max_items(self, value: int | None) -> None:
|
|
284
|
-
if value is None:
|
|
285
|
-
self.pop("maxItems", None)
|
|
286
|
-
else:
|
|
287
|
-
self["maxItems"] = value
|
|
288
|
-
|
|
289
|
-
@property
|
|
290
|
-
def min_items(self) -> int | None:
|
|
291
|
-
"""Minimum number of array items."""
|
|
292
|
-
return self.get("minItems")
|
|
293
|
-
|
|
294
|
-
@min_items.setter
|
|
295
|
-
def min_items(self, value: int | None) -> None:
|
|
296
|
-
if value is None:
|
|
297
|
-
self.pop("minItems", None)
|
|
298
|
-
else:
|
|
299
|
-
self["minItems"] = value
|
|
300
|
-
|
|
301
|
-
@property
|
|
302
|
-
def unique_items(self) -> bool | None:
|
|
303
|
-
"""Whether array items must be unique."""
|
|
304
|
-
return self.get("uniqueItems")
|
|
305
|
-
|
|
306
|
-
@unique_items.setter
|
|
307
|
-
def unique_items(self, value: bool | None) -> None:
|
|
308
|
-
if value is None:
|
|
309
|
-
self.pop("uniqueItems", None)
|
|
310
|
-
else:
|
|
311
|
-
self["uniqueItems"] = value
|
|
312
|
-
|
|
313
|
-
# JSON Schema Core - Object validation
|
|
314
|
-
@property
|
|
315
|
-
def max_properties(self) -> int | None:
|
|
316
|
-
"""Maximum number of object properties."""
|
|
317
|
-
return self.get("maxProperties")
|
|
318
|
-
|
|
319
|
-
@max_properties.setter
|
|
320
|
-
def max_properties(self, value: int | None) -> None:
|
|
321
|
-
if value is None:
|
|
322
|
-
self.pop("maxProperties", None)
|
|
323
|
-
else:
|
|
324
|
-
self["maxProperties"] = value
|
|
325
|
-
|
|
326
|
-
@property
|
|
327
|
-
def min_properties(self) -> int | None:
|
|
328
|
-
"""Minimum number of object properties."""
|
|
329
|
-
return self.get("minProperties")
|
|
330
|
-
|
|
331
|
-
@min_properties.setter
|
|
332
|
-
def min_properties(self, value: int | None) -> None:
|
|
333
|
-
if value is None:
|
|
334
|
-
self.pop("minProperties", None)
|
|
335
|
-
else:
|
|
336
|
-
self["minProperties"] = value
|
|
337
|
-
|
|
338
|
-
@property
|
|
339
|
-
def required(self) -> list[str] | None:
|
|
340
|
-
"""List of required property names (for object types)."""
|
|
341
|
-
return self.get("required")
|
|
342
|
-
|
|
343
|
-
@required.setter
|
|
344
|
-
def required(self, value: list[str] | None) -> None:
|
|
345
|
-
if value is None:
|
|
346
|
-
self.pop("required", None)
|
|
347
|
-
else:
|
|
348
|
-
self["required"] = value
|
|
349
|
-
|
|
350
|
-
@property
|
|
351
|
-
def enum(self) -> list[Any] | None:
|
|
352
|
-
"""Allowed values."""
|
|
353
|
-
return self.get("enum")
|
|
354
|
-
|
|
355
|
-
@enum.setter
|
|
356
|
-
def enum(self, value: list[Any] | None) -> None:
|
|
357
|
-
if value is None:
|
|
358
|
-
self.pop("enum", None)
|
|
359
|
-
else:
|
|
360
|
-
self["enum"] = value
|
|
361
|
-
|
|
362
|
-
# JSON Schema Type
|
|
363
|
-
@property
|
|
364
|
-
def type(self) -> str | None:
|
|
365
|
-
"""Type of the schema (string, number, integer, boolean, array, object, null)."""
|
|
366
|
-
return self.get("type")
|
|
367
|
-
|
|
368
|
-
@type.setter
|
|
369
|
-
def type(self, value: str | None) -> None:
|
|
370
|
-
if value is None:
|
|
371
|
-
self.pop("type", None)
|
|
372
|
-
else:
|
|
373
|
-
self["type"] = value
|
|
374
|
-
|
|
375
|
-
@property
|
|
376
|
-
def all_of(self) -> list[Schema | Reference] | None:
|
|
377
|
-
"""Schemas that must all be valid (list of Schema or Reference objects)."""
|
|
378
|
-
return self.get("allOf")
|
|
379
|
-
|
|
380
|
-
@all_of.setter
|
|
381
|
-
def all_of(self, value: list[Schema | Reference] | None) -> None:
|
|
382
|
-
if value is None:
|
|
383
|
-
self.pop("allOf", None)
|
|
384
|
-
else:
|
|
385
|
-
self["allOf"] = value
|
|
386
|
-
|
|
387
|
-
@property
|
|
388
|
-
def one_of(self) -> list[Schema | Reference] | None:
|
|
389
|
-
"""Schemas where exactly one must be valid (list of Schema or Reference objects)."""
|
|
390
|
-
return self.get("oneOf")
|
|
391
|
-
|
|
392
|
-
@one_of.setter
|
|
393
|
-
def one_of(self, value: list[Schema | Reference] | None) -> None:
|
|
394
|
-
if value is None:
|
|
395
|
-
self.pop("oneOf", None)
|
|
396
|
-
else:
|
|
397
|
-
self["oneOf"] = value
|
|
398
|
-
|
|
399
|
-
@property
|
|
400
|
-
def any_of(self) -> list[Schema | Reference] | None:
|
|
401
|
-
"""Schemas where at least one must be valid (list of Schema or Reference objects)."""
|
|
402
|
-
return self.get("anyOf")
|
|
403
|
-
|
|
404
|
-
@any_of.setter
|
|
405
|
-
def any_of(self, value: list[Schema | Reference] | None) -> None:
|
|
406
|
-
if value is None:
|
|
407
|
-
self.pop("anyOf", None)
|
|
408
|
-
else:
|
|
409
|
-
self["anyOf"] = value
|
|
410
|
-
|
|
411
|
-
@property
|
|
412
|
-
def not_(self) -> Schema | Reference | None:
|
|
413
|
-
"""Schema that must NOT be valid."""
|
|
414
|
-
return self.get("not")
|
|
415
|
-
|
|
416
|
-
@not_.setter
|
|
417
|
-
def not_(self, value: Schema | Reference | None) -> None:
|
|
418
|
-
if value is None:
|
|
419
|
-
self.pop("not", None)
|
|
420
|
-
else:
|
|
421
|
-
self["not"] = value
|
|
422
|
-
|
|
423
|
-
@property
|
|
424
|
-
def items_(self) -> Schema | Reference | None:
|
|
425
|
-
"""
|
|
426
|
-
Schema for array items (Schema or Reference object).
|
|
427
|
-
|
|
428
|
-
Note: Property named 'items_' (with underscore) to avoid conflict with
|
|
429
|
-
MutableMapping.items() method. Dict access still uses the standard field name:
|
|
430
|
-
schema["items"].
|
|
431
|
-
"""
|
|
432
|
-
return self.get("items")
|
|
433
|
-
|
|
434
|
-
@items_.setter
|
|
435
|
-
def items_(self, value: Schema | Reference | None) -> None:
|
|
436
|
-
if value is None:
|
|
437
|
-
self.pop("items", None)
|
|
438
|
-
else:
|
|
439
|
-
self["items"] = value
|
|
440
|
-
|
|
441
|
-
@property
|
|
442
|
-
def properties(self) -> dict[str, Schema | Reference] | None:
|
|
443
|
-
"""Object properties (map of property name to Schema or Reference)."""
|
|
444
|
-
return self.get("properties")
|
|
445
|
-
|
|
446
|
-
@properties.setter
|
|
447
|
-
def properties(self, value: Mapping[str, Schema | Reference] | None) -> None:
|
|
448
|
-
if value is None:
|
|
449
|
-
self.pop("properties", None)
|
|
450
|
-
else:
|
|
451
|
-
self["properties"] = dict(value) if isinstance(value, Mapping) else value
|
|
452
|
-
|
|
453
|
-
@property
|
|
454
|
-
def additional_properties(self) -> bool | Schema | Reference | None:
|
|
455
|
-
"""Schema for additional properties (bool or Schema or Reference object)."""
|
|
456
|
-
return self.get("additionalProperties")
|
|
457
|
-
|
|
458
|
-
@additional_properties.setter
|
|
459
|
-
def additional_properties(self, value: bool | Schema | Reference | None) -> None:
|
|
460
|
-
if value is None:
|
|
461
|
-
self.pop("additionalProperties", None)
|
|
462
|
-
else:
|
|
463
|
-
self["additionalProperties"] = value
|
|
464
312
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
self["format"] = value
|
|
489
|
-
|
|
490
|
-
@property
|
|
491
|
-
def default(self) -> Any:
|
|
492
|
-
"""Default value."""
|
|
493
|
-
return self.get("default")
|
|
494
|
-
|
|
495
|
-
@default.setter
|
|
496
|
-
def default(self, value: Any) -> None:
|
|
497
|
-
if value is None:
|
|
498
|
-
self.pop("default", None)
|
|
499
|
-
else:
|
|
500
|
-
self["default"] = value
|
|
501
|
-
|
|
502
|
-
# OpenAPI Extensions
|
|
503
|
-
@property
|
|
504
|
-
def nullable(self) -> bool | None:
|
|
505
|
-
"""Whether the value can be null (OpenAPI extension)."""
|
|
506
|
-
return self.get("nullable")
|
|
507
|
-
|
|
508
|
-
@nullable.setter
|
|
509
|
-
def nullable(self, value: bool | None) -> None:
|
|
510
|
-
if value is None:
|
|
511
|
-
self.pop("nullable", None)
|
|
512
|
-
else:
|
|
513
|
-
self["nullable"] = value
|
|
514
|
-
|
|
515
|
-
@property
|
|
516
|
-
def discriminator(self) -> Discriminator | None:
|
|
517
|
-
"""Discriminator for polymorphism (OpenAPI extension)."""
|
|
518
|
-
return self.get("discriminator")
|
|
519
|
-
|
|
520
|
-
@discriminator.setter
|
|
521
|
-
def discriminator(self, value: Discriminator | None) -> None:
|
|
522
|
-
if value is None:
|
|
523
|
-
self.pop("discriminator", None)
|
|
524
|
-
else:
|
|
525
|
-
self["discriminator"] = value
|
|
526
|
-
|
|
527
|
-
@property
|
|
528
|
-
def read_only(self) -> bool | None:
|
|
529
|
-
"""Whether the property is read-only (OpenAPI extension)."""
|
|
530
|
-
return self.get("readOnly")
|
|
531
|
-
|
|
532
|
-
@read_only.setter
|
|
533
|
-
def read_only(self, value: bool | None) -> None:
|
|
534
|
-
if value is None:
|
|
535
|
-
self.pop("readOnly", None)
|
|
536
|
-
else:
|
|
537
|
-
self["readOnly"] = value
|
|
538
|
-
|
|
539
|
-
@property
|
|
540
|
-
def write_only(self) -> bool | None:
|
|
541
|
-
"""Whether the property is write-only (OpenAPI extension)."""
|
|
542
|
-
return self.get("writeOnly")
|
|
543
|
-
|
|
544
|
-
@write_only.setter
|
|
545
|
-
def write_only(self, value: bool | None) -> None:
|
|
546
|
-
if value is None:
|
|
547
|
-
self.pop("writeOnly", None)
|
|
548
|
-
else:
|
|
549
|
-
self["writeOnly"] = value
|
|
550
|
-
|
|
551
|
-
@property
|
|
552
|
-
def xml(self) -> XML | None:
|
|
553
|
-
"""XML representation metadata (OpenAPI extension)."""
|
|
554
|
-
return self.get("xml")
|
|
555
|
-
|
|
556
|
-
@xml.setter
|
|
557
|
-
def xml(self, value: XML | None) -> None:
|
|
558
|
-
if value is None:
|
|
559
|
-
self.pop("xml", None)
|
|
560
|
-
else:
|
|
561
|
-
self["xml"] = value
|
|
562
|
-
|
|
563
|
-
@property
|
|
564
|
-
def external_docs(self) -> ExternalDocumentation | None:
|
|
565
|
-
"""External documentation (OpenAPI extension)."""
|
|
566
|
-
return self.get("externalDocs")
|
|
567
|
-
|
|
568
|
-
@external_docs.setter
|
|
569
|
-
def external_docs(self, value: ExternalDocumentation | None) -> None:
|
|
570
|
-
if value is None:
|
|
571
|
-
self.pop("externalDocs", None)
|
|
572
|
-
else:
|
|
573
|
-
self["externalDocs"] = value
|
|
574
|
-
|
|
575
|
-
@property
|
|
576
|
-
def example(self) -> Any:
|
|
577
|
-
"""Example value (OpenAPI extension)."""
|
|
578
|
-
return self.get("example")
|
|
579
|
-
|
|
580
|
-
@example.setter
|
|
581
|
-
def example(self, value: Any) -> None:
|
|
582
|
-
if value is None:
|
|
583
|
-
self.pop("example", None)
|
|
584
|
-
else:
|
|
585
|
-
self["example"] = value
|
|
586
|
-
|
|
587
|
-
@property
|
|
588
|
-
def deprecated(self) -> bool | None:
|
|
589
|
-
"""Whether the schema is deprecated (OpenAPI extension)."""
|
|
590
|
-
return self.get("deprecated")
|
|
591
|
-
|
|
592
|
-
@deprecated.setter
|
|
593
|
-
def deprecated(self, value: bool | None) -> None:
|
|
594
|
-
if value is None:
|
|
595
|
-
self.pop("deprecated", None)
|
|
596
|
-
else:
|
|
597
|
-
self["deprecated"] = value
|
|
598
|
-
|
|
599
|
-
# Helper methods
|
|
600
|
-
@classmethod
|
|
601
|
-
def _unmarshal_schema_or_reference(cls, value: Any) -> Schema | Reference | Any:
|
|
602
|
-
"""
|
|
603
|
-
Unmarshal a value into Schema or Reference if it's a Mapping.
|
|
604
|
-
|
|
605
|
-
Converts raw data (dict/Mapping) into Schema or Reference objects during construction.
|
|
606
|
-
Uses the actual class (or subclass) for creating Schema instances.
|
|
607
|
-
|
|
608
|
-
Args:
|
|
609
|
-
value: Value to unmarshal
|
|
610
|
-
|
|
611
|
-
Returns:
|
|
612
|
-
Schema or Reference instance, or value as-is
|
|
613
|
-
"""
|
|
614
|
-
if not isinstance(value, Mapping):
|
|
615
|
-
return value
|
|
616
|
-
|
|
617
|
-
# Already unmarshaled
|
|
618
|
-
if isinstance(value, (Schema, Reference)):
|
|
619
|
-
return value
|
|
620
|
-
|
|
621
|
-
# Check if it's a reference (has $ref field)
|
|
622
|
-
if "$ref" in value:
|
|
623
|
-
return Reference(value)
|
|
624
|
-
else:
|
|
625
|
-
# Otherwise treat as Schema (or subclass)
|
|
626
|
-
return cls(value)
|
|
313
|
+
def _build_schema_or_reference(node: yaml.Node, context: Context) -> NestedSchema:
|
|
314
|
+
"""
|
|
315
|
+
Build either a Schema or Reference from a YAML node.
|
|
316
|
+
|
|
317
|
+
This helper handles the polymorphic nature of OpenAPI where many fields
|
|
318
|
+
can contain either a Schema object or a Reference object ($ref).
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
node: The YAML node to parse
|
|
322
|
+
context: Parsing context
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
A Schema, Reference, or ValueSource if the node is invalid
|
|
326
|
+
"""
|
|
327
|
+
# Check if it's a reference (has $ref key)
|
|
328
|
+
if isinstance(node, yaml.MappingNode):
|
|
329
|
+
for key_node, _ in node.value:
|
|
330
|
+
key = context.yaml_constructor.construct_yaml_str(key_node)
|
|
331
|
+
if key == "$ref":
|
|
332
|
+
return build_reference(node, context)
|
|
333
|
+
|
|
334
|
+
# Otherwise, try to build as Schema (which may be recursive)
|
|
335
|
+
return build(node, context)
|