jentic-openapi-parser 1.0.0a19__tar.gz → 1.0.0a21__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.
- {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/PKG-INFO +101 -6
- {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/README.md +98 -4
- {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/pyproject.toml +6 -2
- jentic_openapi_parser-1.0.0a21/src/jentic/apitools/openapi/parser/backends/datamodel_low.py +102 -0
- jentic_openapi_parser-1.0.0a21/src/jentic/apitools/openapi/parser/backends/ruamel_ast.py +58 -0
- {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/backends/ruamel_roundtrip.py +7 -1
- {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/backends/ruamel_safe.py +5 -1
- {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/LICENSE +0 -0
- {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/NOTICE +0 -0
- {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/backends/base.py +0 -0
- {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/backends/py.typed +0 -0
- {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/backends/pyyaml.py +0 -0
- {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/core/__init__.py +0 -0
- {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/core/exceptions.py +0 -0
- {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/core/loader.py +0 -0
- {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/core/openapi_parser.py +0 -0
- {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/core/py.typed +0 -0
- {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/core/serialization.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jentic-openapi-parser
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.0a21
|
|
4
4
|
Summary: Jentic OpenAPI Parser
|
|
5
5
|
Author: Jentic
|
|
6
6
|
Author-email: Jentic <hello@jentic.com>
|
|
@@ -8,7 +8,8 @@ License-Expression: Apache-2.0
|
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
License-File: NOTICE
|
|
10
10
|
Requires-Dist: attrs~=25.4.0
|
|
11
|
-
Requires-Dist: jentic-openapi-common~=1.0.
|
|
11
|
+
Requires-Dist: jentic-openapi-common~=1.0.0a21
|
|
12
|
+
Requires-Dist: jentic-openapi-datamodels~=1.0.0a21
|
|
12
13
|
Requires-Dist: pyyaml~=6.0.3
|
|
13
14
|
Requires-Dist: requests~=2.32.5
|
|
14
15
|
Requires-Dist: ruamel-yaml~=0.18.15
|
|
@@ -24,7 +25,8 @@ A Python library for parsing OpenAPI documents using pluggable parser backends.
|
|
|
24
25
|
|
|
25
26
|
- **Pluggable Backend Architecture**: Support for multiple parsing strategies via entry points
|
|
26
27
|
- **Multiple Input Formats**: Parse OpenAPI documents from file URIs or text strings (JSON/YAML)
|
|
27
|
-
- **Multiple Parser Backends**: Choose from PyYAML, ruamel.yaml, or
|
|
28
|
+
- **Multiple Parser Backends**: Choose from PyYAML, ruamel.yaml (safe/roundtrip/AST modes), or typed datamodels
|
|
29
|
+
- **Low-Level Datamodels**: Parse directly to typed `OpenAPI30`/`OpenAPI31` objects with source tracking
|
|
28
30
|
- **Enhanced JSON Serialization**: Built-in support for datetime, UUID, Path, Decimal, Enum, and attrs classes
|
|
29
31
|
- **Type Safety**: Full type hints with overloaded methods for precise return types
|
|
30
32
|
- **Extensible Design**: Easy integration of third-party parser backends
|
|
@@ -53,12 +55,12 @@ doc = parser.parse("file:///path/to/openapi.yaml")
|
|
|
53
55
|
print(doc["info"]["title"])
|
|
54
56
|
|
|
55
57
|
# Parse from JSON string
|
|
56
|
-
json_doc = '{"openapi":"3.1.
|
|
58
|
+
json_doc = '{"openapi":"3.1.2","info":{"title":"My API","version":"1.0.0"}}'
|
|
57
59
|
doc = parser.parse(json_doc)
|
|
58
60
|
|
|
59
61
|
# Parse from YAML string
|
|
60
62
|
yaml_doc = """
|
|
61
|
-
openapi: 3.1.
|
|
63
|
+
openapi: 3.1.2
|
|
62
64
|
info:
|
|
63
65
|
title: My API
|
|
64
66
|
version: 1.0.0
|
|
@@ -95,6 +97,14 @@ parser = OpenAPIParser("ruamel-roundtrip")
|
|
|
95
97
|
doc = parser.parse("file:///path/to/openapi.yaml", return_type=CommentedMap)
|
|
96
98
|
# Access line/column information
|
|
97
99
|
print(doc.lc.line, doc.lc.col)
|
|
100
|
+
|
|
101
|
+
# Use ruamel.yaml AST mode (returns YAML nodes with source tracking)
|
|
102
|
+
from jentic.apitools.openapi.parser.backends.ruamel_ast import MappingNode
|
|
103
|
+
parser = OpenAPIParser("ruamel-ast")
|
|
104
|
+
node = parser.parse("file:///path/to/openapi.yaml", return_type=MappingNode)
|
|
105
|
+
# Access precise line/column for any node
|
|
106
|
+
for key_node, value_node in node.value:
|
|
107
|
+
print(f"{key_node.value} at line {key_node.start_mark.line}")
|
|
98
108
|
```
|
|
99
109
|
|
|
100
110
|
## Configuration Options
|
|
@@ -171,7 +181,7 @@ class OpenAPIParser:
|
|
|
171
181
|
|
|
172
182
|
**Parameters:**
|
|
173
183
|
- `backend`: Parser backend to use. Can be:
|
|
174
|
-
- `str`: Name of a backend registered via entry points (e.g., "pyyaml", "ruamel-safe", "ruamel-roundtrip")
|
|
184
|
+
- `str`: Name of a backend registered via entry points (e.g., "pyyaml", "ruamel-safe", "ruamel-roundtrip", "ruamel-ast", "datamodel-low")
|
|
175
185
|
- `BaseParserBackend`: Instance of a parser backend
|
|
176
186
|
- `Type[BaseParserBackend]`: Class of a parser backend (will be instantiated)
|
|
177
187
|
- Defaults to `"pyyaml"` if `None`
|
|
@@ -282,6 +292,91 @@ doc = parser.parse(content, return_type=CommentedMap)
|
|
|
282
292
|
print(f"Line: {doc.lc.line}, Column: {doc.lc.col}")
|
|
283
293
|
```
|
|
284
294
|
|
|
295
|
+
### ruamel-ast
|
|
296
|
+
ruamel.yaml AST mode that returns YAML nodes with complete source location tracking. Ideal for building low-level data models with precise error reporting.
|
|
297
|
+
|
|
298
|
+
**Accepts:** `text` (JSON/YAML strings), `uri` (file paths/URLs)
|
|
299
|
+
|
|
300
|
+
**Returns:** `yaml.MappingNode` (YAML AST) instead of dictionaries
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
from jentic.apitools.openapi.parser.backends.ruamel_ast import MappingNode
|
|
304
|
+
|
|
305
|
+
parser = OpenAPIParser("ruamel-ast")
|
|
306
|
+
node = parser.parse(content, return_type=MappingNode)
|
|
307
|
+
|
|
308
|
+
# Access YAML nodes with source information
|
|
309
|
+
assert isinstance(node, MappingNode)
|
|
310
|
+
|
|
311
|
+
# Get precise line/column information for any node
|
|
312
|
+
for key_node, value_node in node.value:
|
|
313
|
+
print(f"Key: {key_node.value}")
|
|
314
|
+
print(f" Line: {key_node.start_mark.line}, Column: {key_node.start_mark.column}")
|
|
315
|
+
|
|
316
|
+
# Perfect for building low-level datamodels with source tracking
|
|
317
|
+
# Works seamlessly with jentic-openapi-datamodels
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
**Note:** You can also import directly from `ruamel.yaml` if preferred:
|
|
321
|
+
```python
|
|
322
|
+
from ruamel.yaml import MappingNode # Alternative import
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Use Cases:**
|
|
326
|
+
- Building low-level data models that preserve source locations
|
|
327
|
+
- Implementing precise error reporting with line/column numbers
|
|
328
|
+
- AST-based transformations and analysis
|
|
329
|
+
- Integration with validation tools that need exact source positions
|
|
330
|
+
|
|
331
|
+
### datamodel-low
|
|
332
|
+
Low-level OpenAPI data model parser that automatically detects the OpenAPI version (3.0.x or 3.1.x) and returns the appropriate typed datamodel with complete source tracking.
|
|
333
|
+
|
|
334
|
+
**Accepts:** `text` (JSON/YAML strings), `uri` (file paths/URLs)
|
|
335
|
+
|
|
336
|
+
**Returns:** `OpenAPI30` or `OpenAPI31` data model (from `jentic-openapi-datamodels`)
|
|
337
|
+
|
|
338
|
+
```python
|
|
339
|
+
from jentic.apitools.openapi.parser.core import OpenAPIParser
|
|
340
|
+
from jentic.apitools.openapi.datamodels.low.v30.openapi import OpenAPI30
|
|
341
|
+
from jentic.apitools.openapi.datamodels.low.v31.openapi import OpenAPI31
|
|
342
|
+
|
|
343
|
+
parser = OpenAPIParser("datamodel-low")
|
|
344
|
+
|
|
345
|
+
# Parse OpenAPI 3.0.x document
|
|
346
|
+
doc = parser.parse("file:///path/to/openapi-3.0.yaml")
|
|
347
|
+
assert isinstance(doc, OpenAPI30)
|
|
348
|
+
print(doc.openapi.value) # "3.0.4"
|
|
349
|
+
print(doc.info.value.title.value) # Access with source tracking
|
|
350
|
+
|
|
351
|
+
# Parse OpenAPI 3.1.x document
|
|
352
|
+
doc = parser.parse("file:///path/to/openapi-3.1.yaml")
|
|
353
|
+
assert isinstance(doc, OpenAPI31)
|
|
354
|
+
print(doc.openapi.value) # "3.1.2"
|
|
355
|
+
|
|
356
|
+
# Access fields with source information
|
|
357
|
+
print(f"Title at line {doc.info.key_node.start_mark.line}")
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
**Features:**
|
|
361
|
+
- **Automatic Version Detection**: Parses `openapi` field and routes to correct builder
|
|
362
|
+
- **Typed Datamodels**: Returns strongly-typed `OpenAPI30` or `OpenAPI31` objects
|
|
363
|
+
- **Complete Source Tracking**: Every field preserves YAML node information
|
|
364
|
+
- **Error Handling**: Clear errors for unsupported versions (2.0, 3.2+)
|
|
365
|
+
|
|
366
|
+
**Supported Versions:**
|
|
367
|
+
- OpenAPI 3.0.x → returns `OpenAPI30`
|
|
368
|
+
- OpenAPI 3.1.x → returns `OpenAPI31`
|
|
369
|
+
|
|
370
|
+
**Unsupported (raises ValueError):**
|
|
371
|
+
- OpenAPI 2.0 (Swagger)
|
|
372
|
+
- OpenAPI 3.2.x and above
|
|
373
|
+
|
|
374
|
+
**Use Cases:**
|
|
375
|
+
- Type-safe OpenAPI document manipulation
|
|
376
|
+
- Building validation tools with precise error messages
|
|
377
|
+
- Code generation from OpenAPI specifications
|
|
378
|
+
- AST transformations with version-aware logic
|
|
379
|
+
|
|
285
380
|
## Testing
|
|
286
381
|
|
|
287
382
|
Run the test suite:
|
|
@@ -6,7 +6,8 @@ A Python library for parsing OpenAPI documents using pluggable parser backends.
|
|
|
6
6
|
|
|
7
7
|
- **Pluggable Backend Architecture**: Support for multiple parsing strategies via entry points
|
|
8
8
|
- **Multiple Input Formats**: Parse OpenAPI documents from file URIs or text strings (JSON/YAML)
|
|
9
|
-
- **Multiple Parser Backends**: Choose from PyYAML, ruamel.yaml, or
|
|
9
|
+
- **Multiple Parser Backends**: Choose from PyYAML, ruamel.yaml (safe/roundtrip/AST modes), or typed datamodels
|
|
10
|
+
- **Low-Level Datamodels**: Parse directly to typed `OpenAPI30`/`OpenAPI31` objects with source tracking
|
|
10
11
|
- **Enhanced JSON Serialization**: Built-in support for datetime, UUID, Path, Decimal, Enum, and attrs classes
|
|
11
12
|
- **Type Safety**: Full type hints with overloaded methods for precise return types
|
|
12
13
|
- **Extensible Design**: Easy integration of third-party parser backends
|
|
@@ -35,12 +36,12 @@ doc = parser.parse("file:///path/to/openapi.yaml")
|
|
|
35
36
|
print(doc["info"]["title"])
|
|
36
37
|
|
|
37
38
|
# Parse from JSON string
|
|
38
|
-
json_doc = '{"openapi":"3.1.
|
|
39
|
+
json_doc = '{"openapi":"3.1.2","info":{"title":"My API","version":"1.0.0"}}'
|
|
39
40
|
doc = parser.parse(json_doc)
|
|
40
41
|
|
|
41
42
|
# Parse from YAML string
|
|
42
43
|
yaml_doc = """
|
|
43
|
-
openapi: 3.1.
|
|
44
|
+
openapi: 3.1.2
|
|
44
45
|
info:
|
|
45
46
|
title: My API
|
|
46
47
|
version: 1.0.0
|
|
@@ -77,6 +78,14 @@ parser = OpenAPIParser("ruamel-roundtrip")
|
|
|
77
78
|
doc = parser.parse("file:///path/to/openapi.yaml", return_type=CommentedMap)
|
|
78
79
|
# Access line/column information
|
|
79
80
|
print(doc.lc.line, doc.lc.col)
|
|
81
|
+
|
|
82
|
+
# Use ruamel.yaml AST mode (returns YAML nodes with source tracking)
|
|
83
|
+
from jentic.apitools.openapi.parser.backends.ruamel_ast import MappingNode
|
|
84
|
+
parser = OpenAPIParser("ruamel-ast")
|
|
85
|
+
node = parser.parse("file:///path/to/openapi.yaml", return_type=MappingNode)
|
|
86
|
+
# Access precise line/column for any node
|
|
87
|
+
for key_node, value_node in node.value:
|
|
88
|
+
print(f"{key_node.value} at line {key_node.start_mark.line}")
|
|
80
89
|
```
|
|
81
90
|
|
|
82
91
|
## Configuration Options
|
|
@@ -153,7 +162,7 @@ class OpenAPIParser:
|
|
|
153
162
|
|
|
154
163
|
**Parameters:**
|
|
155
164
|
- `backend`: Parser backend to use. Can be:
|
|
156
|
-
- `str`: Name of a backend registered via entry points (e.g., "pyyaml", "ruamel-safe", "ruamel-roundtrip")
|
|
165
|
+
- `str`: Name of a backend registered via entry points (e.g., "pyyaml", "ruamel-safe", "ruamel-roundtrip", "ruamel-ast", "datamodel-low")
|
|
157
166
|
- `BaseParserBackend`: Instance of a parser backend
|
|
158
167
|
- `Type[BaseParserBackend]`: Class of a parser backend (will be instantiated)
|
|
159
168
|
- Defaults to `"pyyaml"` if `None`
|
|
@@ -264,6 +273,91 @@ doc = parser.parse(content, return_type=CommentedMap)
|
|
|
264
273
|
print(f"Line: {doc.lc.line}, Column: {doc.lc.col}")
|
|
265
274
|
```
|
|
266
275
|
|
|
276
|
+
### ruamel-ast
|
|
277
|
+
ruamel.yaml AST mode that returns YAML nodes with complete source location tracking. Ideal for building low-level data models with precise error reporting.
|
|
278
|
+
|
|
279
|
+
**Accepts:** `text` (JSON/YAML strings), `uri` (file paths/URLs)
|
|
280
|
+
|
|
281
|
+
**Returns:** `yaml.MappingNode` (YAML AST) instead of dictionaries
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
from jentic.apitools.openapi.parser.backends.ruamel_ast import MappingNode
|
|
285
|
+
|
|
286
|
+
parser = OpenAPIParser("ruamel-ast")
|
|
287
|
+
node = parser.parse(content, return_type=MappingNode)
|
|
288
|
+
|
|
289
|
+
# Access YAML nodes with source information
|
|
290
|
+
assert isinstance(node, MappingNode)
|
|
291
|
+
|
|
292
|
+
# Get precise line/column information for any node
|
|
293
|
+
for key_node, value_node in node.value:
|
|
294
|
+
print(f"Key: {key_node.value}")
|
|
295
|
+
print(f" Line: {key_node.start_mark.line}, Column: {key_node.start_mark.column}")
|
|
296
|
+
|
|
297
|
+
# Perfect for building low-level datamodels with source tracking
|
|
298
|
+
# Works seamlessly with jentic-openapi-datamodels
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**Note:** You can also import directly from `ruamel.yaml` if preferred:
|
|
302
|
+
```python
|
|
303
|
+
from ruamel.yaml import MappingNode # Alternative import
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
**Use Cases:**
|
|
307
|
+
- Building low-level data models that preserve source locations
|
|
308
|
+
- Implementing precise error reporting with line/column numbers
|
|
309
|
+
- AST-based transformations and analysis
|
|
310
|
+
- Integration with validation tools that need exact source positions
|
|
311
|
+
|
|
312
|
+
### datamodel-low
|
|
313
|
+
Low-level OpenAPI data model parser that automatically detects the OpenAPI version (3.0.x or 3.1.x) and returns the appropriate typed datamodel with complete source tracking.
|
|
314
|
+
|
|
315
|
+
**Accepts:** `text` (JSON/YAML strings), `uri` (file paths/URLs)
|
|
316
|
+
|
|
317
|
+
**Returns:** `OpenAPI30` or `OpenAPI31` data model (from `jentic-openapi-datamodels`)
|
|
318
|
+
|
|
319
|
+
```python
|
|
320
|
+
from jentic.apitools.openapi.parser.core import OpenAPIParser
|
|
321
|
+
from jentic.apitools.openapi.datamodels.low.v30.openapi import OpenAPI30
|
|
322
|
+
from jentic.apitools.openapi.datamodels.low.v31.openapi import OpenAPI31
|
|
323
|
+
|
|
324
|
+
parser = OpenAPIParser("datamodel-low")
|
|
325
|
+
|
|
326
|
+
# Parse OpenAPI 3.0.x document
|
|
327
|
+
doc = parser.parse("file:///path/to/openapi-3.0.yaml")
|
|
328
|
+
assert isinstance(doc, OpenAPI30)
|
|
329
|
+
print(doc.openapi.value) # "3.0.4"
|
|
330
|
+
print(doc.info.value.title.value) # Access with source tracking
|
|
331
|
+
|
|
332
|
+
# Parse OpenAPI 3.1.x document
|
|
333
|
+
doc = parser.parse("file:///path/to/openapi-3.1.yaml")
|
|
334
|
+
assert isinstance(doc, OpenAPI31)
|
|
335
|
+
print(doc.openapi.value) # "3.1.2"
|
|
336
|
+
|
|
337
|
+
# Access fields with source information
|
|
338
|
+
print(f"Title at line {doc.info.key_node.start_mark.line}")
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Features:**
|
|
342
|
+
- **Automatic Version Detection**: Parses `openapi` field and routes to correct builder
|
|
343
|
+
- **Typed Datamodels**: Returns strongly-typed `OpenAPI30` or `OpenAPI31` objects
|
|
344
|
+
- **Complete Source Tracking**: Every field preserves YAML node information
|
|
345
|
+
- **Error Handling**: Clear errors for unsupported versions (2.0, 3.2+)
|
|
346
|
+
|
|
347
|
+
**Supported Versions:**
|
|
348
|
+
- OpenAPI 3.0.x → returns `OpenAPI30`
|
|
349
|
+
- OpenAPI 3.1.x → returns `OpenAPI31`
|
|
350
|
+
|
|
351
|
+
**Unsupported (raises ValueError):**
|
|
352
|
+
- OpenAPI 2.0 (Swagger)
|
|
353
|
+
- OpenAPI 3.2.x and above
|
|
354
|
+
|
|
355
|
+
**Use Cases:**
|
|
356
|
+
- Type-safe OpenAPI document manipulation
|
|
357
|
+
- Building validation tools with precise error messages
|
|
358
|
+
- Code generation from OpenAPI specifications
|
|
359
|
+
- AST transformations with version-aware logic
|
|
360
|
+
|
|
267
361
|
## Testing
|
|
268
362
|
|
|
269
363
|
Run the test suite:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "jentic-openapi-parser"
|
|
3
|
-
version = "1.0.0-alpha.
|
|
3
|
+
version = "1.0.0-alpha.21"
|
|
4
4
|
description = "Jentic OpenAPI Parser"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "Jentic", email = "hello@jentic.com" }]
|
|
@@ -9,7 +9,8 @@ license-files = ["LICENSE", "NOTICE"]
|
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
10
|
dependencies = [
|
|
11
11
|
"attrs~=25.4.0",
|
|
12
|
-
"jentic-openapi-common~=1.0.0-alpha.
|
|
12
|
+
"jentic-openapi-common~=1.0.0-alpha.21",
|
|
13
|
+
"jentic-openapi-datamodels~=1.0.0-alpha.21",
|
|
13
14
|
"pyyaml~=6.0.3",
|
|
14
15
|
"requests~=2.32.5",
|
|
15
16
|
"ruamel-yaml~=0.18.15"
|
|
@@ -28,6 +29,7 @@ module-root = "src/"
|
|
|
28
29
|
|
|
29
30
|
[tool.uv.sources]
|
|
30
31
|
jentic-openapi-common = { workspace = true }
|
|
32
|
+
jentic-openapi-datamodels = { workspace = true }
|
|
31
33
|
|
|
32
34
|
[build-system]
|
|
33
35
|
requires = ["uv_build~=0.8.15"]
|
|
@@ -37,4 +39,6 @@ build-backend = "uv_build"
|
|
|
37
39
|
pyyaml = "jentic.apitools.openapi.parser.backends.pyyaml:PyYAMLParserBackend"
|
|
38
40
|
ruamel-safe = "jentic.apitools.openapi.parser.backends.ruamel_safe:RuamelSafeParserBackend"
|
|
39
41
|
ruamel-roundtrip = "jentic.apitools.openapi.parser.backends.ruamel_roundtrip:RuamelRoundTripParserBackend"
|
|
42
|
+
ruamel-ast = "jentic.apitools.openapi.parser.backends.ruamel_ast:RuamelASTParserBackend"
|
|
43
|
+
datamodel-low = "jentic.apitools.openapi.parser.backends.datamodel_low:DataModelLowParserBackend"
|
|
40
44
|
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from collections.abc import Sequence
|
|
3
|
+
from typing import Literal, TypeAlias
|
|
4
|
+
|
|
5
|
+
from ruamel.yaml import Node
|
|
6
|
+
|
|
7
|
+
from jentic.apitools.openapi.common.uri import is_uri_like
|
|
8
|
+
from jentic.apitools.openapi.common.version_detection import is_openapi_30, is_openapi_31
|
|
9
|
+
from jentic.apitools.openapi.datamodels.low.sources import ValueSource
|
|
10
|
+
from jentic.apitools.openapi.datamodels.low.v30 import build as build_v30
|
|
11
|
+
from jentic.apitools.openapi.datamodels.low.v30.openapi import OpenAPI30
|
|
12
|
+
from jentic.apitools.openapi.datamodels.low.v31 import build as build_v31
|
|
13
|
+
from jentic.apitools.openapi.datamodels.low.v31.openapi import OpenAPI31
|
|
14
|
+
from jentic.apitools.openapi.parser.backends.base import BaseParserBackend
|
|
15
|
+
from jentic.apitools.openapi.parser.backends.ruamel_ast import RuamelASTParserBackend
|
|
16
|
+
from jentic.apitools.openapi.parser.core.loader import load_uri
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Type alias for the datamodel-low return type
|
|
20
|
+
DataModelLow: TypeAlias = OpenAPI30 | OpenAPI31 | ValueSource
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
__all__ = ["DataModelLowParserBackend", "DataModelLow"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DataModelLowParserBackend(BaseParserBackend):
|
|
27
|
+
"""Parser backend that returns low-level OpenAPI datamodels.
|
|
28
|
+
|
|
29
|
+
This backend uses the RuamelASTParserBackend to parse YAML documents into
|
|
30
|
+
AST nodes, then automatically detects the OpenAPI version and builds the
|
|
31
|
+
appropriate low-level data model (OpenAPI 3.0 or 3.1).
|
|
32
|
+
|
|
33
|
+
The returned datamodels preserve all source location information from the
|
|
34
|
+
original YAML document, enabling precise error reporting and validation.
|
|
35
|
+
|
|
36
|
+
Supported versions:
|
|
37
|
+
- OpenAPI 3.0.x → returns OpenAPI30 data model
|
|
38
|
+
- OpenAPI 3.1.x → returns OpenAPI31 data model
|
|
39
|
+
|
|
40
|
+
Unsupported versions (raises ValueError):
|
|
41
|
+
- OpenAPI 2.0 (Swagger)
|
|
42
|
+
- OpenAPI 3.2.x (not yet released/supported)
|
|
43
|
+
- Any other version
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self, pure: bool = True):
|
|
47
|
+
"""Initialize the datamodel-low parser backend.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
pure: Whether to use pure Python YAML implementation (default: True).
|
|
51
|
+
Set to False to use libyaml if available for better performance.
|
|
52
|
+
"""
|
|
53
|
+
self._ast_backend = RuamelASTParserBackend(pure=pure)
|
|
54
|
+
|
|
55
|
+
def parse(self, document: str, *, logger: logging.Logger | None = None) -> DataModelLow:
|
|
56
|
+
"""Parse an OpenAPI document and return the appropriate data model.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
document: URI/path to OpenAPI document, or YAML/JSON string
|
|
60
|
+
logger: Optional logger for diagnostic messages
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
OpenAPI30 or OpenAPI31 data model depending on document version,
|
|
64
|
+
or ValueSource if the document is invalid
|
|
65
|
+
|
|
66
|
+
Raises:
|
|
67
|
+
ValueError: If OpenAPI version is unsupported (2.0, 3.2, etc.)
|
|
68
|
+
"""
|
|
69
|
+
logger = logger or logging.getLogger(__name__)
|
|
70
|
+
|
|
71
|
+
if is_uri_like(document):
|
|
72
|
+
return self._parse_uri(document, logger)
|
|
73
|
+
return self._parse_text(document, logger)
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def accepts() -> Sequence[Literal["uri", "text"]]:
|
|
77
|
+
"""Return supported input formats.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Sequence of supported document format identifiers:
|
|
81
|
+
- "uri": File path or URI pointing to OpenAPI Document
|
|
82
|
+
- "text": String (JSON/YAML) representation
|
|
83
|
+
"""
|
|
84
|
+
return ["uri", "text"]
|
|
85
|
+
|
|
86
|
+
def _parse_uri(self, uri: str, logger: logging.Logger) -> DataModelLow:
|
|
87
|
+
"""Parse an OpenAPI document from a URI."""
|
|
88
|
+
logger.debug("Starting download of %s", uri)
|
|
89
|
+
return self._parse_text(load_uri(uri, 5, 10, logger), logger)
|
|
90
|
+
|
|
91
|
+
def _parse_text(self, text: str, logger: logging.Logger) -> DataModelLow:
|
|
92
|
+
"""Parse an OpenAPI document from text."""
|
|
93
|
+
if is_openapi_30(text):
|
|
94
|
+
logger.debug("Building OpenAPI 3.0.x data model")
|
|
95
|
+
ast_node: Node = self._ast_backend.parse(text, logger=logger)
|
|
96
|
+
return build_v30(ast_node)
|
|
97
|
+
elif is_openapi_31(text):
|
|
98
|
+
logger.debug("Building OpenAPI 3.1.x data model")
|
|
99
|
+
ast_node: Node = self._ast_backend.parse(text, logger=logger)
|
|
100
|
+
return build_v31(ast_node)
|
|
101
|
+
else:
|
|
102
|
+
raise ValueError("Unsupported OpenAPI version. Supported versions: 3.0.x, 3.1.x")
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from collections.abc import Sequence
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from ruamel.yaml import YAML, MappingNode
|
|
6
|
+
|
|
7
|
+
from jentic.apitools.openapi.common.uri import is_uri_like
|
|
8
|
+
from jentic.apitools.openapi.parser.backends.base import BaseParserBackend
|
|
9
|
+
from jentic.apitools.openapi.parser.core.loader import load_uri
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"RuamelASTParserBackend",
|
|
14
|
+
# Re-export Mapping YAML node type for convenience
|
|
15
|
+
"MappingNode",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RuamelASTParserBackend(BaseParserBackend):
|
|
20
|
+
def __init__(self, typ: str = "rt", pure: bool = True):
|
|
21
|
+
self.yaml = YAML(typ=typ, pure=pure)
|
|
22
|
+
self.yaml.default_flow_style = False
|
|
23
|
+
|
|
24
|
+
def parse(self, document: str, *, logger: logging.Logger | None = None) -> MappingNode:
|
|
25
|
+
logger = logger or logging.getLogger(__name__)
|
|
26
|
+
if is_uri_like(document):
|
|
27
|
+
return self._parse_uri(document, logger)
|
|
28
|
+
return self._parse_text(document, logger)
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def accepts() -> Sequence[Literal["uri", "text"]]:
|
|
32
|
+
"""Return supported input formats.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Sequence of supported document format identifiers:
|
|
36
|
+
- "uri": File path or URI pointing to OpenAPI Document
|
|
37
|
+
- "text": String (JSON/YAML) representation
|
|
38
|
+
"""
|
|
39
|
+
return ["uri", "text"]
|
|
40
|
+
|
|
41
|
+
def _parse_uri(self, uri: str, logger: logging.Logger) -> MappingNode:
|
|
42
|
+
logger.debug("Starting download of %s", uri)
|
|
43
|
+
return self._parse_text(load_uri(uri, 5, 10, logger), logger)
|
|
44
|
+
|
|
45
|
+
def _parse_text(self, text: str, logger: logging.Logger) -> MappingNode:
|
|
46
|
+
if not isinstance(text, (bytes, str)):
|
|
47
|
+
raise TypeError(f"Unsupported document type: {type(text)!r}")
|
|
48
|
+
|
|
49
|
+
if isinstance(text, bytes):
|
|
50
|
+
text = text.decode()
|
|
51
|
+
|
|
52
|
+
node: MappingNode = self.yaml.compose(text)
|
|
53
|
+
logger.debug("YAML document successfully parsed")
|
|
54
|
+
|
|
55
|
+
if not isinstance(node, MappingNode):
|
|
56
|
+
raise TypeError(f"Parsed YAML document is not a mapping: {type(node)!r}")
|
|
57
|
+
|
|
58
|
+
return node
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
from ruamel.yaml import CommentedMap
|
|
2
|
+
|
|
1
3
|
from jentic.apitools.openapi.parser.backends.ruamel_safe import RuamelSafeParserBackend
|
|
2
4
|
|
|
3
5
|
|
|
4
|
-
__all__ = [
|
|
6
|
+
__all__ = [
|
|
7
|
+
"RuamelRoundTripParserBackend",
|
|
8
|
+
# Re-export CommentedMap type for convenience
|
|
9
|
+
"CommentedMap",
|
|
10
|
+
]
|
|
5
11
|
|
|
6
12
|
|
|
7
13
|
class RuamelRoundTripParserBackend(RuamelSafeParserBackend):
|
|
@@ -9,7 +9,11 @@ from jentic.apitools.openapi.parser.backends.base import BaseParserBackend
|
|
|
9
9
|
from jentic.apitools.openapi.parser.core.loader import load_uri
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
__all__ = [
|
|
12
|
+
__all__ = [
|
|
13
|
+
"RuamelSafeParserBackend",
|
|
14
|
+
# Re-export CommentedMap type for convenience
|
|
15
|
+
"CommentedMap",
|
|
16
|
+
]
|
|
13
17
|
|
|
14
18
|
|
|
15
19
|
class RuamelSafeParserBackend(BaseParserBackend):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|