jentic-openapi-datamodels 1.0.0a12__py3-none-any.whl → 1.0.0a13__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 (26) hide show
  1. jentic/apitools/openapi/datamodels/low/context.py +21 -0
  2. jentic/apitools/openapi/datamodels/low/extractors.py +126 -0
  3. jentic/apitools/openapi/datamodels/low/fields.py +45 -0
  4. jentic/apitools/openapi/datamodels/low/model_builder.py +113 -0
  5. jentic/apitools/openapi/datamodels/low/py.typed +0 -0
  6. jentic/apitools/openapi/datamodels/low/sources.py +89 -0
  7. jentic/apitools/openapi/datamodels/low/v30/__init__.py +0 -28
  8. jentic/apitools/openapi/datamodels/low/v30/discriminator.py +53 -78
  9. jentic/apitools/openapi/datamodels/low/v30/external_documentation.py +47 -61
  10. jentic/apitools/openapi/datamodels/low/v30/oauth_flow.py +54 -123
  11. jentic/apitools/openapi/datamodels/low/v30/oauth_flows.py +102 -151
  12. jentic/apitools/openapi/datamodels/low/v30/reference.py +43 -44
  13. jentic/apitools/openapi/datamodels/low/v30/schema.py +316 -607
  14. jentic/apitools/openapi/datamodels/low/v30/security_requirement.py +82 -72
  15. jentic/apitools/openapi/datamodels/low/v30/security_scheme.py +94 -286
  16. jentic/apitools/openapi/datamodels/low/v30/tag.py +88 -119
  17. jentic/apitools/openapi/datamodels/low/v30/xml.py +46 -120
  18. jentic_openapi_datamodels-1.0.0a13.dist-info/METADATA +211 -0
  19. jentic_openapi_datamodels-1.0.0a13.dist-info/RECORD +23 -0
  20. jentic/apitools/openapi/datamodels/low/v30/specification_object.py +0 -217
  21. jentic_openapi_datamodels-1.0.0a12.dist-info/METADATA +0 -52
  22. jentic_openapi_datamodels-1.0.0a12.dist-info/RECORD +0 -18
  23. /jentic/apitools/openapi/datamodels/low/{v30/py.typed → __init__.py} +0 -0
  24. {jentic_openapi_datamodels-1.0.0a12.dist-info → jentic_openapi_datamodels-1.0.0a13.dist-info}/WHEEL +0 -0
  25. {jentic_openapi_datamodels-1.0.0a12.dist-info → jentic_openapi_datamodels-1.0.0a13.dist-info}/licenses/LICENSE +0 -0
  26. {jentic_openapi_datamodels-1.0.0a12.dist-info → jentic_openapi_datamodels-1.0.0a13.dist-info}/licenses/NOTICE +0 -0
@@ -1,132 +1,101 @@
1
- """
2
- OpenAPI 3.0.4 Tag Object model.
3
-
4
- Adds metadata to a single tag that is used by operations.
5
- """
6
-
7
- from collections.abc import Mapping
8
- from typing import Any
9
-
1
+ from dataclasses import dataclass, field, replace
2
+
3
+ from ruamel import yaml
4
+
5
+ from jentic.apitools.openapi.datamodels.low.context import Context
6
+ from jentic.apitools.openapi.datamodels.low.fields import fixed_field
7
+ from jentic.apitools.openapi.datamodels.low.model_builder import build_model
8
+ from jentic.apitools.openapi.datamodels.low.sources import (
9
+ FieldSource,
10
+ KeySource,
11
+ ValueSource,
12
+ YAMLInvalidValue,
13
+ YAMLValue,
14
+ )
10
15
  from jentic.apitools.openapi.datamodels.low.v30.external_documentation import (
11
16
  ExternalDocumentation,
12
17
  )
13
- from jentic.apitools.openapi.datamodels.low.v30.specification_object import SpecificationObject
18
+ from jentic.apitools.openapi.datamodels.low.v30.external_documentation import (
19
+ build as build_external_documentation,
20
+ )
14
21
 
15
22
 
16
- __all__ = ["Tag"]
23
+ __all__ = ["Tag", "build"]
17
24
 
18
25
 
