jentic-openapi-datamodels 1.0.0a15__tar.gz → 1.0.0a17__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 (27) hide show
  1. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/PKG-INFO +1 -1
  2. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/pyproject.toml +1 -1
  3. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/model_builder.py +17 -1
  4. jentic_openapi_datamodels-1.0.0a17/src/jentic/apitools/openapi/datamodels/low/v30/contact.py +67 -0
  5. jentic_openapi_datamodels-1.0.0a17/src/jentic/apitools/openapi/datamodels/low/v30/info.py +123 -0
  6. jentic_openapi_datamodels-1.0.0a17/src/jentic/apitools/openapi/datamodels/low/v30/license.py +65 -0
  7. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/v30/schema.py +22 -4
  8. jentic_openapi_datamodels-1.0.0a17/src/jentic/apitools/openapi/datamodels/low/v30/server_variable.py +76 -0
  9. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/LICENSE +0 -0
  10. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/NOTICE +0 -0
  11. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/README.md +0 -0
  12. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/__init__.py +0 -0
  13. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/context.py +0 -0
  14. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/extractors.py +0 -0
  15. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/fields.py +0 -0
  16. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/py.typed +0 -0
  17. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/sources.py +0 -0
  18. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/v30/__init__.py +0 -0
  19. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/v30/discriminator.py +0 -0
  20. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/v30/external_documentation.py +0 -0
  21. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/v30/oauth_flow.py +0 -0
  22. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/v30/oauth_flows.py +0 -0
  23. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/v30/reference.py +0 -0
  24. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/v30/security_requirement.py +0 -0
  25. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/v30/security_scheme.py +0 -0
  26. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/v30/tag.py +0 -0
  27. {jentic_openapi_datamodels-1.0.0a15 → jentic_openapi_datamodels-1.0.0a17}/src/jentic/apitools/openapi/datamodels/low/v30/xml.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jentic-openapi-datamodels
3
- Version: 1.0.0a15
3
+ Version: 1.0.0a17
4
4
  Summary: Jentic OpenAPI Data Models
5
5
  Author: Jentic
6
6
  Author-email: Jentic <hello@jentic.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "jentic-openapi-datamodels"
3
- version = "1.0.0-alpha.15"
3
+ version = "1.0.0-alpha.17"
4
4
  description = "Jentic OpenAPI Data Models"
5
5
  readme = "README.md"
6
6
  authors = [{ name = "Jentic", email = "hello@jentic.com" }]
@@ -62,7 +62,7 @@ def build_model(
62
62
  }
63
63
 
64
64
  # Extract field values in a single pass (non-recursive, single layer only)
65
- field_values: dict[str, Any] = {}
65
+ field_values: dict[str, FieldSource[Any]] = {}
66
66
  for key_node, value_node in root.value:
67
67
  key = context.yaml_constructor.construct_yaml_str(key_node)
68
68
 
