jentic-openapi-datamodels 1.0.0a18__py3-none-any.whl → 1.0.0a20__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 (73) hide show
  1. jentic/apitools/openapi/datamodels/low/extractors.py +3 -3
  2. jentic/apitools/openapi/datamodels/low/v30/__init__.py +76 -0
  3. jentic/apitools/openapi/datamodels/low/v30/builders/__init__.py +312 -0
  4. jentic/apitools/openapi/datamodels/low/v30/callback.py +131 -0
  5. jentic/apitools/openapi/datamodels/low/v30/components.py +236 -0
  6. jentic/apitools/openapi/datamodels/low/v30/contact.py +4 -10
  7. jentic/apitools/openapi/datamodels/low/v30/discriminator.py +4 -9
  8. jentic/apitools/openapi/datamodels/low/v30/encoding.py +81 -0
  9. jentic/apitools/openapi/datamodels/low/v30/example.py +91 -0
  10. jentic/apitools/openapi/datamodels/low/v30/external_documentation.py +4 -10
  11. jentic/apitools/openapi/datamodels/low/v30/header.py +120 -0
  12. jentic/apitools/openapi/datamodels/low/v30/info.py +14 -23
  13. jentic/apitools/openapi/datamodels/low/v30/license.py +4 -10
  14. jentic/apitools/openapi/datamodels/low/v30/link.py +141 -0
  15. jentic/apitools/openapi/datamodels/low/v30/media_type.py +110 -0
  16. jentic/apitools/openapi/datamodels/low/v30/oauth_flow.py +4 -10
  17. jentic/apitools/openapi/datamodels/low/v30/oauth_flows.py +7 -15
  18. jentic/apitools/openapi/datamodels/low/v30/openapi.py +149 -0
  19. jentic/apitools/openapi/datamodels/low/v30/operation.py +134 -0
  20. jentic/apitools/openapi/datamodels/low/v30/parameter.py +123 -0
  21. jentic/apitools/openapi/datamodels/low/v30/path_item.py +125 -0
  22. jentic/apitools/openapi/datamodels/low/v30/paths.py +108 -0
  23. jentic/apitools/openapi/datamodels/low/v30/reference.py +5 -9
  24. jentic/apitools/openapi/datamodels/low/v30/request_body.py +108 -0
  25. jentic/apitools/openapi/datamodels/low/v30/response.py +104 -0
  26. jentic/apitools/openapi/datamodels/low/v30/responses.py +109 -0
  27. jentic/apitools/openapi/datamodels/low/v30/schema.py +81 -97
  28. jentic/apitools/openapi/datamodels/low/v30/security_requirement.py +14 -9
  29. jentic/apitools/openapi/datamodels/low/v30/security_scheme.py +42 -22
  30. jentic/apitools/openapi/datamodels/low/v30/server.py +111 -0
  31. jentic/apitools/openapi/datamodels/low/v30/server_variable.py +4 -10
  32. jentic/apitools/openapi/datamodels/low/v30/tag.py +8 -46
  33. jentic/apitools/openapi/datamodels/low/v30/xml.py +4 -10
  34. jentic/apitools/openapi/datamodels/low/v31/__init__.py +77 -0
  35. jentic/apitools/openapi/datamodels/low/v31/builders/__init__.py +347 -0
  36. jentic/apitools/openapi/datamodels/low/v31/callback.py +131 -0
  37. jentic/apitools/openapi/datamodels/low/v31/components.py +240 -0
  38. jentic/apitools/openapi/datamodels/low/v31/contact.py +61 -0
  39. jentic/apitools/openapi/datamodels/low/v31/discriminator.py +62 -0
  40. jentic/apitools/openapi/datamodels/low/v31/encoding.py +81 -0
  41. jentic/apitools/openapi/datamodels/low/v31/example.py +91 -0
  42. jentic/apitools/openapi/datamodels/low/v31/external_documentation.py +59 -0
  43. jentic/apitools/openapi/datamodels/low/v31/header.py +120 -0
  44. jentic/apitools/openapi/datamodels/low/v31/info.py +116 -0
  45. jentic/apitools/openapi/datamodels/low/v31/license.py +61 -0
  46. jentic/apitools/openapi/datamodels/low/v31/link.py +141 -0
  47. jentic/apitools/openapi/datamodels/low/v31/media_type.py +110 -0
  48. jentic/apitools/openapi/datamodels/low/v31/oauth_flow.py +65 -0
  49. jentic/apitools/openapi/datamodels/low/v31/oauth_flows.py +108 -0
  50. jentic/apitools/openapi/datamodels/low/v31/openapi.py +168 -0
  51. jentic/apitools/openapi/datamodels/low/v31/operation.py +133 -0
  52. jentic/apitools/openapi/datamodels/low/v31/parameter.py +123 -0
  53. jentic/apitools/openapi/datamodels/low/v31/path_item.py +125 -0
  54. jentic/apitools/openapi/datamodels/low/v31/paths.py +108 -0
  55. jentic/apitools/openapi/datamodels/low/v31/reference.py +65 -0
  56. jentic/apitools/openapi/datamodels/low/v31/request_body.py +108 -0
  57. jentic/apitools/openapi/datamodels/low/v31/response.py +104 -0
  58. jentic/apitools/openapi/datamodels/low/v31/responses.py +109 -0
  59. jentic/apitools/openapi/datamodels/low/v31/schema.py +498 -0
  60. jentic/apitools/openapi/datamodels/low/v31/security_requirement.py +106 -0
  61. jentic/apitools/openapi/datamodels/low/v31/security_scheme.py +129 -0
  62. jentic/apitools/openapi/datamodels/low/v31/server.py +111 -0
  63. jentic/apitools/openapi/datamodels/low/v31/server_variable.py +70 -0
  64. jentic/apitools/openapi/datamodels/low/v31/tag.py +63 -0
  65. jentic/apitools/openapi/datamodels/low/v31/xml.py +54 -0
  66. jentic_openapi_datamodels-1.0.0a20.dist-info/METADATA +379 -0
  67. jentic_openapi_datamodels-1.0.0a20.dist-info/RECORD +75 -0
  68. jentic/apitools/openapi/datamodels/low/model_builder.py +0 -129
  69. jentic_openapi_datamodels-1.0.0a18.dist-info/METADATA +0 -211
  70. jentic_openapi_datamodels-1.0.0a18.dist-info/RECORD +0 -27
  71. {jentic_openapi_datamodels-1.0.0a18.dist-info → jentic_openapi_datamodels-1.0.0a20.dist-info}/WHEEL +0 -0
  72. {jentic_openapi_datamodels-1.0.0a18.dist-info → jentic_openapi_datamodels-1.0.0a20.dist-info}/licenses/LICENSE +0 -0
  73. {jentic_openapi_datamodels-1.0.0a18.dist-info → jentic_openapi_datamodels-1.0.0a20.dist-info}/licenses/NOTICE +0 -0