19
- class Tag(SpecificationObject):
26
+ @dataclass(frozen=True, slots=True)
27
+ class Tag:
20
28
  """
21
- Represents a Tag Object from OpenAPI 3.0.4.
29
+ Tag Object representation for OpenAPI 3.0.
22
30
 
23
- Adds metadata to a single tag that is used by the Operation Object.
24
- It is not mandatory to have a Tag Object per tag defined in the Operation Object instances.
31
+ Adds metadata to a single tag that is used by the Operation Object. It is not mandatory
32
+ to have a Tag Object per tag defined in the Operation Object instances.
25
33
 
26
- Supports specification extensions (x-* fields).
34
+ Attributes:
35
+ root_node: The top-level node representing the entire Tag object in the original source file
36
+ name: The name of the tag. REQUIRED.
37
+ description: A short description for the tag. CommonMark syntax MAY be used for rich text representation.
38
+ external_docs: Additional external documentation for this tag.
39
+ extensions: Specification extensions (x-* fields)
40
+ """
27
41
 
28
- Example:
29
- >>> # Basic tag
30
- >>> tag = Tag({
31
- ... "name": "pet",
32
- ... "description": "Everything about your Pets"
33
- ... })
34
- >>> tag.name
35
- 'pet'
36
- >>> tag.description
37
- 'Everything about your Pets'
38
-
39
- >>> # Tag with external docs
40
- >>> tag = Tag({
41
- ... "name": "store",
42
- ... "description": "Access to Petstore orders",
43
- ... "externalDocs": {
44
- ... "description": "Find out more",
45
- ... "url": "http://example.com"
46
- ... }
47
- ... })
48
- >>> tag.external_docs.url
49
- 'http://example.com'
42
+ root_node: yaml.Node
43
+ name: FieldSource[str] | None = fixed_field()
44
+ description: FieldSource[str] | None = fixed_field()
45
+ external_docs: FieldSource[ExternalDocumentation] | None = fixed_field(
46
+ metadata={"yaml_name": "externalDocs"}
47
+ )
48
+ extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
49
+
50
+
51
+ def build(root: yaml.Node, context: Context | None = None) -> Tag | ValueSource[YAMLInvalidValue]:
50
52
  """
53
+ Build a Tag object from a YAML node.
54
+
55
+ Preserves all source data as-is, regardless of type. This is a low-level/plumbing
56
+ model that provides complete source fidelity for inspection and validation.
57
+
58
+ Args:
59
+ root: The YAML node to parse (should be a MappingNode)
60
+ context: Optional parsing context. If None, a default context will be created.
51
61
 
52
- _supports_extensions: bool = True
53
- _fixed_fields: frozenset[str] = frozenset({"name", "description", "externalDocs"})
54
-
55
- def __init__(self, data: Mapping[str, Any] | None = None):
56
- """
57
- Initialize a Tag object.
58
-
59
- Automatically marshals nested externalDocs data (Mapping) into ExternalDocumentation instance.
60
-
61
- Args:
62
- data: Optional mapping to initialize the object with
63
- """
64
- super().__init__()
65
- if data:
66
- for key, value in data.items():
67
- # Marshal externalDocs field specifically if it's a raw Mapping
68
- if (
69
- key == "externalDocs"
70
- and isinstance(value, Mapping)
71
- and not isinstance(value, ExternalDocumentation)
72
- ):
73
- self[key] = ExternalDocumentation(value)
74
- else:
75
- # Store as-is (already ExternalDocumentation, extension, or other)
76
- self[key] = self._copy_value(value)
77
-
78
- @property
79
- def name(self) -> str | None:
80
- """
81
- The name of the tag.
82
-
83
- REQUIRED field.
84
-
85
- Returns:
86
- Tag name or None if not present
87
- """
88
- return self.get("name")
89
-
90
- @name.setter
91
- def name(self, value: str | None) -> None:
92
- """Set the tag name."""
93
- if value is None:
94
- self.pop("name", None)
95
- else:
96
- self["name"] = value
97
-
98
- @property
99
- def description(self) -> str | None:
100
- """
101
- A description for the tag.
102
-
103
- Returns:
104
- Description or None if not present
105
- """
106
- return self.get("description")
107
-
108
- @description.setter
109
- def description(self, value: str | None) -> None:
110
- """Set the description."""
111
- if value is None:
112
- self.pop("description", None)
113
- else:
114
- self["description"] = value
115
-
116
- @property
117
- def external_docs(self) -> ExternalDocumentation | None:
118
- """
119
- Additional external documentation for this tag.
120
-
121
- Returns:
122
- ExternalDocumentation instance or None if not present
123
- """
124
- return self.get("externalDocs")
125
-
126
- @external_docs.setter
127
- def external_docs(self, value: ExternalDocumentation | None) -> None:
128
- """Set the external documentation."""
129
- if value is None:
130
- self.pop("externalDocs", None)
131
- else:
132
- self["externalDocs"] = value
62
+ Returns:
63
+ A Tag object if the node is valid, or a ValueSource containing
64
+ the invalid data if the root is not a MappingNode (preserving the invalid data
65
+ and its source location for validation).
66
+
67
+ Example:
68
+ from ruamel.yaml import YAML
69
+ yaml = YAML()
70
+ root = yaml.compose("name: pet\\ndescription: Everything about your Pets")
71
+ tag = build(root)
72
+ assert tag.name.value == 'pet'
73
+ """
74
+ # Initialize context once at the beginning
75
+ if context is None:
76
+ context = Context()
77
+
78
+ if not isinstance(root, yaml.MappingNode):
79
+ # Preserve invalid root data instead of returning None
80
+ value = context.yaml_constructor.construct_object(root, deep=True)
81
+ return ValueSource(value=value, value_node=root)
82
+
83
+ # Use build_model to handle most fields
84
+ tag = build_model(root, Tag, context=context)
85
+
86
+ # Manually handle special fields that build_model can't process (nested objects)
87
+ for key_node, value_node in root.value:
88
+ key = context.yaml_constructor.construct_yaml_str(key_node)
89
+
90
+ if key == "externalDocs":
91
+ # Handle nested ExternalDocumentation object - child builder handles invalid nodes
92
+ # FieldSource will auto-unwrap ValueSource if child returns it for invalid data
93
+ external_docs = FieldSource(
94
+ value=build_external_documentation(value_node, context=context),
95
+ key_node=key_node,
96
+ value_node=value_node,
97
+ )
98
+ tag = replace(tag, external_docs=external_docs)
99
+ break
100
+
101
+ return tag
@@ -1,134 +1,60 @@
1
- """
2
- OpenAPI 3.0.4 XML Object model.
1
+ from dataclasses import dataclass, field
3
2
 
4
- Metadata object for XML representation of properties.
5
- """
3
+ from ruamel import yaml
6
4
 