@@ -98,6 +98,22 @@ def build_model(
98
98
  field_values[field_name] = FieldSource(
99
99
  value=value, key_node=key_node, value_node=value_node
100
100
  )
101
+ elif field_type_args & {FieldSource[list[ValueSource[str]]]}:
102
+ # Handle list with ValueSource wrapping for each item
103
+ if isinstance(value_node, yaml.SequenceNode):
104
+ value_list: list[ValueSource[str]] = []
105
+ for item_node in value_node.value:
106
+ item_value = context.yaml_constructor.construct_object(item_node, deep=True)
107
+ value_list.append(ValueSource(value=item_value, value_node=item_node))
108
+ field_values[field_name] = FieldSource(
109
+ value=value_list, key_node=key_node, value_node=value_node
110
+ )
111
+ else:
112
+ # Not a sequence - preserve as-is for validation
113
+ value = context.yaml_constructor.construct_object(value_node, deep=True)
114
+ field_values[field_name] = FieldSource(
115
+ value=value, key_node=key_node, value_node=value_node
116
+ )
101
117
 
102
118
  # Build and return the dataclass instance
103
119
  # Conditionally include extensions field if dataclass supports it
@@ -0,0 +1,67 @@
1
+ from dataclasses import dataclass, field
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
+ )
15
+
16
+
17
+ __all__ = ["Contact", "build"]
18
+
19
+
20
+ @dataclass(frozen=True, slots=True)
21
+ class Contact:
22
+ """
23
+ Contact Object representation for OpenAPI 3.0.
24
+
25
+ Contact information for the exposed API.
26
+
27
+ Attributes:
28
+ root_node: The top-level node representing the entire Contact object in the original source file
29
+ name: The identifying name of the contact person/organization.
30
+ url: The URL pointing to the contact information. Value MUST be in the format of a URL.
31
+ email: The email address of the contact person/organization. Value MUST be in the format of an email address.
32
+ extensions: Specification extensions (x-* fields)
33
+ """
34
+
35
+ root_node: yaml.Node
36
+ name: FieldSource[str] | None = fixed_field()
37
+ url: FieldSource[str] | None = fixed_field()
38
+ email: FieldSource[str] | None = fixed_field()
39
+ extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
40
+
41
+
42
+ def build(
43
+ root: yaml.Node, context: Context | None = None
44
+ ) -> Contact | ValueSource[YAMLInvalidValue]:
45
+ """
46
+ Build a Contact object from a YAML node.
47
+
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.
50
+
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.
54
+
55
+ Returns:
56
+ A Contact 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
+ Example:
61
+ from ruamel.yaml import YAML
62
+ yaml = YAML()
63
+ root = yaml.compose("name: API Support\\nemail: support@example.com")
64
+ contact = build(root)
65
+ assert contact.name.value == 'API Support'
66
+ """
67
+ return build_model(root, Contact, context=context)
@@ -0,0 +1,123 @@
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
+ )
15
+ from jentic.apitools.openapi.datamodels.low.v30.contact import Contact
16
+ from jentic.apitools.openapi.datamodels.low.v30.contact import build as build_contact
17
+ from jentic.apitools.openapi.datamodels.low.v30.license import License
18
+ from jentic.apitools.openapi.datamodels.low.v30.license import build as build_license
19
+
20
+
21
+ __all__ = ["Info", "build"]
22
+
23
+
24
+ @dataclass(frozen=True, slots=True)
25
+ class Info:
26
+ """
27
+ Info Object representation for OpenAPI 3.0.
28
+
29
+ Provides metadata about the API. The metadata MAY be used by the clients if needed,
30
+ and MAY be presented in editing or documentation generation tools for convenience.
31
+
32
+ Attributes:
33
+ root_node: The top-level node representing the entire Info object in the original source file
34
+ title: REQUIRED. The title of the API.
35
+ description: A description of the API. CommonMark syntax MAY be used for rich text representation.
36
+ termsOfService: A URL to the Terms of Service for the API. This MUST be in the form of a URL.
37
+ contact: The contact information for the exposed API.
38
+ license: The license information for the exposed API.
39
+ version: REQUIRED. The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API implementation version).
40
+ extensions: Specification extensions (x-* fields)
41
+ """
42
+
43
+ root_node: yaml.Node
44
+ title: FieldSource[str] | None = fixed_field()
45
+ description: FieldSource[str] | None = fixed_field()
46
+ termsOfService: FieldSource[str] | None = fixed_field()
47
+ contact: FieldSource[Contact] | None = fixed_field()
48
+ license: FieldSource[License] | None = fixed_field()
49
+ version: FieldSource[str] | None = fixed_field()
50
+ extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
51
+
52
+
53
+ def build(root: yaml.Node, context: Context | None = None) -> Info | ValueSource[YAMLInvalidValue]:
54
+ """
55
+ Build an Info object from a YAML node.
56
+
57
+ Preserves all source data as-is, regardless of type. This is a low-level/plumbing
58
+ model that provides complete source fidelity for inspection and validation.
59
+
60
+ Args:
61
+ root: The YAML node to parse (should be a MappingNode)
62
+ context: Optional parsing context. If None, a default context will be created.
63
+
64
+ Returns:
65
+ An Info object if the node is valid, or a ValueSource containing
66
+ the invalid data if the root is not a MappingNode (preserving the invalid data
67
+ and its source location for validation).
68
+
69
+ Example:
70
+ from ruamel.yaml import YAML
71
+ yaml = YAML()
72
+ root = yaml.compose('''
73
+ title: Sample Pet Store App
74
+ version: 1.0.1
75
+ description: This is a sample server for a pet store.
76
+ contact:
77
+ name: API Support
78
+ email: support@example.com
79
+ license:
80
+ name: Apache 2.0
81
+ url: https://www.apache.org/licenses/LICENSE-2.0.html
82
+ ''')
83
+ info = build(root)
84
+ assert info.title.value == 'Sample Pet Store App'
85
+ assert info.version.value == '1.0.1'
86
+ assert info.contact.value.name.value == 'API Support'
87
+ """
88
+ # Initialize context once at the beginning
89
+ if context is None:
90
+ context = Context()
91
+
92
+ if not isinstance(root, yaml.MappingNode):
93
+ # Preserve invalid root data instead of returning None
94
+ value = context.yaml_constructor.construct_object(root, deep=True)
95
+ return ValueSource(value=value, value_node=root)
96
+
97
+ # Use build_model to handle most fields
98
+ info = build_model(root, Info, context=context)
99
+
100
+ # Manually handle nested objects (contact, license)
101
+ for key_node, value_node in root.value:
102
+ key = context.yaml_constructor.construct_yaml_str(key_node)
103
+
104
+ if key == "contact":
105
+ # Handle nested Contact object - child builder handles invalid nodes
106
+ # FieldSource will auto-unwrap ValueSource if child returns it for invalid data
107
+ contact = FieldSource(
108
+ value=build_contact(value_node, context=context),
109
+ key_node=key_node,
110
+ value_node=value_node,
111
+ )
112
+ info = replace(info, contact=contact)
113
+ elif key == "license":
114
+ # Handle nested License object - child builder handles invalid nodes
115
+ # FieldSource will auto-unwrap ValueSource if child returns it for invalid data
116
+ license_obj = FieldSource(
117
+ value=build_license(value_node, context=context),
118
+ key_node=key_node,
119
+ value_node=value_node,
120
+ )
121
+ info = replace(info, license=license_obj)
122
+
123
+ return info
@@ -0,0 +1,65 @@
1
+ from dataclasses import dataclass, field
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
+ )
15
+
16
+
17
+ __all__ = ["License", "build"]
18
+
19
+
20
+ @dataclass(frozen=True, slots=True)
21
+ class License:
22
+ """
23
+ License Object representation for OpenAPI 3.0.
24
+
25
+ License information for the exposed API.
26
+
27
+ Attributes:
28
+ root_node: The top-level node representing the entire License object in the original source file
29
+ name: REQUIRED. The license name used for the API.
30
+ url: A URL to the license used for the API. Value MUST be in the format of a URL.
31
+ extensions: Specification extensions (x-* fields)
32
+ """
33
+
34
+ root_node: yaml.Node
35
+ name: FieldSource[str] | None = fixed_field()
36
+ url: FieldSource[str] | None = fixed_field()
37
+ extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
38
+
39
+
40
+ def build(
41
+ root: yaml.Node, context: Context | None = None
42
+ ) -> License | ValueSource[YAMLInvalidValue]:
43
+ """
44
+ Build a License object from a YAML node.
45
+
46
+ Preserves all source data as-is, regardless of type. This is a low-level/plumbing
47
+ model that provides complete source fidelity for inspection and validation.
48
+
49
+ Args:
50
+ root: The YAML node to parse (should be a MappingNode)
51
+ context: Optional parsing context. If None, a default context will be created.
52
+
53
+ Returns:
54
+ A License object if the node is valid, or a ValueSource containing
55
+ the invalid data if the root is not a MappingNode (preserving the invalid data
56
+ and its source location for validation).
57
+
58
+ Example:
59
+ from ruamel.yaml import YAML
60
+ yaml = YAML()
61
+ root = yaml.compose("name: MIT\\nurl: https://opensource.org/licenses/MIT")
62
+ license = build(root)
63
+ assert license.name.value == 'MIT'
64
+ """
65
+ return build_model(root, License, context=context)
@@ -113,8 +113,8 @@ class Schema:
113
113
  uniqueItems: FieldSource[bool] | None = fixed_field()