@@ -0,0 +1,379 @@
1
+ Metadata-Version: 2.4
2
+ Name: jentic-openapi-datamodels
3
+ Version: 1.0.0a20
4
+ Summary: Jentic OpenAPI Data Models
5
+ Author: Jentic
6
+ Author-email: Jentic <hello@jentic.com>
7
+ License-Expression: Apache-2.0
8
+ License-File: LICENSE
9
+ License-File: NOTICE
10
+ Requires-Dist: ruamel-yaml~=0.18.15
11
+ Requires-Python: >=3.11
12
+ Project-URL: Homepage, https://github.com/jentic/jentic-openapi-tools
13
+ Description-Content-Type: text/markdown
14
+
15
+ # jentic-openapi-datamodels
16
+
17
+ Low-level data models for OpenAPI specifications.
18
+
19
+ This package provides data model classes for representing OpenAPI specification objects in Python.
20
+
21
+ ## Features
22
+
23
+ **Low-Level Architecture**
24
+ - **Preserve Everything**: All data from source documents preserved exactly as-is, including invalid values
25
+ - **Zero Validation**: No validation or coercion during parsing - deferred to higher layers
26
+ - **Separation of Concerns**: Low-level model focuses on faithful representation; validation belongs elsewhere
27
+
28
+ **Source Tracking**
29
+ - **Complete Source Fidelity**: Every field tracks its exact YAML node location
30
+ - **Precise Error Reporting**: Line and column numbers via `start_mark` and `end_mark`
31
+ - **Metadata Preservation**: Full position tracking for accurate diagnostics
32
+
33
+ **Python Integration**
34
+ - **Python-Idiomatic Naming**: snake_case field names (e.g., `bearer_format`, `property_name`)
35
+ - **Spec-Aligned Mapping**: Automatic YAML name mapping (e.g., `bearerFormat` ↔ `bearer_format`)
36
+ - **Type Safety**: Full type hints with Generic types (`FieldSource[T]`, `KeySource[T]`, `ValueSource[T]`)
37
+
38
+ **Extensibility**
39
+ - **Extension Support**: Automatic extraction of OpenAPI `x-*` specification extensions
40
+ - **Unknown Field Tracking**: Capture typos and invalid fields for validation tools
41
+ - **Generic Builder Pattern**: Core `build_model()` function with object-specific builders for complex cases
42
+
43
+ **Performance**
44
+ - **Memory Efficient**: Immutable frozen dataclasses with `__slots__` for optimal memory usage
45
+ - **Shared Context**: All instances share a single YAML constructor for efficiency
46
+
47
+ **Version Support**
48
+ - **OpenAPI 2.0**: Planned for future release
49
+ - **OpenAPI 3.0.x**: Fully implemented
50
+ - **OpenAPI 3.1.x**: Fully implemented with JSON Schema 2020-12 support
51
+ - **OpenAPI 3.2.x**: Planned for future release
52
+
53
+ ## Installation
54
+
55
+ ```bash
56
+ pip install jentic-openapi-datamodels
57
+ ```
58
+
59
+ **Prerequisites:**
60
+ - Python 3.11+
61
+
62
+ ## Quick Start
63
+
64
+ ### Parsing OpenAPI 3.0 Documents
65
+
66
+ The main use case is parsing complete OpenAPI Documents:
67
+
68
+ ```python
69
+ from jentic.apitools.openapi.parser.core import OpenAPIParser
70
+ from jentic.apitools.openapi.parser.backends.ruamel_ast import MappingNode
71
+ from jentic.apitools.openapi.datamodels.low.v30 import build
72
+
73
+ # Parse OpenAPI document
74
+ parser = OpenAPIParser("ruamel-ast")
75
+ root = parser.parse("""
76
+ openapi: 3.0.4
77
+ info:
78
+ title: Pet Store API
79
+ version: 1.0.0
80
+ paths:
81
+ /pets:
82
+ get:
83
+ summary: List all pets
84
+ responses:
85
+ '200':
86
+ description: A list of pets
87
+ """, return_type=MappingNode)
88
+
89
+ # Build OpenAPI document model
90
+ openapi_doc = build(root)
91
+
92
+ # Access document fields via Python naming (snake_case)
93
+ print(openapi_doc.openapi.value) # "3.0.4"
94
+ print(openapi_doc.info.value.title.value) # "Pet Store API"
95
+ print(openapi_doc.info.value.version.value) # "1.0.0"
96
+
97
+ # Access nested fields with full type safety
98
+ for path_key, path_item in openapi_doc.paths.value.path_items.items():
99
+ print(f"Path: {path_key.value}") # "/pets"
100
+ if path_item.value.get:
101
+ operation = path_item.value.get.value
102
+ print(f" Summary: {operation.summary.value}") # "List all pets"
103
+ ```
104
+
105
+ ### Parsing OpenAPI 3.1 Documents with JSON Schema 2020-12
106
+
107
+ OpenAPI 3.1 fully supports JSON Schema 2020-12, including advanced features like boolean schemas, conditional validation and vocabulary declarations:
108
+
109
+ ```python
110
+ from jentic.apitools.openapi.parser.core import OpenAPIParser
111
+ from jentic.apitools.openapi.parser.backends.ruamel_ast import MappingNode
112
+ from jentic.apitools.openapi.datamodels.low.v31 import build
113
+
114
+ # Parse OpenAPI 3.1 document with JSON Schema 2020-12 features
115
+ parser = OpenAPIParser("ruamel-ast")
116
+ root = parser.parse("""
117
+ openapi: 3.1.2
118
+ info:
119
+ title: Pet Store API
120
+ version: 1.0.0
121
+ paths:
122
+ /pets:
123
+ get:
124
+ responses:
125
+ '200':
126
+ description: Pet list
127
+ content:
128
+ application/json:
129
+ schema:
130
+ type: array
131
+ prefixItems:
132
+ - type: string
133
+ - type: integer
134
+ items: false
135
+ contains:
136
+ type: object
137
+ required: [id]
138
+ """, return_type=MappingNode)
139
+
140
+ openapi_doc = build(root)
141
+
142
+ # Access JSON Schema 2020-12 features
143
+ schema = openapi_doc.paths.value.path_items["/pets"].value.get.value.responses.value["200"].value.content.value["application/json"].value.schema
144
+ print(schema.prefix_items.value[0].type.value) # "string"
145
+ print(schema.items.value) # False (boolean schema)
146
+ print(schema.contains.value.required.value[0].value) # "id"
147
+ ```
148
+
149
+ ### Parsing Individual Spec Objects
150
+
151
+ You can also parse individual OpenAPI specification objects:
152
+
153
+ ```python
154
+ from jentic.apitools.openapi.parser.core import OpenAPIParser
155
+ from jentic.apitools.openapi.parser.backends.ruamel_ast import MappingNode
156
+ from jentic.apitools.openapi.datamodels.low.v30.security_scheme import build as build_security_scheme
157
+
158
+ # Parse a Security Scheme object
159
+ parser = OpenAPIParser("ruamel-ast")
160
+ root = parser.parse("""
161
+ type: http
162
+ scheme: bearer
163
+ bearerFormat: JWT
164
+ """, return_type=MappingNode)
165
+
166
+ security_scheme = build_security_scheme(root)
167
+
168
+ # Access via Python field names (snake_case)
169
+ print(security_scheme.bearer_format.value) # "JWT"
170
+
171
+ # Access source location information
172
+ print(security_scheme.bearer_format.key_node.value) # "bearerFormat"
173
+ print(security_scheme.bearer_format.key_node.start_mark.line) # Line number
174
+ ```
175
+
176
+ You can also parse OpenAPI 3.1 Schema objects with JSON Schema 2020-12 features:
177
+
178
+ ```python
179
+ from jentic.apitools.openapi.parser.core import OpenAPIParser
180
+ from jentic.apitools.openapi.parser.backends.ruamel_ast import MappingNode
181
+ from jentic.apitools.openapi.datamodels.low.v31.schema import build as build_schema
182
+
183
+ # Parse a Schema object with JSON Schema 2020-12 features
184
+ parser = OpenAPIParser("ruamel-ast")
185
+ root = parser.parse("""
186
+ type: object
187
+ properties:
188
+ id:
189
+ type: integer
190
+ tags:
191
+ type: array
192
+ prefixItems:
193
+ - type: string
194
+ - type: string
195
+ items: false
196
+ patternProperties:
197
+ "^x-":
198
+ type: string
199
+ unevaluatedProperties: false
200
+ if:
201
+ properties:
202
+ premium:
203
+ const: true
204
+ then:
205
+ required: [support_tier]
206
+ """, return_type=MappingNode)
207
+
208
+ schema = build_schema(root)
209
+
210
+ # Access JSON Schema 2020-12 fields via Python naming (snake_case)
211
+ print(schema.properties.value["id"].type.value) # "integer"
212
+ print(schema.pattern_properties.value["^x-"].type.value) # "string"
213
+ print(schema.unevaluated_properties.value) # False
214
+ print(schema.prefix_items.value[0].type.value) # "string"
215
+
216
+ # Access conditional schema fields
217
+ print(schema.if_.value.properties.value["premium"].const.value) # True
218
+ print(schema.then_.value.required.value[0].value) # "support_tier"
219
+
220
+ # Access source location information
221
+ print(schema.type.key_node.start_mark.line) # Line number for "type" key
222
+ ```
223
+
224
+ ### Field Name Mapping
225
+
226
+ YAML `camelCase` fields automatically map to Python `snake_case`:
227
+ - `bearerFormat` → `bearer_format`
228
+ - `authorizationUrl` → `authorization_url`
229
+ - `openIdConnectUrl` → `openid_connect_url`
230
+
231
+ Special cases for Python reserved keywords and `$` fields:
232
+
233
+ - `in` → `in_`
234
+ - `if` → `if_`
235
+ - `then` → `then_`
236
+ - `else` → `else_`
237
+ - `not` → `not_`
238
+ - `$ref` → `ref_`
239
+ - `$id` → `id_`
240
+ - `$schema` → `schema_`
241
+
242
+ ### Source Tracking
243
+
244
+ The package provides three immutable wrapper types for preserving source information:
245
+
246
+ **FieldSource[T]** - For OpenAPI fields with key-value pairs
247
+ - Used for: Fixed fields (`name`, `bearer_format`) and patterned fields (status codes, path items, schema properties)
248
+ - Tracks: Both key and value nodes
249
+ - Example: `SecurityScheme.bearer_format` is `FieldSource[str]`, response status codes are `FieldSource[Response]`
250
+
251
+ **KeySource[T]** - For dictionary keys
252
+ - Used for: keys in OpenAPI fields, `x-*` extensions and mapping dictionaries
253
+ - Tracks: Only key node
254
+ - Example: Keys in `Discriminator.mapping` are `KeySource[str]`
255
+
256
+ **ValueSource[T]** - For dictionary values and array items
257
+ - Used for: values in OpenAPI fields, in `x-*` extensions, mapping dictionaries and array items
258
+ - Tracks: Only value node
259
+ - Example: Values in `Discriminator.mapping` are `ValueSource[str]`
260
+
261
+ ```python
262
+ from jentic.apitools.openapi.parser.core import OpenAPIParser
263
+ from jentic.apitools.openapi.parser.backends.ruamel_ast import MappingNode
264
+ from jentic.apitools.openapi.datamodels.low.v30 import build
265
+
266
+ # FieldSource: Fixed specification fields in OpenAPI document
267
+ parser = OpenAPIParser("ruamel-ast")
268
+ root = parser.parse("""
269
+ openapi: 3.0.4
270
+ info:
271
+ title: Pet Store API
272
+ version: 1.0.0
273
+ paths: {}
274
+ """, return_type=MappingNode)
275
+ openapi_doc = build(root)
276
+
277
+ field = openapi_doc.info.value.title # FieldSource[str]
278
+ print(field.value) # "Pet Store API" - The actual value
279
+ print(field.key_node) # YAML node for "title"
280
+ print(field.value_node) # YAML node for "Pet Store API"
281
+
282
+ # KeySource/ValueSource: Dictionary fields (extensions, mapping)
283
+ # Extensions in OpenAPI objects use KeySource/ValueSource
284
+ root = parser.parse("""
285
+ openapi: 3.0.4
286
+ info:
287
+ title: API
288
+ version: 1.0.0
289
+ x-custom: value
290
+ x-another: data
291
+ paths: {}
292
+ """, return_type=MappingNode)
293
+ openapi_doc = build(root)
294
+
295
+ for key, value in openapi_doc.info.value.extensions.items():
296
+ print(key.value) # KeySource[str]: "x-custom" or "x-another"
297
+ print(key.key_node) # YAML node for the extension key
298
+ print(value.value) # ValueSource: "value" or "data"
299
+ print(value.value_node) # YAML node for the extension value
300
+ ```
301
+
302
+ ### Location Ranges
303
+
304
+ Access precise location ranges within the source document using start_mark and end_mark:
305
+
306
+ ```python
307
+ from jentic.apitools.openapi.parser.core import OpenAPIParser
308
+ from jentic.apitools.openapi.parser.backends.ruamel_ast import MappingNode
309
+ from jentic.apitools.openapi.datamodels.low.v30 import build
310
+
311
+ yaml_content = """
312
+ openapi: 3.0.4
313
+ info:
314
+ title: Pet Store API
315
+ version: 1.0.0
316
+ description: A sample Pet Store API
317
+ paths: {}
318
+ """
319
+
320
+ parser = OpenAPIParser("ruamel-ast")
321
+ root = parser.parse(yaml_content, return_type=MappingNode)
322
+ openapi_doc = build(root)
323
+
324
+ # Access location information for any field
325
+ field = openapi_doc.info.value.title
326
+
327
+ # Key location (e.g., "title")
328
+ print(f"Key start: line {field.key_node.start_mark.line}, col {field.key_node.start_mark.column}")
329
+ print(f"Key end: line {field.key_node.end_mark.line}, col {field.key_node.end_mark.column}")
330
+
331
+ # Value location (e.g., "Pet Store API")
332
+ print(f"Value start: line {field.value_node.start_mark.line}, col {field.value_node.start_mark.column}")
333
+ print(f"Value end: line {field.value_node.end_mark.line}, col {field.value_node.end_mark.column}")
334
+
335
+ # Full field range (from key start to value end)
336
+ start = field.key_node.start_mark
337
+ end = field.value_node.end_mark
338
+ print(f"Field range: ({start.line}:{start.column}) to ({end.line}:{end.column})")
339
+ ```
340
+
341
+ ### Invalid Data Handling
342
+
343
+ Low-level models preserve invalid data without validation:
344
+
345
+ ```python
346
+ from jentic.apitools.openapi.parser.core import OpenAPIParser
347
+ from jentic.apitools.openapi.parser.backends.ruamel_ast import MappingNode
348
+ from jentic.apitools.openapi.datamodels.low.v30 import build
349
+
350
+ parser = OpenAPIParser("ruamel-ast")
351
+ root = parser.parse("""
352
+ openapi: 3.0.4
353
+ info:
354
+ title: 123 # Intentionally wrong type for demonstration (should be string)
355
+ version: 1.0.0
356
+ paths: {}
357
+ """, return_type=MappingNode)
358
+
359
+ openapi_doc = build(root)
360
+ print(openapi_doc.info.value.title.value) # 123 (preserved as-is)
361
+ print(type(openapi_doc.info.value.title.value)) # <class 'int'>
362
+
363
+ # Invalid data is preserved with full source tracking for validation tools
364
+ print(openapi_doc.info.value.title.value_node.start_mark.line) # Line number
365
+ ```
366
+
367
+ ### Error Reporting
368
+
369
+ This architecture—where the low-level model preserves data without validation and validation tools consume
370
+ that data—allows the low-level model to remain simple while enabling sophisticated validation tools to provide
371
+ user-friendly error messages with exact source locations.
372
+
373
+ ## Testing
374
+
375
+ Run the test suite:
376
+
377
+ ```bash
378
+ uv run --package jentic-openapi-datamodels pytest packages/jentic-openapi-datamodels -v
379
+ ```
@@ -0,0 +1,75 @@
1
+ jentic/apitools/openapi/datamodels/low/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ jentic/apitools/openapi/datamodels/low/context.py,sha256=pAuPf8GmdttXMEeuO4clAvTTxH7LMtEHdxoo1RpyK2c,555
3
+ jentic/apitools/openapi/datamodels/low/extractors.py,sha256=ZdTndH_9OcN9O7l560D13n4d0IJPCKLR0ayqrkrey9Y,4840
4
+ jentic/apitools/openapi/datamodels/low/fields.py,sha256=g1Sta-eN4JDg_AnuJxe1MeWS3Ut81A5dw_ZIdyGrgbk,1448
5
+ jentic/apitools/openapi/datamodels/low/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ jentic/apitools/openapi/datamodels/low/sources.py,sha256=H4I6LSn-Ry6cJageIyhCvE0H85O7Lh94gdCIm60wj0E,2924
7
+ jentic/apitools/openapi/datamodels/low/v30/__init__.py,sha256=3i8EWv_2cxoSPkpIOedhswAPSk8cTitUGWbt4iZ9U34,1934
8
+ jentic/apitools/openapi/datamodels/low/v30/builders/__init__.py,sha256=mIpggEOAZa6M4EgJc-THjdhsZB_vsaVTHb8WkKD7eXQ,15717
9
+ jentic/apitools/openapi/datamodels/low/v30/callback.py,sha256=eNPzblFpsm3zP9GiqaDguWFLFgoWMSxNIveUYhvFKPI,4802
10
+ jentic/apitools/openapi/datamodels/low/v30/components.py,sha256=gzpp9EJhO4N_0hqPi55KhLqT4uZfIjo6v1uifyIDg2A,11426
11
+ jentic/apitools/openapi/datamodels/low/v30/contact.py,sha256=1RJCZ165aXOZ0Rjz_LftKy4rB99_hqCVaT0iLV56H3o,2194
12
+ jentic/apitools/openapi/datamodels/low/v30/discriminator.py,sha256=vPQgQ6KQY3z6dkIWbvC4qv36DBev8WHRyvavfbHh0aw,2362
13
+ jentic/apitools/openapi/datamodels/low/v30/encoding.py,sha256=DE6M79J_6BUrBevqaVTCGGLjII83lqJD40NviRFDzm8,3438
14
+ jentic/apitools/openapi/datamodels/low/v30/example.py,sha256=QCtF8-T0uQQ_aGXIEPW754U62ygmly_1VpIUdekEyKY,3488
15
+ jentic/apitools/openapi/datamodels/low/v30/external_documentation.py,sha256=BNOcD6W1LI43g_463wlGfTOYXL8gV41Ib28flsULETk,2269
16
+ jentic/apitools/openapi/datamodels/low/v30/header.py,sha256=oGv-smivn_z0RbS9S5vbvHX2KZaz90tXBnJWi6hxRzU,5275
17
+ jentic/apitools/openapi/datamodels/low/v30/info.py,sha256=-EHY7uRMlFUPYo_OETsQPvSvhtzbs4dNkxY2CzgSvQk,4575
18
+ jentic/apitools/openapi/datamodels/low/v30/license.py,sha256=ghKqCOJPASMNIybe7RLiEA437Lb2PChMfrKnGLhxcUo,2005
19
+ jentic/apitools/openapi/datamodels/low/v30/link.py,sha256=hY8ioLU66ozZ6Gidydd6lycx8DrM4Wr4uaYpSqd2hM0,5881
20
+ jentic/apitools/openapi/datamodels/low/v30/media_type.py,sha256=hnuELzZbl3GL6YtgtwB51erZZxtLXZ-mAO5A_vs0IRc,4698
21
+ jentic/apitools/openapi/datamodels/low/v30/oauth_flow.py,sha256=aAQUy_xX3vEhGHwiSgWiJuBnqidaf3uy6o8JX4fYpns,2727
22
+ jentic/apitools/openapi/datamodels/low/v30/oauth_flows.py,sha256=a2aezPtBhanFvIAYSJGNtSIUcMCh-KGjmSYKizulGjk,4331
23
+ jentic/apitools/openapi/datamodels/low/v30/openapi.py,sha256=tySlKTuFyHgEpS13-vJSRpuy7eWQEM1QrY2F7QDdFPc,5833
24
+ jentic/apitools/openapi/datamodels/low/v30/operation.py,sha256=tU2qngEeJSqRW103-PVo5cBGzLtkdDws8kGRusSLKC0,5446
25
+ jentic/apitools/openapi/datamodels/low/v30/parameter.py,sha256=YopOV5rnA-XiumEVSVOi2AESyRPWT6yqqENuqX04QTM,5186
26
+ jentic/apitools/openapi/datamodels/low/v30/path_item.py,sha256=4MQUIwHEr7qcpHZQHmQzrZstMyKwwTFyvlnfcIiUn_M,4878
27
+ jentic/apitools/openapi/datamodels/low/v30/paths.py,sha256=DTfH6-3eI1muIAgCzL9TbbQjN-9_et_wxlL66LRSFOQ,3896
28
+ jentic/apitools/openapi/datamodels/low/v30/reference.py,sha256=QXuI-caWgMlU1Zl1V5wiOQurIUAn_3t_XoOLyG0hSQ8,1966
29
+ jentic/apitools/openapi/datamodels/low/v30/request_body.py,sha256=8Ca3iWXn13wmMusy75QyZSFR6KmGcwbhGjxTeTHjiFo,3900
30
+ jentic/apitools/openapi/datamodels/low/v30/response.py,sha256=FFLoHRVAw91vKWlkDzD08Xlie0hHnCSKXzjMKDKmH1U,3976
31
+ jentic/apitools/openapi/datamodels/low/v30/responses.py,sha256=JiHotGO1mCoO8rhOgj6ETaDtcEcnm554v-L4bHiJXkY,4245
32
+ jentic/apitools/openapi/datamodels/low/v30/schema.py,sha256=fkGRJz_Ta-Giq06KPoLjAZanDGgTILmX5Ikj5PlSCow,15717
33
+ jentic/apitools/openapi/datamodels/low/v30/security_requirement.py,sha256=LZbUUXmnGtvEzgwXlGVNIUwZTx9gK6uXPbrtmKaXqsw,4277
34
+ jentic/apitools/openapi/datamodels/low/v30/security_scheme.py,sha256=LClieys-mEiM84EEOGK4dsaWwNny1lKztzVBicddQd4,5455
35
+ jentic/apitools/openapi/datamodels/low/v30/server.py,sha256=G9mcOMq9owtd6qECaQIxpvN9VrU7zjxnwaBVAfJw0Cs,4687
36
+ jentic/apitools/openapi/datamodels/low/v30/server_variable.py,sha256=CmWdchAOTO3RtlSIodPOwGQds3-WZnWjaHMh66H7WpA,2762
37
+ jentic/apitools/openapi/datamodels/low/v30/tag.py,sha256=wH9uWAqNLryvatahDWAjQOcSLlpRw0cUrTvIIhfuUbo,2359
38
+ jentic/apitools/openapi/datamodels/low/v30/xml.py,sha256=4KgtMgjp4v8lMTUuLG-7RBOgXGvPSsIrtHU-u6TUbGg,1930
39
+ jentic/apitools/openapi/datamodels/low/v31/__init__.py,sha256=MXwVorDh1quZU5A0mndI4__dVB247A5HQFzDJrocQsM,1978
40
+ jentic/apitools/openapi/datamodels/low/v31/builders/__init__.py,sha256=9-ohxirframb6EmcFlDQ83cnGZ_mbnt_2aoFeCQE51o,17925
41
+ jentic/apitools/openapi/datamodels/low/v31/callback.py,sha256=RBW8Xdhrd97elpiVBXGQqdXfDaWHEBaysM7RhZyqcrA,4802
42
+ jentic/apitools/openapi/datamodels/low/v31/components.py,sha256=pZet9wXywTNz0h_o5CU9HsIpOsY7bc-LdY9cAhRfdeY,11665
43
+ jentic/apitools/openapi/datamodels/low/v31/contact.py,sha256=SnvuCz-YitRwK8l5CwuWSP4LjuB10KWTNmad_fMOENM,2194
44
+ jentic/apitools/openapi/datamodels/low/v31/discriminator.py,sha256=5f4FRqLid4ZLPIha87bxjik-P6BskKixE7hZ8i9DUQY,2460
45
+ jentic/apitools/openapi/datamodels/low/v31/encoding.py,sha256=ZThHWbcUW8JXWQ8iNswOtJ4lPJ5UnNng2W6N7BNhhuY,3438
46
+ jentic/apitools/openapi/datamodels/low/v31/example.py,sha256=EpUEZwGSIgTd71zkRhnIphMHJLV96W5rPiQuepP39AA,3488
47
+ jentic/apitools/openapi/datamodels/low/v31/external_documentation.py,sha256=zQ2Hw2DDiGeR0zUjzRppxtt78SuIkkU-7behTw9ZjPA,2269
48
+ jentic/apitools/openapi/datamodels/low/v31/header.py,sha256=2zCP9wRTYvdv4XWiSSsH1YX537lqUdmnV5gRx0ABnnk,5302
49
+ jentic/apitools/openapi/datamodels/low/v31/info.py,sha256=xK0PJf8mVyAlWe6NqA8lkTuKoEVPFjjhM5EHRJu1OCc,4694
50
+ jentic/apitools/openapi/datamodels/low/v31/license.py,sha256=YlujRqmYc3mYQ3-uMc5vcdxxsFLedTXSzmhGBa264MA,2227
51
+ jentic/apitools/openapi/datamodels/low/v31/link.py,sha256=owYcMIFhSABY4bFlMmwy7XRqjmvK0Cpc3TqzbiZd2ug,5881
52
+ jentic/apitools/openapi/datamodels/low/v31/media_type.py,sha256=kVntYlOZe5uNeOftDZrbnNFDnfbkIulqu9D7yJ1Sdac,4725
53
+ jentic/apitools/openapi/datamodels/low/v31/oauth_flow.py,sha256=iCD-51C0wzO8ZIzRRxEtIAnHxd4oVTLwsHlzH9cMZmI,2727
54
+ jentic/apitools/openapi/datamodels/low/v31/oauth_flows.py,sha256=DLivDlTiQyTR_RtjjxbTjQcD7GbZPfotlg-GGBlGbxs,4331
55
+ jentic/apitools/openapi/datamodels/low/v31/openapi.py,sha256=5cBP0SdMewLtEDB88HvZMz-Yjo7ezamIX_SPnd_rAwE,6940
56
+ jentic/apitools/openapi/datamodels/low/v31/operation.py,sha256=cLQRVF6Vff4k1XkNwam8fL79Oa20I0xMtc4jdo3CwG4,5445
57
+ jentic/apitools/openapi/datamodels/low/v31/parameter.py,sha256=NilV-XRaV1FUon5ZyJG8KN1qdZcK923mBQtNlH59J_w,5213
58
+ jentic/apitools/openapi/datamodels/low/v31/path_item.py,sha256=mQTtGnIg1SYa2kEWHWFVQ2W7GlZ2fgwqcAg8NTQMcjk,4878
59
+ jentic/apitools/openapi/datamodels/low/v31/paths.py,sha256=Yf_DRrqKWjUVJQN57Lxb_4KBuwGVZa170BxGobioRhA,3896
60
+ jentic/apitools/openapi/datamodels/low/v31/reference.py,sha256=9sv24nmuS-l8Fp9T53UuQ_KINLHy86tb5HaUAhXwR8I,2595
61
+ jentic/apitools/openapi/datamodels/low/v31/request_body.py,sha256=tQ2QaNSAZSQAQgpSU1VxB0GhK0-Miq5D1cH0FojQzA8,3900
62
+ jentic/apitools/openapi/datamodels/low/v31/response.py,sha256=N46VyXBuJ1agYhDuD4-gDRlSW3k1kQK_TaOVwZoqHFQ,3976
63
+ jentic/apitools/openapi/datamodels/low/v31/responses.py,sha256=9qozdOF9miFd_RygGHIUZVtLQ_-3ma1OxORvEA_uzNc,4245
64
+ jentic/apitools/openapi/datamodels/low/v31/schema.py,sha256=jRVRKxSUNClWbmdHs1chDmTJKJBVT_DZWj9Nu2kQu7E,24818
65
+ jentic/apitools/openapi/datamodels/low/v31/security_requirement.py,sha256=BbnbK-rkJVFJcIc0mLReNOd9mhvNynNZHhFYOWSM2m0,4277
66
+ jentic/apitools/openapi/datamodels/low/v31/security_scheme.py,sha256=qNlKF9PkTC0tfV_PvbKUZnQMSr6PVeH5fyw3I83JPnw,5455
67
+ jentic/apitools/openapi/datamodels/low/v31/server.py,sha256=IA-dDnhqdD6WCZ_bjdGw_nQmp0cd0HiHeqGU_jIGocw,4687
68
+ jentic/apitools/openapi/datamodels/low/v31/server_variable.py,sha256=UfGDky_NVUF_WQc5ZIU947MjmpxaO__VIv6sq8HMsk0,2762
69
+ jentic/apitools/openapi/datamodels/low/v31/tag.py,sha256=8-QfVk4dUp3r-LbseMSvlCcAWeZkzlhZP3giVKNgaPI,2359
70
+ jentic/apitools/openapi/datamodels/low/v31/xml.py,sha256=SFLNLrhiZRWVH_CBV8SqtO4C4qjDjGtqz_lYpp84BnE,1930
71
+ jentic_openapi_datamodels-1.0.0a20.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
72
+ jentic_openapi_datamodels-1.0.0a20.dist-info/licenses/NOTICE,sha256=pAOGW-rGw9KNc2cuuLWZkfx0GSTV4TicbgBKZSLPMIs,168
73
+ jentic_openapi_datamodels-1.0.0a20.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
74
+ jentic_openapi_datamodels-1.0.0a20.dist-info/METADATA,sha256=7Q28pNaN1yY8_95wbff-bV1izBBWiitL5HZNLAMq3ME,12466
75
+ jentic_openapi_datamodels-1.0.0a20.dist-info/RECORD,,
@@ -1,129 +0,0 @@
1
- from dataclasses import fields
2
- from typing import Any, TypeVar, cast, 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_fields
9
- from jentic.apitools.openapi.datamodels.low.sources import (
10
- FieldSource,
11
- KeySource,
12
- ValueSource,
13
- YAMLInvalidValue,
14
- )
15
-
16
-
17
- __all__ = ["build_model"]
18
-
19
-
20
- T = TypeVar("T")
21
-
22
-
23
- def build_model(
24
- root: yaml.Node, dataclass_type: type[T], *, context: Context | None = None
25
- ) -> T | ValueSource[YAMLInvalidValue]:
26
- """
27
- Generic builder for OpenAPI low model.
28
-
29
- Builds any dataclass that follows the pattern:
30
- - Has a required `root_node: yaml.Node` field
31
- - Has an optional `extensions: dict[...]` field
32
- - Has spec fields marked with `fixed_field()`
33
-
34
- Args:
35
- root: The YAML node to parse (should be a MappingNode)
36
- dataclass_type: The dataclass type to build
37
- context: Optional parsing context. If None, a default context will be created.
38
-
39
- Returns:
40
- An instance of dataclass_type if the node is valid, or a ValueSource containing
41
- the invalid data if the root is not a MappingNode (preserving the invalid data
42
- and its source location for validation).
43
-
44
- Example:
45
- xml = build_model(root_node, XML, context=context)
46
- """
47
- # Initialize context once at the beginning
48
- if context is None:
49
- context = Context()
50
-
51
- if not isinstance(root, yaml.MappingNode):
52
- # Preserve invalid root data instead of returning None
53
- value = context.yaml_constructor.construct_object(root, deep=True)
54
- return ValueSource(value=value, value_node=root)
55
-
56
- # Get fixed specification fields for this dataclass type
57
- _fixed_fields = fixed_fields(dataclass_type)
58
-
59
- # Build YAML name to Python field name mapping
60
- yaml_to_field = {
61
- field.metadata.get("yaml_name", fname): fname for fname, field in _fixed_fields.items()
62
- }
63
-
64
- # Extract field values in a single pass (non-recursive, single layer only)
65
- field_values: dict[str, FieldSource[Any]] = {}
66
- for key_node, value_node in root.value:
67
- key = context.yaml_constructor.construct_yaml_str(key_node)
68
-
69
- # Map YAML key to Python field name
70
- field_name = yaml_to_field.get(key)
71
- if field_name:
72
- field = _fixed_fields[field_name]
73
- field_type_args = set(get_args(field.type))
74
-
75
- if field_type_args & {FieldSource[str], FieldSource[bool], FieldSource[int]}:
76
- value = context.yaml_constructor.construct_object(value_node, deep=True)
77
- field_values[field_name] = FieldSource(
78
- value=value, key_node=key_node, value_node=value_node
79
- )
80
- elif field_type_args & {FieldSource[dict[KeySource[str], ValueSource[str]]]}:
81
- # Handle dict with KeySource/ValueSource wrapping
82
- if isinstance(value_node, yaml.MappingNode):
83
- mapping_dict: dict[KeySource[str], ValueSource[str]] = {}
84
- for map_key_node, map_value_node in value_node.value:
85
- map_key = context.yaml_constructor.construct_yaml_str(map_key_node)
86
- map_value = context.yaml_constructor.construct_object(
87
- map_value_node, deep=True
88
- )
89
- mapping_dict[KeySource(value=map_key, key_node=map_key_node)] = ValueSource(
90
- value=map_value, value_node=map_value_node
91
- )
92
- field_values[field_name] = FieldSource(
93
- value=mapping_dict, key_node=key_node, value_node=value_node
94
- )
95
- else:
96
- # Not a mapping - preserve as-is for validation
97
- value = context.yaml_constructor.construct_object(value_node, deep=True)
98
- field_values[field_name] = FieldSource(
99
- value=value, key_node=key_node, value_node=value_node
100
- )
101
- elif field_type_args & {FieldSource[list[ValueSource[str]]]}:
102
- # Handle list with ValueSource wrapping for each item
103
- if isinstance(value_node, yaml.SequenceNode):
104
- value_list: list[ValueSource[str]] = []
105
- for item_node in value_node.value:
106
- item_value = context.yaml_constructor.construct_object(item_node, deep=True)
107
- value_list.append(ValueSource(value=item_value, value_node=item_node))
108
- field_values[field_name] = FieldSource(
109
- value=value_list, key_node=key_node, value_node=value_node
110
- )
111
- else:
112
- # Not a sequence - preserve as-is for validation
113
- value = context.yaml_constructor.construct_object(value_node, deep=True)
114
- field_values[field_name] = FieldSource(
115
- value=value, key_node=key_node, value_node=value_node
116
- )
117
-
118
- # Build and return the dataclass instance
119
- # Conditionally include extensions field if dataclass supports it
120
- # Cast to Any to work around generic type constraints
121
- has_extensions = any(f.name == "extensions" for f in fields(cast(Any, dataclass_type)))
122
- return cast(
123
- T,
124
- dataclass_type(
125
- root_node=root, # type: ignore[call-arg]
126
- **field_values,
127
- **({"extensions": extract_extension_fields(root, context)} if has_extensions else {}),
128
- ),
129
- )