7
- from jentic.apitools.openapi.datamodels.low.v30.specification_object import SpecificationObject
5
+ from jentic.apitools.openapi.datamodels.low.context import Context
6
+ from jentic.apitools.openapi.datamodels.low.fields import fixed_field
7
+ from jentic.apitools.openapi.datamodels.low.model_builder import build_model
8
+ from jentic.apitools.openapi.datamodels.low.sources import (
9
+ FieldSource,
10
+ KeySource,
11
+ ValueSource,
12
+ YAMLInvalidValue,
13
+ YAMLValue,
14
+ )
8
15
 
9
16
 
10
- __all__ = ["XML"]
17
+ __all__ = ["XML", "build"]
11
18
 
12
19
 
13
- class XML(SpecificationObject):
20
+ @dataclass(frozen=True, slots=True)
21
+ class XML:
14
22
  """
15
- Represents an XML Object from OpenAPI 3.0.4.
16
-
17
- A metadata object that allows for more fine-tuned XML model definitions.
18
-
19
- Supports specification extensions (x-* fields).
20
-
21
- Example:
22
- >>> # Basic XML element name override
23
- >>> xml = XML({"name": "animal"})
24
- >>> xml.name
25
- 'animal'
26
-
27
- >>> # XML namespace
28
- >>> xml = XML({
29
- ... "name": "Person",
30
- ... "namespace": "http://example.com/schema/person",
31
- ... "prefix": "sample"
32
- ... })
33
- >>> xml.namespace
34
- 'http://example.com/schema/person'
23
+ XML Object representation for OpenAPI 3.0.
24
+
25
+ Attributes:
26
+ root_node: The top-level node representing the entire XML object in the original source file
27
+ name: Name of the XML element
28
+ namespace: XML namespace
29
+ prefix: XML namespace prefix
30
+ attribute: Whether property is an attribute (deprecated in OpenAPI 3.2+)
31
+ wrapped: Whether array is wrapped
32
+ extensions: Specification extensions
35
33
  """