114
114
  maxProperties: FieldSource[int] | None = fixed_field()
115
115
  minProperties: FieldSource[int] | None = fixed_field()
116
- required: FieldSource[list[str]] | None = fixed_field()
117
- enum: FieldSource[list[YAMLValue]] | None = fixed_field()
116
+ required: FieldSource[list[ValueSource[str]]] | None = fixed_field()
117
+ enum: FieldSource[list[ValueSource[YAMLValue]]] | None = fixed_field()
118
118
 
119
119
  # JSON Schema Type and Structure (nested schemas)
120
120
  type: FieldSource[str] | None = fixed_field()
@@ -215,14 +215,32 @@ def build(
215
215
  FieldSource[int],
216
216
  FieldSource[int | float],
217
217
  FieldSource[YAMLValue],
218
- FieldSource[list[str]],
219
- FieldSource[list[YAMLValue]],
220
218
  }:
221
219
  value = context.yaml_constructor.construct_object(value_node, deep=True)
222
220
  field_values[field_name] = FieldSource(
223
221
  value=value, key_node=key_node, value_node=value_node
224
222
  )
225
223
 
224
+ # Handle list with ValueSource wrapping for each item (e.g., required, enum fields)
225
+ elif field_type_args & {
226
+ FieldSource[list[ValueSource[str]]],
227
+ FieldSource[list[ValueSource[YAMLValue]]],
228
+ }:
229
+ if isinstance(value_node, yaml.SequenceNode):
230
+ value_list: list[ValueSource[Any]] = []
231
+ for item_node in value_node.value:
232
+ item_value = context.yaml_constructor.construct_object(item_node, deep=True)
233
+ value_list.append(ValueSource(value=item_value, value_node=item_node))
234
+ field_values[field_name] = FieldSource(
235
+ value=value_list, key_node=key_node, value_node=value_node
236
+ )
237
+ else:
238
+ # Not a sequence - preserve as-is for validation
239
+ value = context.yaml_constructor.construct_object(value_node, deep=True)
240
+ field_values[field_name] = FieldSource(
241
+ value=value, key_node=key_node, value_node=value_node
242
+ )
243
+
226
244
  # Recursive schema list fields (allOf, oneOf, anyOf)
