openapi-python-client 0.29.0__py3-none-any.whl → 0.29.1__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.
- openapi_python_client/__init__.py +26 -17
- openapi_python_client/cli.py +1 -2
- openapi_python_client/config.py +3 -1
- openapi_python_client/parser/bodies.py +8 -7
- openapi_python_client/parser/openapi.py +58 -48
- openapi_python_client/parser/properties/__init__.py +34 -33
- openapi_python_client/parser/properties/any.py +11 -10
- openapi_python_client/parser/properties/boolean.py +13 -13
- openapi_python_client/parser/properties/const.py +11 -10
- openapi_python_client/parser/properties/date.py +12 -11
- openapi_python_client/parser/properties/datetime.py +12 -11
- openapi_python_client/parser/properties/enum_property.py +23 -20
- openapi_python_client/parser/properties/file.py +11 -10
- openapi_python_client/parser/properties/float.py +13 -13
- openapi_python_client/parser/properties/int.py +11 -11
- openapi_python_client/parser/properties/list_property.py +22 -22
- openapi_python_client/parser/properties/literal_enum_property.py +20 -20
- openapi_python_client/parser/properties/merge_properties.py +9 -6
- openapi_python_client/parser/properties/model_property.py +34 -33
- openapi_python_client/parser/properties/none.py +11 -11
- openapi_python_client/parser/properties/protocol.py +42 -32
- openapi_python_client/parser/properties/schemas.py +30 -23
- openapi_python_client/parser/properties/string.py +10 -11
- openapi_python_client/parser/properties/union.py +24 -22
- openapi_python_client/parser/properties/uuid.py +11 -11
- openapi_python_client/parser/responses.py +30 -23
- openapi_python_client/schema/__init__.py +2 -1
- openapi_python_client/schema/openapi_schema_pydantic/callback.py +3 -1
- openapi_python_client/schema/openapi_schema_pydantic/components.py +10 -9
- openapi_python_client/schema/openapi_schema_pydantic/contact.py +5 -3
- openapi_python_client/schema/openapi_schema_pydantic/discriminator.py +4 -2
- openapi_python_client/schema/openapi_schema_pydantic/encoding.py +4 -3
- openapi_python_client/schema/openapi_schema_pydantic/example.py +5 -3
- openapi_python_client/schema/openapi_schema_pydantic/external_documentation.py +4 -2
- openapi_python_client/schema/openapi_schema_pydantic/header.py +20 -6
- openapi_python_client/schema/openapi_schema_pydantic/info.py +5 -4
- openapi_python_client/schema/openapi_schema_pydantic/license.py +4 -2
- openapi_python_client/schema/openapi_schema_pydantic/link.py +5 -4
- openapi_python_client/schema/openapi_schema_pydantic/media_type.py +3 -2
- openapi_python_client/schema/openapi_schema_pydantic/oauth_flow.py +6 -4
- openapi_python_client/schema/openapi_schema_pydantic/open_api.py +5 -3
- openapi_python_client/schema/openapi_schema_pydantic/operation.py +6 -5
- openapi_python_client/schema/openapi_schema_pydantic/parameter.py +6 -5
- openapi_python_client/schema/openapi_schema_pydantic/path_item.py +5 -3
- openapi_python_client/schema/openapi_schema_pydantic/paths.py +2 -1
- openapi_python_client/schema/openapi_schema_pydantic/reference.py +3 -1
- openapi_python_client/schema/openapi_schema_pydantic/request_body.py +3 -2
- openapi_python_client/schema/openapi_schema_pydantic/response.py +5 -4
- openapi_python_client/schema/openapi_schema_pydantic/responses.py +2 -1
- openapi_python_client/schema/openapi_schema_pydantic/schema.py +7 -6
- openapi_python_client/schema/openapi_schema_pydantic/security_requirement.py +3 -1
- openapi_python_client/schema/openapi_schema_pydantic/security_scheme.py +8 -7
- openapi_python_client/schema/openapi_schema_pydantic/server.py +4 -3
- openapi_python_client/schema/openapi_schema_pydantic/server_variable.py +5 -3
- openapi_python_client/schema/openapi_schema_pydantic/tag.py +3 -2
- openapi_python_client/schema/openapi_schema_pydantic/xml.py +5 -3
- openapi_python_client/schema/ref.py +52 -0
- openapi_python_client/schema/untrusted_string.py +56 -0
- openapi_python_client/strings.py +245 -0
- openapi_python_client/templates/README.md.jinja +3 -3
- openapi_python_client/templates/client.py.jinja +5 -5
- openapi_python_client/templates/endpoint_macros.py.jinja +18 -18
- openapi_python_client/templates/endpoint_module.py.jinja +13 -13
- openapi_python_client/templates/helpers.jinja +3 -7
- openapi_python_client/templates/literal_enum.py.jinja +1 -1
- openapi_python_client/templates/model.py.jinja +18 -18
- openapi_python_client/templates/package_init.py.jinja +2 -2
- openapi_python_client/templates/property_templates/const_property.py.jinja +3 -3
- openapi_python_client/templates/property_templates/date_property.py.jinja +1 -1
- openapi_python_client/templates/property_templates/datetime_property.py.jinja +1 -1
- openapi_python_client/templates/property_templates/enum_property.py.jinja +1 -1
- openapi_python_client/templates/property_templates/file_property.py.jinja +1 -1
- openapi_python_client/templates/property_templates/list_property.py.jinja +3 -3
- openapi_python_client/templates/property_templates/literal_enum_property.py.jinja +2 -2
- openapi_python_client/templates/property_templates/model_property.py.jinja +1 -1
- openapi_python_client/templates/property_templates/property_macros.py.jinja +1 -1
- openapi_python_client/templates/property_templates/union_property.py.jinja +9 -9
- openapi_python_client/templates/property_templates/uuid_property.py.jinja +1 -1
- openapi_python_client/templates/pyproject_pdm.toml.jinja +2 -2
- openapi_python_client/templates/pyproject_poetry.toml.jinja +2 -2
- openapi_python_client/templates/pyproject_uv.toml.jinja +3 -3
- openapi_python_client/templates/setup.py.jinja +2 -2
- openapi_python_client/templates/str_enum.py.jinja +2 -2
- {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.29.1.dist-info}/METADATA +4 -5
- openapi_python_client-0.29.1.dist-info/RECORD +115 -0
- {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.29.1.dist-info}/WHEEL +1 -1
- openapi_python_client/utils.py +0 -122
- openapi_python_client-0.29.0.dist-info/RECORD +0 -113
- {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.29.1.dist-info}/entry_points.txt +0 -0
- {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.29.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,24 +2,24 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, ClassVar
|
|
4
4
|
|
|
5
|
-
from attr import define
|
|
5
|
+
from attr import define, field
|
|
6
6
|
|
|
7
7
|
from ... import schema as oai
|
|
8
|
-
from ...
|
|
8
|
+
from ...strings import PythonCode, PythonIdentifier
|
|
9
9
|
from ..errors import PropertyError
|
|
10
|
-
from .protocol import PropertyProtocol, Value
|
|
10
|
+
from .protocol import PropertyProtocol, Value, convert_example
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
@define
|
|
14
14
|
class NoneProperty(PropertyProtocol):
|
|
15
15
|
"""A property that can only be None"""
|
|
16
16
|
|
|
17
|
-
name:
|
|
17
|
+
name: oai.UntrustedString
|
|
18
18
|
required: bool
|
|
19
19
|
default: Value | None
|
|
20
20
|
python_name: PythonIdentifier
|
|
21
|
-
description:
|
|
22
|
-
example:
|
|
21
|
+
description: oai.UntrustedString | None
|
|
22
|
+
example: oai.UntrustedString | None = field(converter=convert_example)
|
|
23
23
|
|
|
24
24
|
_allowed_locations: ClassVar[set[oai.ParameterLocation]] = {
|
|
25
25
|
oai.ParameterLocation.QUERY,
|
|
@@ -32,12 +32,12 @@ class NoneProperty(PropertyProtocol):
|
|
|
32
32
|
@classmethod
|
|
33
33
|
def build(
|
|
34
34
|
cls,
|
|
35
|
-
name:
|
|
35
|
+
name: oai.UntrustedString,
|
|
36
36
|
required: bool,
|
|
37
37
|
default: Any,
|
|
38
38
|
python_name: PythonIdentifier,
|
|
39
|
-
description:
|
|
40
|
-
example:
|
|
39
|
+
description: oai.UntrustedString | None,
|
|
40
|
+
example: Any,
|
|
41
41
|
) -> NoneProperty | PropertyError:
|
|
42
42
|
checked_default = cls.convert_value(default)
|
|
43
43
|
if isinstance(checked_default, PropertyError):
|
|
@@ -52,10 +52,10 @@ class NoneProperty(PropertyProtocol):
|
|
|
52
52
|
)
|
|
53
53
|
|
|
54
54
|
@classmethod
|
|
55
|
-
def convert_value(cls, value: Any) -> Value |
|
|
55
|
+
def convert_value(cls, value: Any) -> Value | PropertyError | None:
|
|
56
56
|
if value is None or isinstance(value, Value):
|
|
57
57
|
return value
|
|
58
58
|
if isinstance(value, str):
|
|
59
59
|
if value == "None":
|
|
60
|
-
return Value(python_code=value, raw_value=value)
|
|
60
|
+
return Value(python_code=PythonCode(value), raw_value=value)
|
|
61
61
|
return PropertyError(f"Value {value} is not valid, only None is allowed")
|
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
__all__ = ["PropertyProtocol", "Value"]
|
|
3
|
+
__all__ = ["PropertyProtocol", "Value", "convert_example"]
|
|
4
4
|
|
|
5
5
|
from abc import abstractmethod
|
|
6
6
|
from dataclasses import dataclass
|
|
7
|
-
from typing import
|
|
7
|
+
from typing import Any, ClassVar, Protocol, TypeVar
|
|
8
8
|
|
|
9
9
|
from ... import Config
|
|
10
10
|
from ... import schema as oai
|
|
11
|
-
from ...
|
|
11
|
+
from ...strings import PythonCode, PythonIdentifier, safe_for_docstring
|
|
12
12
|
from ..errors import ParseError, PropertyError
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
|
|
15
|
+
def convert_example(example: Any) -> oai.UntrustedString | None:
|
|
16
|
+
"""
|
|
17
|
+
Convert a raw OpenAPI `example` (which can be anything) into an `UntrustedString` (or None).
|
|
18
|
+
|
|
19
|
+
Examples only ever appear in docstrings, so non-string examples are stringified. Wrapping ensures a
|
|
20
|
+
template which uses an example without escaping renders a safe-but-broken value instead of an exploit.
|
|
21
|
+
"""
|
|
22
|
+
if example is None or isinstance(example, oai.UntrustedString):
|
|
23
|
+
return example
|
|
24
|
+
if isinstance(example, str):
|
|
25
|
+
return oai.UntrustedString(example)
|
|
26
|
+
return oai.UntrustedString(str(example))
|
|
18
27
|
|
|
19
28
|
|
|
20
29
|
@dataclass
|
|
@@ -24,7 +33,7 @@ class Value:
|
|
|
24
33
|
(with string escaping, for example). We still keep the `raw_value` around for merging `allOf`.
|
|
25
34
|
"""
|
|
26
35
|
|
|
27
|
-
python_code:
|
|
36
|
+
python_code: PythonCode
|
|
28
37
|
raw_value: Any
|
|
29
38
|
|
|
30
39
|
|
|
@@ -45,7 +54,7 @@ class PropertyProtocol(Protocol):
|
|
|
45
54
|
ValidationError: Raised when the default value fails to be converted to the expected type
|
|
46
55
|
"""
|
|
47
56
|
|
|
48
|
-
name:
|
|
57
|
+
name: oai.UntrustedString
|
|
49
58
|
required: bool
|
|
50
59
|
_type_string: ClassVar[str] = ""
|
|
51
60
|
_json_type_string: ClassVar[str] = "" # Type of the property after JSON serialization
|
|
@@ -56,26 +65,26 @@ class PropertyProtocol(Protocol):
|
|
|
56
65
|
}
|
|
57
66
|
default: Value | None
|
|
58
67
|
python_name: PythonIdentifier
|
|
59
|
-
description:
|
|
60
|
-
example:
|
|
68
|
+
description: oai.UntrustedString | None
|
|
69
|
+
example: oai.UntrustedString | None
|
|
61
70
|
|
|
62
71
|
template: ClassVar[str] = "any_property.py.jinja"
|
|
63
72
|
json_is_dict: ClassVar[bool] = False
|
|
64
73
|
|
|
65
74
|
@abstractmethod
|
|
66
|
-
def convert_value(self, value: Any) -> Value |
|
|
75
|
+
def convert_value(self, value: Any) -> Value | PropertyError | None:
|
|
67
76
|
"""Convert a string value to a Value object"""
|
|
68
77
|
raise NotImplementedError() # pragma: no cover
|
|
69
78
|
|
|
70
79
|
def validate_location(self, location: oai.ParameterLocation) -> ParseError | None:
|
|
71
80
|
"""Returns an error if this type of property is not allowed in the given location"""
|
|
72
81
|
if location not in self._allowed_locations:
|
|
73
|
-
return ParseError(detail=f"{self.get_type_string()} is not allowed in {location}")
|
|
82
|
+
return ParseError(detail=f"{self.get_type_string().as_unembedded_code()} is not allowed in {location}")
|
|
74
83
|
if location == oai.ParameterLocation.PATH and not self.required:
|
|
75
84
|
return ParseError(detail="Path parameter must be required")
|
|
76
85
|
return None
|
|
77
86
|
|
|
78
|
-
def set_python_name(self, new_name:
|
|
87
|
+
def set_python_name(self, new_name: oai.UntrustedString, config: Config, skip_snake_case: bool = False) -> None:
|
|
79
88
|
"""Mutates this Property to set a new python_name.
|
|
80
89
|
|
|
81
90
|
Required to mutate due to how Properties are stored and the difficulty of updating them in-dict.
|
|
@@ -88,21 +97,21 @@ class PropertyProtocol(Protocol):
|
|
|
88
97
|
PythonIdentifier(value=new_name, prefix=config.field_prefix, skip_snake_case=skip_snake_case),
|
|
89
98
|
)
|
|
90
99
|
|
|
91
|
-
def get_base_type_string(self) ->
|
|
92
|
-
"""Get the
|
|
93
|
-
return self._type_string
|
|
100
|
+
def get_base_type_string(self) -> PythonCode:
|
|
101
|
+
"""Get the code describing the Python type of this property. Base types no require quoting."""
|
|
102
|
+
return PythonCode(self._type_string)
|
|
94
103
|
|
|
95
|
-
def get_base_json_type_string(self) ->
|
|
96
|
-
"""Get the
|
|
97
|
-
return self._json_type_string
|
|
104
|
+
def get_base_json_type_string(self) -> PythonCode:
|
|
105
|
+
"""Get the code describing the JSON type of this property. Base types no require quoting."""
|
|
106
|
+
return PythonCode(self._json_type_string)
|
|
98
107
|
|
|
99
108
|
def get_type_string(
|
|
100
109
|
self,
|
|
101
110
|
no_optional: bool = False,
|
|
102
111
|
json: bool = False,
|
|
103
|
-
) ->
|
|
112
|
+
) -> PythonCode:
|
|
104
113
|
"""
|
|
105
|
-
Get a
|
|
114
|
+
Get a Python code representation of type that should be used when declaring this property
|
|
106
115
|
|
|
107
116
|
Args:
|
|
108
117
|
no_optional: Do not include Optional or Unset even if the value is optional (needed for isinstance checks)
|
|
@@ -115,10 +124,10 @@ class PropertyProtocol(Protocol):
|
|
|
115
124
|
|
|
116
125
|
if no_optional or self.required:
|
|
117
126
|
return type_string
|
|
118
|
-
return f"{type_string} | Unset"
|
|
127
|
+
return PythonCode(f"{type_string.as_unembedded_code()} | Unset")
|
|
119
128
|
|
|
120
|
-
def get_instance_type_string(self) ->
|
|
121
|
-
"""Get a
|
|
129
|
+
def get_instance_type_string(self) -> PythonCode:
|
|
130
|
+
"""Get a Python code representation of runtime type that should be used for `isinstance` checks"""
|
|
122
131
|
return self.get_type_string(no_optional=True)
|
|
123
132
|
|
|
124
133
|
# noinspection PyUnusedLocal
|
|
@@ -144,25 +153,26 @@ class PropertyProtocol(Protocol):
|
|
|
144
153
|
"""
|
|
145
154
|
return set()
|
|
146
155
|
|
|
147
|
-
def to_string(self) ->
|
|
156
|
+
def to_string(self) -> PythonCode:
|
|
148
157
|
"""How this should be declared in a dataclass"""
|
|
149
158
|
default: str | None
|
|
150
159
|
if self.default is not None:
|
|
151
|
-
default = self.default.python_code
|
|
160
|
+
default = self.default.python_code.as_unembedded_code()
|
|
152
161
|
elif not self.required:
|
|
153
162
|
default = "UNSET"
|
|
154
163
|
else:
|
|
155
164
|
default = None
|
|
156
165
|
|
|
166
|
+
declaration = f"{self.python_name}: {self.get_type_string().as_unembedded_code()}"
|
|
157
167
|
if default is not None:
|
|
158
|
-
|
|
159
|
-
return
|
|
168
|
+
declaration += f" = {default}"
|
|
169
|
+
return PythonCode(declaration)
|
|
160
170
|
|
|
161
171
|
def to_docstring(self) -> str:
|
|
162
172
|
"""Returns property docstring"""
|
|
163
|
-
doc = f"{self.python_name} ({self.get_type_string()}): {self.description or ''}"
|
|
173
|
+
doc = f"{self.python_name} ({safe_for_docstring(self.get_type_string())}): {safe_for_docstring(self.description or '')}"
|
|
164
174
|
if self.default:
|
|
165
|
-
doc += f" Default: {self.default.python_code}."
|
|
175
|
+
doc += f" Default: {safe_for_docstring(self.default.python_code)}."
|
|
166
176
|
if self.example:
|
|
167
|
-
doc += f" Example: {self.example}."
|
|
177
|
+
doc += f" Example: {safe_for_docstring(self.example)}."
|
|
168
178
|
return doc
|
|
@@ -10,7 +10,7 @@ __all__ = [
|
|
|
10
10
|
"update_schemas_with_data",
|
|
11
11
|
]
|
|
12
12
|
|
|
13
|
-
from typing import TYPE_CHECKING, NewType
|
|
13
|
+
from typing import TYPE_CHECKING, NewType
|
|
14
14
|
from urllib.parse import urlparse
|
|
15
15
|
|
|
16
16
|
from attrs import define, evolve, field
|
|
@@ -18,7 +18,9 @@ from attrs import define, evolve, field
|
|
|
18
18
|
from ... import Config
|
|
19
19
|
from ... import schema as oai
|
|
20
20
|
from ...schema.openapi_schema_pydantic import Parameter
|
|
21
|
-
from ...
|
|
21
|
+
from ...schema.ref import Ref
|
|
22
|
+
from ...schema.untrusted_string import UntrustedString
|
|
23
|
+
from ...strings import ClassName, PythonIdentifier
|
|
22
24
|
from ..errors import ParameterError, ParseError, PropertyError
|
|
23
25
|
|
|
24
26
|
if TYPE_CHECKING: # pragma: no cover
|
|
@@ -29,10 +31,10 @@ else:
|
|
|
29
31
|
Property = "Property"
|
|
30
32
|
|
|
31
33
|
|
|
32
|
-
ReferencePath = NewType("ReferencePath",
|
|
34
|
+
ReferencePath = NewType("ReferencePath", UntrustedString)
|
|
33
35
|
|
|
34
36
|
|
|
35
|
-
def parse_reference_path(ref_path_raw:
|
|
37
|
+
def parse_reference_path(ref_path_raw: Ref) -> ReferencePath | ParseError:
|
|
36
38
|
"""
|
|
37
39
|
Takes a raw string provided in a `$ref` and turns it into a validated `_ReferencePath` or a `ParseError` if
|
|
38
40
|
validation fails.
|
|
@@ -40,17 +42,17 @@ def parse_reference_path(ref_path_raw: str) -> ReferencePath | ParseError:
|
|
|
40
42
|
See Also:
|
|
41
43
|
- https://swagger.io/docs/specification/using-ref/
|
|
42
44
|
"""
|
|
43
|
-
parsed = urlparse(ref_path_raw)
|
|
45
|
+
parsed = urlparse(ref_path_raw.get_untrusted_value())
|
|
44
46
|
if parsed.scheme or parsed.path:
|
|
45
|
-
return ParseError(detail=f"Remote references such as {
|
|
46
|
-
return
|
|
47
|
+
return ParseError(detail=f"Remote references such as {parsed} are not supported yet.")
|
|
48
|
+
return ReferencePath(UntrustedString(parsed.fragment))
|
|
47
49
|
|
|
48
50
|
|
|
49
|
-
def get_reference_simple_name(ref_path:
|
|
51
|
+
def get_reference_simple_name(ref_path: Ref | UntrustedString) -> UntrustedString:
|
|
50
52
|
"""
|
|
51
53
|
Takes a path like `/components/schemas/NameOfThing` and returns a string like `NameOfThing`.
|
|
52
54
|
"""
|
|
53
|
-
return ref_path.rsplit("/", maxsplit=1)[-1]
|
|
55
|
+
return UntrustedString(ref_path.get_untrusted_value().rsplit("/", maxsplit=1)[-1])
|
|
54
56
|
|
|
55
57
|
|
|
56
58
|
@define
|
|
@@ -61,10 +63,10 @@ class Class:
|
|
|
61
63
|
module_name: PythonIdentifier
|
|
62
64
|
|
|
63
65
|
@staticmethod
|
|
64
|
-
def from_string(*, string: str, config: Config) -> "Class":
|
|
66
|
+
def from_string(*, string: str | UntrustedString, config: Config) -> "Class":
|
|
65
67
|
"""Get a Class from an arbitrary string"""
|
|
66
|
-
|
|
67
|
-
class_name = ClassName(
|
|
68
|
+
untrusted_class_name = get_reference_simple_name(Ref(string)) # Get rid of ref path stuff
|
|
69
|
+
class_name = ClassName(untrusted_class_name, config.field_prefix)
|
|
68
70
|
override = config.class_overrides.get(class_name)
|
|
69
71
|
|
|
70
72
|
if override is not None and override.class_name is not None:
|
|
@@ -134,12 +136,15 @@ def update_schemas_with_data(
|
|
|
134
136
|
)
|
|
135
137
|
|
|
136
138
|
if isinstance(prop, PropertyError):
|
|
137
|
-
|
|
138
|
-
prop.header = f"Unable to parse schema {ref_path}"
|
|
139
|
-
if isinstance(prop.data, oai.Reference) and prop.data.ref.endswith(
|
|
140
|
-
|
|
139
|
+
detail = f"{prop.header}: {prop.detail}" if prop.detail is not None else prop.header
|
|
140
|
+
prop.header = f"Unable to parse schema {ref_path.get_untrusted_value()}"
|
|
141
|
+
if isinstance(prop.data, oai.Reference) and prop.data.ref.endswith(
|
|
142
|
+
ref_path.get_untrusted_value()
|
|
143
|
+
): # pragma: nocover
|
|
144
|
+
detail += (
|
|
141
145
|
"\n\nRecursive and circular references are not supported directly in an array schema's 'items' section"
|
|
142
146
|
)
|
|
147
|
+
prop.detail = detail
|
|
143
148
|
return prop
|
|
144
149
|
|
|
145
150
|
schemas = evolve(schemas, classes_by_reference={ref_path: prop, **schemas.classes_by_reference})
|
|
@@ -157,7 +162,7 @@ class Parameters:
|
|
|
157
162
|
|
|
158
163
|
def parameter_from_data(
|
|
159
164
|
*,
|
|
160
|
-
name:
|
|
165
|
+
name: UntrustedString,
|
|
161
166
|
data: oai.Reference | oai.Parameter,
|
|
162
167
|
parameters: Parameters,
|
|
163
168
|
config: Config,
|
|
@@ -171,10 +176,10 @@ def parameter_from_data(
|
|
|
171
176
|
return ParameterError("Parameter has no schema"), parameters
|
|
172
177
|
|
|
173
178
|
new_param = Parameter(
|
|
174
|
-
name=name,
|
|
179
|
+
name=name.get_untrusted_value(),
|
|
175
180
|
required=data.required,
|
|
176
181
|
explode=data.explode,
|
|
177
|
-
style=data.style,
|
|
182
|
+
style=data.style.get_untrusted_value() if data.style is not None else None,
|
|
178
183
|
param_schema=data.param_schema,
|
|
179
184
|
param_in=data.param_in,
|
|
180
185
|
)
|
|
@@ -203,10 +208,12 @@ def update_parameters_with_data(
|
|
|
203
208
|
"""
|
|
204
209
|
param, parameters = parameter_from_data(data=data, name=data.name, parameters=parameters, config=config)
|
|
205
210
|
|
|
206
|
-
if isinstance(param, ParameterError):
|
|
211
|
+
if isinstance(param, ParameterError): # pragma: no cover
|
|
207
212
|
param.detail = f"{param.header}: {param.detail}"
|
|
208
|
-
param.header = f"Unable to parse parameter {ref_path}"
|
|
209
|
-
if isinstance(param.data, oai.Reference) and param.data.ref.endswith(
|
|
213
|
+
param.header = f"Unable to parse parameter {ref_path.get_untrusted_value()}"
|
|
214
|
+
if isinstance(param.data, oai.Reference) and param.data.ref.endswith(
|
|
215
|
+
ref_path.get_untrusted_value()
|
|
216
|
+
): # pragma: nocover
|
|
210
217
|
param.detail += (
|
|
211
218
|
"\n\nRecursive and circular references are not supported. "
|
|
212
219
|
"See https://github.com/openapi-generators/openapi-python-client/issues/466"
|
|
@@ -245,5 +252,5 @@ def parameter_from_reference(
|
|
|
245
252
|
|
|
246
253
|
_resolved_parameter_class = parameters.classes_by_reference.get(ref_path, None)
|
|
247
254
|
if _resolved_parameter_class is None:
|
|
248
|
-
return ParameterError(detail=f"Reference `{ref_path}` not found.")
|
|
255
|
+
return ParameterError(detail=f"Reference `{ref_path.get_untrusted_value()}` not found.")
|
|
249
256
|
return _resolved_parameter_class
|
|
@@ -2,25 +2,24 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, ClassVar, overload
|
|
4
4
|
|
|
5
|
-
from attr import define
|
|
5
|
+
from attr import define, field
|
|
6
6
|
|
|
7
7
|
from ... import schema as oai
|
|
8
|
-
from ... import
|
|
9
|
-
from ...utils import PythonIdentifier
|
|
8
|
+
from ...strings import PythonCode, PythonIdentifier, remove_string_escapes
|
|
10
9
|
from ..errors import PropertyError
|
|
11
|
-
from .protocol import PropertyProtocol, Value
|
|
10
|
+
from .protocol import PropertyProtocol, Value, convert_example
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
@define
|
|
15
14
|
class StringProperty(PropertyProtocol):
|
|
16
15
|
"""A property of type str"""
|
|
17
16
|
|
|
18
|
-
name:
|
|
17
|
+
name: oai.UntrustedString
|
|
19
18
|
required: bool
|
|
20
19
|
default: Value | None
|
|
21
20
|
python_name: PythonIdentifier
|
|
22
|
-
description:
|
|
23
|
-
example:
|
|
21
|
+
description: oai.UntrustedString | None
|
|
22
|
+
example: oai.UntrustedString | None = field(converter=convert_example)
|
|
24
23
|
_type_string: ClassVar[str] = "str"
|
|
25
24
|
_json_type_string: ClassVar[str] = "str"
|
|
26
25
|
_allowed_locations: ClassVar[set[oai.ParameterLocation]] = {
|
|
@@ -33,12 +32,12 @@ class StringProperty(PropertyProtocol):
|
|
|
33
32
|
@classmethod
|
|
34
33
|
def build(
|
|
35
34
|
cls,
|
|
36
|
-
name:
|
|
35
|
+
name: oai.UntrustedString,
|
|
37
36
|
required: bool,
|
|
38
37
|
default: Any,
|
|
39
38
|
python_name: PythonIdentifier,
|
|
40
|
-
description:
|
|
41
|
-
example:
|
|
39
|
+
description: oai.UntrustedString | None,
|
|
40
|
+
example: Any,
|
|
42
41
|
) -> StringProperty | PropertyError:
|
|
43
42
|
checked_default = cls.convert_value(default)
|
|
44
43
|
return cls(
|
|
@@ -65,4 +64,4 @@ class StringProperty(PropertyProtocol):
|
|
|
65
64
|
return value
|
|
66
65
|
if not isinstance(value, str):
|
|
67
66
|
value = str(value)
|
|
68
|
-
return Value(python_code=repr(
|
|
67
|
+
return Value(python_code=PythonCode(repr(remove_string_escapes(value))), raw_value=value)
|
|
@@ -4,13 +4,13 @@ from collections.abc import Iterator
|
|
|
4
4
|
from itertools import chain
|
|
5
5
|
from typing import Any, ClassVar, cast
|
|
6
6
|
|
|
7
|
-
from attr import define, evolve
|
|
7
|
+
from attr import define, evolve, field
|
|
8
8
|
|
|
9
|
-
from ... import Config
|
|
9
|
+
from ... import Config, strings
|
|
10
10
|
from ... import schema as oai
|
|
11
|
-
from ...
|
|
11
|
+
from ...strings import PythonIdentifier
|
|
12
12
|
from ..errors import ParseError, PropertyError
|
|
13
|
-
from .protocol import PropertyProtocol, Value
|
|
13
|
+
from .protocol import PropertyProtocol, Value, convert_example
|
|
14
14
|
from .schemas import Schemas
|
|
15
15
|
|
|
16
16
|
|
|
@@ -18,12 +18,12 @@ from .schemas import Schemas
|
|
|
18
18
|
class UnionProperty(PropertyProtocol):
|
|
19
19
|
"""A property representing a Union (anyOf) of other properties"""
|
|
20
20
|
|
|
21
|
-
name:
|
|
21
|
+
name: oai.UntrustedString
|
|
22
22
|
required: bool
|
|
23
23
|
default: Value | None
|
|
24
24
|
python_name: PythonIdentifier
|
|
25
|
-
description:
|
|
26
|
-
example:
|
|
25
|
+
description: oai.UntrustedString | None
|
|
26
|
+
example: oai.UntrustedString | None = field(converter=convert_example)
|
|
27
27
|
inner_properties: list[PropertyProtocol]
|
|
28
28
|
template: ClassVar[str] = "union_property.py.jinja"
|
|
29
29
|
|
|
@@ -32,7 +32,7 @@ class UnionProperty(PropertyProtocol):
|
|
|
32
32
|
cls,
|
|
33
33
|
*,
|
|
34
34
|
data: oai.Schema,
|
|
35
|
-
name:
|
|
35
|
+
name: oai.UntrustedString,
|
|
36
36
|
required: bool,
|
|
37
37
|
schemas: Schemas,
|
|
38
38
|
parent_name: str,
|
|
@@ -70,12 +70,12 @@ class UnionProperty(PropertyProtocol):
|
|
|
70
70
|
and sub_prop_data.title is not None
|
|
71
71
|
and sub_prop_data.title != data.title
|
|
72
72
|
):
|
|
73
|
-
subscript = sub_prop_data.title
|
|
73
|
+
subscript = sub_prop_data.title.get_untrusted_value()
|
|
74
74
|
else:
|
|
75
75
|
subscript = f"type_{i}"
|
|
76
76
|
|
|
77
77
|
sub_prop, schemas = property_from_data(
|
|
78
|
-
name=f"{name}_{subscript}",
|
|
78
|
+
name=oai.UntrustedString(f"{name.get_untrusted_value()}_{subscript}"),
|
|
79
79
|
required=True,
|
|
80
80
|
data=sub_prop_data,
|
|
81
81
|
schemas=schemas,
|
|
@@ -120,7 +120,7 @@ class UnionProperty(PropertyProtocol):
|
|
|
120
120
|
prop = evolve(prop, default=default_or_error)
|
|
121
121
|
return prop, schemas
|
|
122
122
|
|
|
123
|
-
def convert_value(self, value: Any) -> Value |
|
|
123
|
+
def convert_value(self, value: Any) -> Value | PropertyError | None:
|
|
124
124
|
if value is None or isinstance(value, Value):
|
|
125
125
|
return None
|
|
126
126
|
value_or_error: Value | PropertyError | None = PropertyError(
|
|
@@ -132,7 +132,7 @@ class UnionProperty(PropertyProtocol):
|
|
|
132
132
|
return value_or_error
|
|
133
133
|
return value_or_error
|
|
134
134
|
|
|
135
|
-
def _get_inner_type_strings(self, json: bool) -> set[
|
|
135
|
+
def _get_inner_type_strings(self, json: bool) -> set[strings.PythonCode]:
|
|
136
136
|
return {
|
|
137
137
|
p.get_type_string(
|
|
138
138
|
no_optional=True,
|
|
@@ -142,18 +142,19 @@ class UnionProperty(PropertyProtocol):
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
@staticmethod
|
|
145
|
-
def _get_type_string_from_inner_type_strings(inner_types: set[
|
|
145
|
+
def _get_type_string_from_inner_type_strings(inner_types: set[strings.PythonCode]) -> strings.PythonCode:
|
|
146
146
|
if len(inner_types) == 1:
|
|
147
147
|
return inner_types.pop()
|
|
148
|
-
|
|
148
|
+
joined = " | ".join(sorted((t.as_unembedded_code() for t in inner_types), key=str.lower))
|
|
149
|
+
return strings.PythonCode(joined)
|
|
149
150
|
|
|
150
|
-
def get_base_type_string(self) ->
|
|
151
|
+
def get_base_type_string(self) -> strings.PythonCode:
|
|
151
152
|
return self._get_type_string_from_inner_type_strings(self._get_inner_type_strings(json=False))
|
|
152
153
|
|
|
153
|
-
def get_base_json_type_string(self) ->
|
|
154
|
+
def get_base_json_type_string(self) -> strings.PythonCode:
|
|
154
155
|
return self._get_type_string_from_inner_type_strings(self._get_inner_type_strings(json=True))
|
|
155
156
|
|
|
156
|
-
def get_type_strings_in_union(self, *, no_optional: bool = False, json: bool) -> set[
|
|
157
|
+
def get_type_strings_in_union(self, *, no_optional: bool = False, json: bool) -> set[strings.PythonCode]:
|
|
157
158
|
"""
|
|
158
159
|
Get the set of all the types that should appear within the `Union` representing this property.
|
|
159
160
|
|
|
@@ -164,22 +165,22 @@ class UnionProperty(PropertyProtocol):
|
|
|
164
165
|
json: If True, this returns the JSON types, not the Python types, of this property.
|
|
165
166
|
|
|
166
167
|
Returns:
|
|
167
|
-
A set of
|
|
168
|
+
A set of code snippets containing the types that should appear within `Union`.
|
|
168
169
|
"""
|
|
169
170
|
type_strings = self._get_inner_type_strings(json=json)
|
|
170
171
|
if no_optional:
|
|
171
172
|
return type_strings
|
|
172
173
|
if not self.required:
|
|
173
|
-
type_strings.add("Unset")
|
|
174
|
+
type_strings.add(strings.PythonCode("Unset"))
|
|
174
175
|
return type_strings
|
|
175
176
|
|
|
176
177
|
def get_type_string(
|
|
177
178
|
self,
|
|
178
179
|
no_optional: bool = False,
|
|
179
180
|
json: bool = False,
|
|
180
|
-
) ->
|
|
181
|
+
) -> strings.PythonCode:
|
|
181
182
|
"""
|
|
182
|
-
Get a
|
|
183
|
+
Get a Python code representation of type that should be used when declaring this property.
|
|
183
184
|
This implementation differs slightly from `Property.get_type_string` in order to collapse
|
|
184
185
|
nested union types.
|
|
185
186
|
"""
|
|
@@ -212,5 +213,6 @@ class UnionProperty(PropertyProtocol):
|
|
|
212
213
|
|
|
213
214
|
for inner_prop in self.inner_properties:
|
|
214
215
|
if evolve(cast(Property, inner_prop), required=self.required).validate_location(location) is not None:
|
|
215
|
-
|
|
216
|
+
type_string = self.get_type_string().as_unembedded_code()
|
|
217
|
+
return ParseError(detail=f"{type_string} is not allowed in {location}")
|
|
216
218
|
return None
|
|
@@ -3,24 +3,24 @@ from __future__ import annotations
|
|
|
3
3
|
from typing import Any, ClassVar
|
|
4
4
|
from uuid import UUID
|
|
5
5
|
|
|
6
|
-
from attr import define
|
|
6
|
+
from attr import define, field
|
|
7
7
|
|
|
8
8
|
from ... import schema as oai
|
|
9
|
-
from ...
|
|
9
|
+
from ...strings import PythonCode, PythonIdentifier
|
|
10
10
|
from ..errors import PropertyError
|
|
11
|
-
from .protocol import PropertyProtocol, Value
|
|
11
|
+
from .protocol import PropertyProtocol, Value, convert_example
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
@define
|
|
15
15
|
class UuidProperty(PropertyProtocol):
|
|
16
16
|
"""A property of type uuid.UUID"""
|
|
17
17
|
|
|
18
|
-
name:
|
|
18
|
+
name: oai.UntrustedString
|
|
19
19
|
required: bool
|
|
20
20
|
default: Value | None
|
|
21
21
|
python_name: PythonIdentifier
|
|
22
|
-
description:
|
|
23
|
-
example:
|
|
22
|
+
description: oai.UntrustedString | None
|
|
23
|
+
example: oai.UntrustedString | None = field(converter=convert_example)
|
|
24
24
|
|
|
25
25
|
_type_string: ClassVar[str] = "UUID"
|
|
26
26
|
_json_type_string: ClassVar[str] = "str"
|
|
@@ -35,12 +35,12 @@ class UuidProperty(PropertyProtocol):
|
|
|
35
35
|
@classmethod
|
|
36
36
|
def build(
|
|
37
37
|
cls,
|
|
38
|
-
name:
|
|
38
|
+
name: oai.UntrustedString,
|
|
39
39
|
required: bool,
|
|
40
40
|
default: Any,
|
|
41
41
|
python_name: PythonIdentifier,
|
|
42
|
-
description:
|
|
43
|
-
example:
|
|
42
|
+
description: oai.UntrustedString | None,
|
|
43
|
+
example: Any,
|
|
44
44
|
) -> UuidProperty | PropertyError:
|
|
45
45
|
checked_default = cls.convert_value(default)
|
|
46
46
|
if isinstance(checked_default, PropertyError):
|
|
@@ -56,7 +56,7 @@ class UuidProperty(PropertyProtocol):
|
|
|
56
56
|
)
|
|
57
57
|
|
|
58
58
|
@classmethod
|
|
59
|
-
def convert_value(cls, value: Any) -> Value |
|
|
59
|
+
def convert_value(cls, value: Any) -> Value | PropertyError | None:
|
|
60
60
|
if value is None or isinstance(value, Value):
|
|
61
61
|
return value
|
|
62
62
|
if isinstance(value, str):
|
|
@@ -64,7 +64,7 @@ class UuidProperty(PropertyProtocol):
|
|
|
64
64
|
UUID(value)
|
|
65
65
|
except ValueError:
|
|
66
66
|
return PropertyError(f"Invalid UUID value: {value}")
|
|
67
|
-
return Value(python_code=f"UUID('{value}')", raw_value=value)
|
|
67
|
+
return Value(python_code=PythonCode(f"UUID('{value}')"), raw_value=value)
|
|
68
68
|
return PropertyError(f"Invalid UUID value: {value}")
|
|
69
69
|
|
|
70
70
|
def get_imports(self, *, prefix: str) -> set[str]:
|