36
34
 
37
- _supports_extensions: bool = True
38
- _fixed_fields: frozenset[str] = frozenset(
39
- {"name", "namespace", "prefix", "attribute", "wrapped"}
40
- )
41
-
42
- @property
43
- def name(self) -> str | None:
44
- """
45
- Replaces the name of the element/attribute.
46
-
47
- Returns:
48
- Element/attribute name or None if not present
49
- """
50
- return self.get("name")
51
-
52
- @name.setter
53
- def name(self, value: str | None) -> None:
54
- """Set the element/attribute name."""
55
- if value is None:
56
- self.pop("name", None)
57
- else:
58
- self["name"] = value
59
-
60
- @property
61
- def namespace(self) -> str | None:
62
- """
63
- The URI of the namespace definition.
64
-
65
- Returns:
66
- Namespace URI or None if not present
67
- """
68
- return self.get("namespace")
35
+ root_node: yaml.Node
36
+ name: FieldSource[str] | None = fixed_field()
37
+ namespace: FieldSource[str] | None = fixed_field()
38
+ prefix: FieldSource[str] | None = fixed_field()
39
+ attribute: FieldSource[bool] | None = fixed_field()
40
+ wrapped: FieldSource[bool] | None = fixed_field()
41
+ extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
69
42
 
70
- @namespace.setter
71
- def namespace(self, value: str | None) -> None:
72
- """Set the namespace URI."""
73
- if value is None:
74
- self.pop("namespace", None)
75
- else:
76
- self["namespace"] = value
77
43
 
78
- @property
79
- def prefix(self) -> str | None:
80
- """
81
- The prefix to be used for the name.
82
-
83
- Returns:
84
- Prefix or None if not present
85
- """
86
- return self.get("prefix")
87
-
88
- @prefix.setter
89
- def prefix(self, value: str | None) -> None:
90
- """Set the prefix."""
91
- if value is None:
92
- self.pop("prefix", None)
93
- else:
94
- self["prefix"] = value
95
-
96
- @property
97
- def attribute(self) -> bool | None:
98
- """
99
- Declares whether the property is an XML attribute.
100
-
101
- Returns:
102
- True if the property is an XML attribute,
103
- False if present and set to false,
104
- or None if not present.
105
- """
106
- return self.get("attribute")
107
-
108
- @attribute.setter
109
- def attribute(self, value: bool | None) -> None:
110
- """Set the attribute flag."""
111
- if value is None:
112
- self.pop("attribute", None)
113
- else:
114
- self["attribute"] = value
115
-
116
- @property
117
- def wrapped(self) -> bool | None:
118
- """
119
- For arrays, wraps the array in a containing element.
44
+ def build(root: yaml.Node, context: Context | None = None) -> XML | ValueSource[YAMLInvalidValue]:
45
+ """
46
+ Build an XML object from a YAML node.
120
47
 
121
- Only affects arrays. Returns None when not set.
48
+ Preserves all source data as-is, regardless of type. This is a low-level/plumbing
49
+ model that provides complete source fidelity for inspection and validation.
122
50
 
