jentic-openapi-datamodels 1.0.0a18__py3-none-any.whl → 1.0.0a19__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.0a19.dist-info/METADATA +372 -0
  67. jentic_openapi_datamodels-1.0.0a19.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.0a19.dist-info}/WHEEL +0 -0
  72. {jentic_openapi_datamodels-1.0.0a18.dist-info → jentic_openapi_datamodels-1.0.0a19.dist-info}/licenses/LICENSE +0 -0
  73. {jentic_openapi_datamodels-1.0.0a18.dist-info → jentic_openapi_datamodels-1.0.0a19.dist-info}/licenses/NOTICE +0 -0
@@ -0,0 +1,372 @@
1
+ Metadata-Version: 2.4
2
+ Name: jentic-openapi-datamodels
3
+ Version: 1.0.0a19
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 ruamel.yaml import YAML
70
+ from jentic.apitools.openapi.datamodels.low.v30 import build
71
+
72
+ # Parse OpenAPI document
73
+ yaml = YAML()
74
+ root = yaml.compose("""
75
+ openapi: 3.0.4
76
+ info:
77
+ title: Pet Store API
78
+ version: 1.0.0
79
+ paths:
80
+ /pets:
81
+ get:
82
+ summary: List all pets
83
+ responses:
84
+ '200':
85
+ description: A list of pets
86
+ """)
87
+
88
+ # Build OpenAPI document model
89
+ openapi_doc = build(root)
90
+
91
+ # Access document fields via Python naming (snake_case)
92
+ print(openapi_doc.openapi.value) # "3.0.4"
93
+ print(openapi_doc.info.value.title.value) # "Pet Store API"
94
+ print(openapi_doc.info.value.version.value) # "1.0.0"
95
+
96
+ # Access nested fields with full type safety
97
+ for path_key, path_item in openapi_doc.paths.value.path_items.items():
98
+ print(f"Path: {path_key.value}") # "/pets"
99
+ if path_item.value.get:
100
+ operation = path_item.value.get.value
101
+ print(f" Summary: {operation.summary.value}") # "List all pets"
102
+ ```
103
+
104
+ ### Parsing OpenAPI 3.1 Documents with JSON Schema 2020-12
105
+
106
+ OpenAPI 3.1 fully supports JSON Schema 2020-12, including advanced features like boolean schemas, conditional validation and vocabulary declarations:
107
+
108
+ ```python
109
+ from ruamel.yaml import YAML
110
+ from jentic.apitools.openapi.datamodels.low.v31 import build
111
+
112
+ # Parse OpenAPI 3.1 document with JSON Schema 2020-12 features
113
+ yaml = YAML()
114
+ root = yaml.compose("""
115
+ openapi: 3.1.2
116
+ info:
117
+ title: Pet Store API
118
+ version: 1.0.0
119
+ paths:
120
+ /pets:
121
+ get:
122
+ responses:
123
+ '200':
124
+ description: Pet list
125
+ content:
126
+ application/json:
127
+ schema:
128
+ type: array
129
+ prefixItems:
130
+ - type: string
131
+ - type: integer
132
+ items: false
133
+ contains:
134
+ type: object
135
+ required: [id]
136
+ """)
137
+
138
+ openapi_doc = build(root)
139
+
140
+ # Access JSON Schema 2020-12 features
141
+ schema = openapi_doc.paths.value.path_items["/pets"].value.get.value.responses.value["200"].value.content.value["application/json"].value.schema
142
+ print(schema.prefix_items.value[0].type.value) # "string"
143
+ print(schema.items.value) # False (boolean schema)
144
+ print(schema.contains.value.required.value[0].value) # "id"
145
+ ```
146
+
147
+ ### Parsing Individual Spec Objects
148
+
149
+ You can also parse individual OpenAPI specification objects:
150
+
151
+ ```python
152
+ from ruamel.yaml import YAML
153
+ from jentic.apitools.openapi.datamodels.low.v30.security_scheme import build as build_security_scheme
154
+
155
+ # Parse a Security Scheme object
156
+ yaml = YAML()
157
+ root = yaml.compose("""
158
+ type: http
159
+ scheme: bearer
160
+ bearerFormat: JWT
161
+ """)
162
+
163
+ security_scheme = build_security_scheme(root)
164
+
165
+ # Access via Python field names (snake_case)
166
+ print(security_scheme.bearer_format.value) # "JWT"
167
+
168
+ # Access source location information
169
+ print(security_scheme.bearer_format.key_node.value) # "bearerFormat"
170
+ print(security_scheme.bearer_format.key_node.start_mark.line) # Line number
171
+ ```
172
+
173
+ You can also parse OpenAPI 3.1 Schema objects with JSON Schema 2020-12 features:
174
+
175
+ ```python
176
+ from ruamel.yaml import YAML
177
+ from jentic.apitools.openapi.datamodels.low.v31.schema import build as build_schema
178
+
179
+ # Parse a Schema object with JSON Schema 2020-12 features
180
+ yaml = YAML()
181
+ root = yaml.compose("""
182
+ type: object
183
+ properties:
184
+ id:
185
+ type: integer
186
+ tags:
187
+ type: array
188
+ prefixItems:
189
+ - type: string
190
+ - type: string
191
+ items: false
192
+ patternProperties:
193
+ "^x-":
194
+ type: string
195
+ unevaluatedProperties: false
196
+ if:
197
+ properties:
198
+ premium:
199
+ const: true
200
+ then:
201
+ required: [support_tier]
202
+ """)
203
+
204
+ schema = build_schema(root)
205
+
206
+ # Access JSON Schema 2020-12 fields via Python naming (snake_case)
207
+ print(schema.properties.value["id"].type.value) # "integer"
208
+ print(schema.pattern_properties.value["^x-"].type.value) # "string"
209
+ print(schema.unevaluated_properties.value) # False
210
+ print(schema.prefix_items.value[0].type.value) # "string"
211
+
212
+ # Access conditional schema fields
213
+ print(schema.if_.value.properties.value["premium"].const.value) # True
214
+ print(schema.then_.value.required.value[0].value) # "support_tier"
215
+
216
+ # Access source location information
217
+ print(schema.type.key_node.start_mark.line) # Line number for "type" key
218
+ ```
219
+
220
+ ### Field Name Mapping
221
+
222
+ YAML `camelCase` fields automatically map to Python `snake_case`:
223
+ - `bearerFormat` → `bearer_format`
224
+ - `authorizationUrl` → `authorization_url`
225
+ - `openIdConnectUrl` → `openid_connect_url`
226
+
227
+ Special cases for Python reserved keywords and `$` fields:
228
+
229
+ - `in` → `in_`
230
+ - `if` → `if_`
231
+ - `then` → `then_`
232
+ - `else` → `else_`
233
+ - `not` → `not_`
234
+ - `$ref` → `ref_`
235
+ - `$id` → `id_`
236
+ - `$schema` → `schema_`
237
+
238
+ ### Source Tracking
239
+
240
+ The package provides three immutable wrapper types for preserving source information:
241
+
242
+ **FieldSource[T]** - For OpenAPI fields with key-value pairs
243
+ - Used for: Fixed fields (`name`, `bearer_format`) and patterned fields (status codes, path items, schema properties)
244
+ - Tracks: Both key and value nodes
245
+ - Example: `SecurityScheme.bearer_format` is `FieldSource[str]`, response status codes are `FieldSource[Response]`
246
+
247
+ **KeySource[T]** - For dictionary keys
248
+ - Used for: keys in OpenAPI fields, `x-*` extensions and mapping dictionaries
249
+ - Tracks: Only key node
250
+ - Example: Keys in `Discriminator.mapping` are `KeySource[str]`
251
+
252
+ **ValueSource[T]** - For dictionary values and array items
253
+ - Used for: values in OpenAPI fields, in `x-*` extensions, mapping dictionaries and array items
254
+ - Tracks: Only value node
255
+ - Example: Values in `Discriminator.mapping` are `ValueSource[str]`
256
+
257
+ ```python
258
+ from ruamel.yaml import YAML
259
+ from jentic.apitools.openapi.datamodels.low.v30 import build
260
+
261
+ # FieldSource: Fixed specification fields in OpenAPI document
262
+ yaml = YAML()
263
+ root = yaml.compose("""
264
+ openapi: 3.0.4
265
+ info:
266
+ title: Pet Store API
267
+ version: 1.0.0
268
+ paths: {}
269
+ """)
270
+ openapi_doc = build(root)
271
+
272
+ field = openapi_doc.info.value.title # FieldSource[str]
273
+ print(field.value) # "Pet Store API" - The actual value
274
+ print(field.key_node) # YAML node for "title"
275
+ print(field.value_node) # YAML node for "Pet Store API"
276
+
277
+ # KeySource/ValueSource: Dictionary fields (extensions, mapping)
278
+ # Extensions in OpenAPI objects use KeySource/ValueSource
279
+ root = yaml.compose("""
280
+ openapi: 3.0.4
281
+ info:
282
+ title: API
283
+ version: 1.0.0
284
+ x-custom: value
285
+ x-another: data
286
+ paths: {}
287
+ """)
288
+ openapi_doc = build(root)
289
+
290
+ for key, value in openapi_doc.info.value.extensions.items():
291
+ print(key.value) # KeySource[str]: "x-custom" or "x-another"
292
+ print(key.key_node) # YAML node for the extension key
293
+ print(value.value) # ValueSource: "value" or "data"
294
+ print(value.value_node) # YAML node for the extension value
295
+ ```
296
+
297
+ ### Location Ranges
298
+
299
+ Access precise location ranges within the source document using start_mark and end_mark:
300
+
301
+ ```python
302
+ from ruamel.yaml import YAML
303
+ from jentic.apitools.openapi.datamodels.low.v30 import build
304
+
305
+ yaml_content = """
306
+ openapi: 3.0.4
307
+ info:
308
+ title: Pet Store API
309
+ version: 1.0.0
310
+ description: A sample Pet Store API
311
+ paths: {}
312
+ """
313
+
314
+ yaml = YAML()
315
+ root = yaml.compose(yaml_content)
316
+ openapi_doc = build(root)
317
+
318
+ # Access location information for any field
319
+ field = openapi_doc.info.value.title
320
+
321
+ # Key location (e.g., "title")
322
+ print(f"Key start: line {field.key_node.start_mark.line}, col {field.key_node.start_mark.column}")
323
+ print(f"Key end: line {field.key_node.end_mark.line}, col {field.key_node.end_mark.column}")
324
+
325
+ # Value location (e.g., "Pet Store API")
326
+ print(f"Value start: line {field.value_node.start_mark.line}, col {field.value_node.start_mark.column}")
327
+ print(f"Value end: line {field.value_node.end_mark.line}, col {field.value_node.end_mark.column}")
328
+
329
+ # Full field range (from key start to value end)
330
+ start = field.key_node.start_mark
331
+ end = field.value_node.end_mark
332
+ print(f"Field range: ({start.line}:{start.column}) to ({end.line}:{end.column})")
333
+ ```
334
+
335
+ ### Invalid Data Handling
336
+
337
+ Low-level models preserve invalid data without validation:
338
+
339
+ ```python
340
+ from ruamel.yaml import YAML
341
+ from jentic.apitools.openapi.datamodels.low.v30 import build
342
+
343
+ yaml = YAML()
344
+ root = yaml.compose("""
345
+ openapi: 3.0.4
346
+ info:
347
+ title: 123 # Intentionally wrong type for demonstration (should be string)
348
+ version: 1.0.0
349
+ paths: {}
350
+ """)
351
+
352
+ openapi_doc = build(root)
353
+ print(openapi_doc.info.value.title.value) # 123 (preserved as-is)
354
+ print(type(openapi_doc.info.value.title.value)) # <class 'int'>
355
+
356
+ # Invalid data is preserved with full source tracking for validation tools
357
+ print(openapi_doc.info.value.title.value_node.start_mark.line) # Line number
358
+ ```
359
+
360
+ ### Error Reporting
361
+
362
+ This architecture—where the low-level model preserves data without validation and validation tools consume
363
+ that data—allows the low-level model to remain simple while enabling sophisticated validation tools to provide
364
+ user-friendly error messages with exact source locations.
365
+
366
+ ## Testing
367
+
368
+ Run the test suite:
369
+
370
+ ```bash
371
+ uv run --package jentic-openapi-datamodels pytest packages/jentic-openapi-datamodels -v
372
+ ```
@@ -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.0a19.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
72
+ jentic_openapi_datamodels-1.0.0a19.dist-info/licenses/NOTICE,sha256=pAOGW-rGw9KNc2cuuLWZkfx0GSTV4TicbgBKZSLPMIs,168
73
+ jentic_openapi_datamodels-1.0.0a19.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
74
+ jentic_openapi_datamodels-1.0.0a19.dist-info/METADATA,sha256=Thjm5eYidVkFu7DD0PQxAbnhmhhjv-JLhThd9tp4wsg,11349
75
+ jentic_openapi_datamodels-1.0.0a19.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
- )