227
245
  elif key in ("allOf", "oneOf", "anyOf"):
228
246
  if isinstance(value_node, yaml.SequenceNode):
@@ -0,0 +1,76 @@
1
+ from dataclasses import dataclass, field
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
+ )
15
+
16
+
17
+ __all__ = ["ServerVariable", "build"]
18
+
19
+
20
+ @dataclass(frozen=True, slots=True)
21
+ class ServerVariable:
22
+ """
23
+ Server Variable Object representation for OpenAPI 3.0.
24
+
25
+ An object representing a Server Variable for server URL template substitution.
26
+
27
+ Attributes:
28
+ root_node: The top-level node representing the entire ServerVariable object in the original source file
29
+ enum: An enumeration of string values to be used if the substitution options are from a limited set. The array SHOULD NOT be empty.
30
+ default: REQUIRED. The default value to use for substitution, which SHALL be sent if an alternate value is not supplied. If the enum is defined, the value SHOULD exist in the enum's values.
31
+ description: An optional description for the server variable. CommonMark syntax MAY be used for rich text representation.
32
+ extensions: Specification extensions (x-* fields)
33
+ """
34
+
35
+ root_node: yaml.Node
36
+ enum: FieldSource[list[ValueSource[str]]] | None = fixed_field()
37
+ default: FieldSource[str] | None = fixed_field()
38
+ description: FieldSource[str] | None = fixed_field()
39
+ extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
40
+
41
+
42
+ def build(
43
+ root: yaml.Node, context: Context | None = None
44
+ ) -> ServerVariable | ValueSource[YAMLInvalidValue]:
45
+ """
46
+ Build a ServerVariable object from a YAML node.
47
+
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.
50
+
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.
54
+
55
+ Returns:
56
+ A ServerVariable 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
+ Example:
61
+ from ruamel.yaml import YAML
62
+ yaml = YAML()
63
+ root = yaml.compose('''
64
+ default: production
65
+ enum:
66
+ - production
67
+ - staging
68
+ - development
69
+ description: The deployment environment
70
+ ''')
71
+ server_variable = build(root)
72
+ assert server_variable.default.value == 'production'
73
+ assert len(server_variable.enum.value) == 3
74
+ assert server_variable.enum.value[0].value == 'production'
75
+ """
76
+ return build_model(root, ServerVariable, context=context)