123
- Returns:
124
- True if wrapped, None if not set or not wrapped
125
- """
126
- return self.get("wrapped")
51
+ Args:
52
+ root: The YAML node to parse (should be a MappingNode)
53
+ context: Optional parsing context. If None, a default context will be created.
127
54
 
128
- @wrapped.setter
129
- def wrapped(self, value: bool | None) -> None:
130
- """Set the wrapped flag."""
131
- if value is None:
132
- self.pop("wrapped", None)
133
- else:
134
- self["wrapped"] = value
55
+ Returns:
56
+ An XML object if the node is valid, or a ValueSource containing
57
+ the invalid data if the root is not a MappingNode (preserving the invalid data
58
+ and its source location for validation).
59
+ """
60
+ return build_model(root, XML, context=context)
@@ -0,0 +1,211 @@
1
+ Metadata-Version: 2.4
2
+ Name: jentic-openapi-datamodels
3
+ Version: 1.0.0a13
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
+ from semantic_release.cli.commands.version import build_distributions
16
+
17
+ # jentic-openapi-datamodels
18
+
19
+ Low-level and high-level data models for OpenAPI specifications.
20
+
21
+ This package provides data model classes for representing OpenAPI specification objects in Python.
22
+
23
+ ## Features
24
+
25
+ **Low-Level Architecture**
26
+ - **Preserve Everything**: All data from source documents preserved exactly as-is, including invalid values
27
+ - **Zero Validation**: No validation or coercion during parsing - deferred to higher layers
28
+ - **Separation of Concerns**: Low-level model focuses on faithful representation; validation belongs elsewhere
29
+
30
+ **Source Tracking**
31
+ - **Complete Source Fidelity**: Every field tracks its exact YAML node location
32
+ - **Precise Error Reporting**: Line and column numbers via `start_mark` and `end_mark`
33
+ - **Metadata Preservation**: Full position tracking for accurate diagnostics
34
+
35
+ **Python Integration**
36
+ - **Python-Idiomatic Naming**: snake_case field names (e.g., `bearer_format`, `property_name`)
37
+ - **Spec-Aligned Mapping**: Automatic YAML name mapping (e.g., `bearerFormat` ↔ `bearer_format`)
38
+ - **Type Safety**: Full type hints with Generic types (`FieldSource[T]`, `KeySource[T]`, `ValueSource[T]`)
39
+
40
+ **Extensibility**
41
+ - **Extension Support**: Automatic extraction of OpenAPI `x-*` specification extensions
42
+ - **Unknown Field Tracking**: Capture typos and invalid fields for validation tools
43
+ - **Generic Builder Pattern**: Core `build_model()` function with object-specific builders for complex cases
44
+
45
+ **Performance**
46
+ - **Memory Efficient**: Immutable frozen dataclasses with `__slots__` for optimal memory usage
47
+ - **Shared Context**: All instances share a single YAML constructor for efficiency
48
+
49
+ **Version Support**
50
+ - **OpenAPI 2.0**: Planned for future release
51
+ - **OpenAPI 3.0.x**: Currently implemented
52
+ - **OpenAPI 3.1.x**: Planned for future release
53
+ - **OpenAPI 3.2.x**: Planned for future release
54
+
55
+ ## Installation
56
+
57
+ ```bash
58
+ pip install jentic-openapi-datamodels
59
+ ```
60
+
61
+ **Prerequisites:**
62
+ - Python 3.11+
63
+
64
+ ## Quick Start
65
+
66
+ ### Basic Usage
67
+
68
+ ```python
69
+ from ruamel.yaml import YAML
70
+ from jentic.apitools.openapi.datamodels.low.v30.security_scheme import build
71
+
72
+ # Parse YAML
73
+ yaml = YAML()
74
+ root = yaml.compose("""
75
+ type: http
76
+ scheme: bearer
77
+ bearerFormat: JWT
78
+ """)
79
+
80
+ # Build low-level model
81
+ security_scheme = build(root)
82
+
83
+ # Access via Python field names (snake_case)
84
+ print(security_scheme.bearer_format.value) # "JWT"
85
+
86
+ # Access source location information
87
+ print(security_scheme.bearer_format.key_node.value) # "bearerFormat"
88
+ print(security_scheme.bearer_format.key_node.start_mark.line) # Line number
89
+ ```
90
+
91
+ ### Field Name Mapping
92
+
93
+ YAML `camelCase` fields automatically map to Python `snake_case`:
94
+ - `bearerFormat` → `bearer_format`
95
+ - `authorizationUrl` → `authorization_url`
96
+ - `openIdConnectUrl` → `openid_connect_url`
97
+
98
+ Special cases for Python reserved keywords/special characters:
99
+ - `$ref` → `ref`
100
+ - `in` → `in_`
101
+
102
+ ### Source Tracking
103
+
104
+ The package provides three immutable wrapper types for preserving source information:
105
+
106
+ **FieldSource[T]** - For OpenAPI fields with key-value pairs
107
+ - Used for: Fixed fields (`name`, `bearer_format`) and patterned fields (status codes, path items, schema properties)
108
+ - Tracks: Both key and value nodes
109
+ - Example: `SecurityScheme.bearer_format` is `FieldSource[str]`, response status codes are `FieldSource[Response]`
110
+
111
+ **KeySource[T]** - For dictionary keys
112
+ - Used for: keys in OpenAPI fields, `x-*` extensions and mapping dictionaries
113
+ - Tracks: Only key node
114
+ - Example: Keys in `Discriminator.mapping` are `KeySource[str]`
115
+
116
+ **ValueSource[T]** - For dictionary values and array items
117
+ - Used for: values in OpenAPI fields, in `x-*` extensions, mapping dictionaries and array items
118
+ - Tracks: Only value node
119
+ - Example: Values in `Discriminator.mapping` are `ValueSource[str]`
120
+
121
+ ```python
122
+ from ruamel.yaml import YAML
123
+ from jentic.apitools.openapi.datamodels.low.v30.security_scheme import build as build_security_scheme
124
+ from jentic.apitools.openapi.datamodels.low.v30.discriminator import build as build_discriminator
125
+
126
+ # FieldSource: Fixed specification fields
127
+ yaml = YAML()
128
+ root = yaml.compose("type: http\nscheme: bearer\nbearerFormat: JWT")
129
+ security_scheme = build_security_scheme(root)
130
+
131
+ field = security_scheme.bearer_format # FieldSource[str]
132
+ print(field.value) # "JWT" - The actual value
133
+ print(field.key_node) # YAML node for "bearerFormat"
134
+ print(field.value_node) # YAML node for "JWT"
135
+
136
+ # KeySource/ValueSource: Dictionary fields (mapping, extensions)
137
+ root = yaml.compose("propertyName: petType\nmapping:\n dog: Dog\n cat: Cat")
138
+ discriminator = build_discriminator(root)
139
+
140
+ for key, value in discriminator.mapping.value.items():
141
+ print(key.value) # KeySource[str]: "dog" or "cat"
142
+ print(key.key_node) # YAML node for the key
143
+ print(value.value) # ValueSource[str]: "Dog" or "Cat"
144
+ print(value.value_node) # YAML node for the value
145
+ ```
146
+
147
+ ### Location Ranges
148
+
149
+ Access precise location ranges within the source document using start_mark and end_mark:
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
+ yaml_content = """
156
+ type: http
157
+ scheme: bearer
158
+ bearerFormat: JWT
159
+ description: Bearer token authentication
160
+ """
161
+
162
+ yaml = YAML()
163
+ root = yaml.compose(yaml_content)
164
+ security_scheme = build_security_scheme(root)
165
+
166
+ # Access location information for any field
167
+ field = security_scheme.bearer_format
168
+
169
+ # Key location (e.g., "bearerFormat")
170
+ print(f"Key start: line {field.key_node.start_mark.line}, col {field.key_node.start_mark.column}")
171
+ print(f"Key end: line {field.key_node.end_mark.line}, col {field.key_node.end_mark.column}")
172
+
173
+ # Value location (e.g., "JWT")
174
+ print(f"Value start: line {field.value_node.start_mark.line}, col {field.value_node.start_mark.column}")
175
+ print(f"Value end: line {field.value_node.end_mark.line}, col {field.value_node.end_mark.column}")
176
+
177
+ # Full field range (from key start to value end)
178
+ start = field.key_node.start_mark
179
+ end = field.value_node.end_mark
180
+ print(f"Field range: ({start.line}:{start.column}) to ({end.line}:{end.column})")
181
+ ```
182
+
183
+ ### Invalid Data Handling
184
+
185
+ Low-level models preserve invalid data:
186
+
187
+ ```python
188
+ from ruamel.yaml import YAML
189
+ from jentic.apitools.openapi.datamodels.low.v30.security_scheme import build as build_security_scheme
190
+
191
+ yaml = YAML()
192
+ root = yaml.compose("bearerFormat: 123") # Wrong type (should be string)
193
+
194
+ security_scheme = build_security_scheme(root)
195
+ print(security_scheme.bearer_format.value) # 123 (preserved as-is)
196
+ print(type(security_scheme.bearer_format.value)) # <class 'int'>
197
+ ```
198
+
199
+ ### Error Reporting
200
+
201
+ This architecture—where the low-level model preserves data without validation and validation tools consume
202
+ that data—allows the low-level model to remain simple while enabling sophisticated validation tools to provide
203
+ user-friendly error messages with exact source locations.
204
+
205
+ ## Testing
206
+
207
+ Run the test suite:
208
+
209
+ ```bash
210
+ uv run --package jentic-openapi-datamodels pytest packages/jentic-openapi-datamodels -v
211
+ ```
@@ -0,0 +1,23 @@
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=ACtSbRRzICi9cjLzldcwN4GDuieT_JvW81lPZ44mq6c,4954
4
+ jentic/apitools/openapi/datamodels/low/fields.py,sha256=g1Sta-eN4JDg_AnuJxe1MeWS3Ut81A5dw_ZIdyGrgbk,1448
5
+ jentic/apitools/openapi/datamodels/low/model_builder.py,sha256=NwfGtJBjYK9SJkYJBkfvgV2oxmVpZB5KIV5P-bt1B4s,4683
6
+ jentic/apitools/openapi/datamodels/low/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ jentic/apitools/openapi/datamodels/low/sources.py,sha256=H4I6LSn-Ry6cJageIyhCvE0H85O7Lh94gdCIm60wj0E,2924
8
+ jentic/apitools/openapi/datamodels/low/v30/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ jentic/apitools/openapi/datamodels/low/v30/discriminator.py,sha256=GVIyNbaoMVhkfavfFhKvlFHvwcIiSDPHKVAmgV9_7pQ,2537
10
+ jentic/apitools/openapi/datamodels/low/v30/external_documentation.py,sha256=5H2SeBkTH4MPakQPyy2NrSuPoh1n64OzeFVbfpyrh28,2448
11
+ jentic/apitools/openapi/datamodels/low/v30/oauth_flow.py,sha256=u6VnU4pMfu-Atg2Ooslp0ladGGhfw3VEjLHgMqZHRn8,2906
12
+ jentic/apitools/openapi/datamodels/low/v30/oauth_flows.py,sha256=gXgCmvYQ0d5v36VeGw2MqkIG20dbClfWIN7IDMq8V_c,4652
13
+ jentic/apitools/openapi/datamodels/low/v30/reference.py,sha256=zwz09f_sgzj5a1KV0V5IwhB313Se5JYX1pr5FaJaxZU,2134
14
+ jentic/apitools/openapi/datamodels/low/v30/schema.py,sha256=57LkSe1OuyQugtX5aM3UAy0qLgaOVyQzdbdYyQtDqBs,15188
15
+ jentic/apitools/openapi/datamodels/low/v30/security_requirement.py,sha256=beYB2TrfflvxnVO83b2iNzVKrInupJ_59zsvM8NYj-k,4032
16
+ jentic/apitools/openapi/datamodels/low/v30/security_scheme.py,sha256=tPOfcx0sh0w-gMMwtOxjybduRb7q-oI8PTq1SDS8ozg,4819
17
+ jentic/apitools/openapi/datamodels/low/v30/tag.py,sha256=FenkPaMCMAXZWJkA2heWewhxB2etKA-opuas494fUhA,3831
18
+ jentic/apitools/openapi/datamodels/low/v30/xml.py,sha256=1LjV-JsU5MqEFPV9e-zVKyJ9qt7QfwWjGN3oVLGxsnQ,2109
19
+ jentic_openapi_datamodels-1.0.0a13.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
20
+ jentic_openapi_datamodels-1.0.0a13.dist-info/licenses/NOTICE,sha256=pAOGW-rGw9KNc2cuuLWZkfx0GSTV4TicbgBKZSLPMIs,168
21
+ jentic_openapi_datamodels-1.0.0a13.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
22
+ jentic_openapi_datamodels-1.0.0a13.dist-info/METADATA,sha256=mV-JYCiurqc1c7SBNJ2OA4A85vhPI-NQ_iNFa3Uia1s,7439
23
+ jentic_openapi_datamodels-1.0.0a13.dist-info/RECORD,,