jentic-openapi-datamodels 1.0.0a18__tar.gz → 1.0.0a20__tar.gz

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 (79) hide show
  1. jentic_openapi_datamodels-1.0.0a20/PKG-INFO +379 -0
  2. jentic_openapi_datamodels-1.0.0a20/README.md +365 -0
  3. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/pyproject.toml +9 -1
  4. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/extractors.py +3 -3
  5. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/__init__.py +76 -0
  6. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/builders/__init__.py +312 -0
  7. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/callback.py +131 -0
  8. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/components.py +236 -0
  9. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/v30/contact.py +4 -10
  10. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/v30/discriminator.py +4 -9
  11. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/encoding.py +81 -0
  12. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/example.py +91 -0
  13. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/v30/external_documentation.py +4 -10
  14. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/header.py +120 -0
  15. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/v30/info.py +14 -23
  16. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/v30/license.py +4 -10
  17. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/link.py +141 -0
  18. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/media_type.py +110 -0
  19. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/v30/oauth_flow.py +4 -10
  20. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/v30/oauth_flows.py +7 -15
  21. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/openapi.py +149 -0
  22. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/operation.py +134 -0
  23. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/parameter.py +123 -0
  24. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/path_item.py +125 -0
  25. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/paths.py +108 -0
  26. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/v30/reference.py +5 -9
  27. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/request_body.py +108 -0
  28. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/response.py +104 -0
  29. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/responses.py +109 -0
  30. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/v30/schema.py +81 -97
  31. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/v30/security_requirement.py +14 -9
  32. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/v30/security_scheme.py +42 -22
  33. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v30/server.py +111 -0
  34. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/v30/server_variable.py +4 -10
  35. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/v30/tag.py +8 -46
  36. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/v30/xml.py +4 -10
  37. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/__init__.py +77 -0
  38. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/builders/__init__.py +347 -0
  39. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/callback.py +131 -0
  40. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/components.py +240 -0
  41. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/contact.py +61 -0
  42. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/discriminator.py +62 -0
  43. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/encoding.py +81 -0
  44. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/example.py +91 -0
  45. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/external_documentation.py +59 -0
  46. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/header.py +120 -0
  47. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/info.py +116 -0
  48. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/license.py +61 -0
  49. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/link.py +141 -0
  50. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/media_type.py +110 -0
  51. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/oauth_flow.py +65 -0
  52. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/oauth_flows.py +108 -0
  53. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/openapi.py +168 -0
  54. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/operation.py +133 -0
  55. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/parameter.py +123 -0
  56. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/path_item.py +125 -0
  57. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/paths.py +108 -0
  58. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/reference.py +65 -0
  59. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/request_body.py +108 -0
  60. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/response.py +104 -0
  61. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/responses.py +109 -0
  62. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/schema.py +498 -0
  63. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/security_requirement.py +106 -0
  64. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/security_scheme.py +129 -0
  65. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/server.py +111 -0
  66. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/server_variable.py +70 -0
  67. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/tag.py +63 -0
  68. jentic_openapi_datamodels-1.0.0a20/src/jentic/apitools/openapi/datamodels/low/v31/xml.py +54 -0
  69. jentic_openapi_datamodels-1.0.0a18/PKG-INFO +0 -211
  70. jentic_openapi_datamodels-1.0.0a18/README.md +0 -197
  71. jentic_openapi_datamodels-1.0.0a18/src/jentic/apitools/openapi/datamodels/low/model_builder.py +0 -129
  72. jentic_openapi_datamodels-1.0.0a18/src/jentic/apitools/openapi/datamodels/low/v30/__init__.py +0 -0
  73. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/LICENSE +0 -0
  74. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/NOTICE +0 -0
  75. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/__init__.py +0 -0
  76. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/context.py +0 -0
  77. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/fields.py +0 -0
  78. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/py.typed +0 -0
  79. {jentic_openapi_datamodels-1.0.0a18 → jentic_openapi_datamodels-1.0.0a20}/src/jentic/apitools/openapi/datamodels/low/sources.py +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
+ ```