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.
Files changed (18) hide show
  1. {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/PKG-INFO +101 -6
  2. {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/README.md +98 -4
  3. {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/pyproject.toml +6 -2
  4. jentic_openapi_parser-1.0.0a21/src/jentic/apitools/openapi/parser/backends/datamodel_low.py +102 -0
  5. jentic_openapi_parser-1.0.0a21/src/jentic/apitools/openapi/parser/backends/ruamel_ast.py +58 -0
  6. {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/backends/ruamel_roundtrip.py +7 -1
  7. {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/backends/ruamel_safe.py +5 -1
  8. {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/LICENSE +0 -0
  9. {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/NOTICE +0 -0
  10. {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/backends/base.py +0 -0
  11. {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/backends/py.typed +0 -0
  12. {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/backends/pyyaml.py +0 -0
  13. {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/core/__init__.py +0 -0
  14. {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/core/exceptions.py +0 -0
  15. {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/core/loader.py +0 -0
  16. {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/core/openapi_parser.py +0 -0
  17. {jentic_openapi_parser-1.0.0a19 → jentic_openapi_parser-1.0.0a21}/src/jentic/apitools/openapi/parser/core/py.typed +0 -0
  18. {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.0a19
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.0a19
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 ruamel.yaml roundtrip modes
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.0","info":{"title":"My API","version":"1.0.0"}}'
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.0
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 ruamel.yaml roundtrip modes
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.0","info":{"title":"My API","version":"1.0.0"}}'
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.0
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.19"
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.19",
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__ = ["RuamelRoundTripParserBackend"]
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__ = ["RuamelSafeParserBackend"]
12
+ __all__ = [
13
+ "RuamelSafeParserBackend",
14
+ # Re-export CommentedMap type for convenience
15
+ "CommentedMap",
16
+ ]
13
17
 
14
18
 
15
19
  class RuamelSafeParserBackend(BaseParserBackend):