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.
Files changed (90) hide show
  1. openapi_python_client/__init__.py +26 -17
  2. openapi_python_client/cli.py +1 -2
  3. openapi_python_client/config.py +3 -1
  4. openapi_python_client/parser/bodies.py +8 -7
  5. openapi_python_client/parser/openapi.py +58 -48
  6. openapi_python_client/parser/properties/__init__.py +34 -33
  7. openapi_python_client/parser/properties/any.py +11 -10
  8. openapi_python_client/parser/properties/boolean.py +13 -13
  9. openapi_python_client/parser/properties/const.py +11 -10
  10. openapi_python_client/parser/properties/date.py +12 -11
  11. openapi_python_client/parser/properties/datetime.py +12 -11
  12. openapi_python_client/parser/properties/enum_property.py +23 -20
  13. openapi_python_client/parser/properties/file.py +11 -10
  14. openapi_python_client/parser/properties/float.py +13 -13
  15. openapi_python_client/parser/properties/int.py +11 -11
  16. openapi_python_client/parser/properties/list_property.py +22 -22
  17. openapi_python_client/parser/properties/literal_enum_property.py +20 -20
  18. openapi_python_client/parser/properties/merge_properties.py +9 -6
  19. openapi_python_client/parser/properties/model_property.py +34 -33
  20. openapi_python_client/parser/properties/none.py +11 -11
  21. openapi_python_client/parser/properties/protocol.py +42 -32
  22. openapi_python_client/parser/properties/schemas.py +30 -23
  23. openapi_python_client/parser/properties/string.py +10 -11
  24. openapi_python_client/parser/properties/union.py +24 -22
  25. openapi_python_client/parser/properties/uuid.py +11 -11
  26. openapi_python_client/parser/responses.py +30 -23
  27. openapi_python_client/schema/__init__.py +2 -1
  28. openapi_python_client/schema/openapi_schema_pydantic/callback.py +3 -1
  29. openapi_python_client/schema/openapi_schema_pydantic/components.py +10 -9
  30. openapi_python_client/schema/openapi_schema_pydantic/contact.py +5 -3
  31. openapi_python_client/schema/openapi_schema_pydantic/discriminator.py +4 -2
  32. openapi_python_client/schema/openapi_schema_pydantic/encoding.py +4 -3
  33. openapi_python_client/schema/openapi_schema_pydantic/example.py +5 -3
  34. openapi_python_client/schema/openapi_schema_pydantic/external_documentation.py +4 -2
  35. openapi_python_client/schema/openapi_schema_pydantic/header.py +20 -6
  36. openapi_python_client/schema/openapi_schema_pydantic/info.py +5 -4
  37. openapi_python_client/schema/openapi_schema_pydantic/license.py +4 -2
  38. openapi_python_client/schema/openapi_schema_pydantic/link.py +5 -4
  39. openapi_python_client/schema/openapi_schema_pydantic/media_type.py +3 -2
  40. openapi_python_client/schema/openapi_schema_pydantic/oauth_flow.py +6 -4
  41. openapi_python_client/schema/openapi_schema_pydantic/open_api.py +5 -3
  42. openapi_python_client/schema/openapi_schema_pydantic/operation.py +6 -5
  43. openapi_python_client/schema/openapi_schema_pydantic/parameter.py +6 -5
  44. openapi_python_client/schema/openapi_schema_pydantic/path_item.py +5 -3
  45. openapi_python_client/schema/openapi_schema_pydantic/paths.py +2 -1
  46. openapi_python_client/schema/openapi_schema_pydantic/reference.py +3 -1
  47. openapi_python_client/schema/openapi_schema_pydantic/request_body.py +3 -2
  48. openapi_python_client/schema/openapi_schema_pydantic/response.py +5 -4
  49. openapi_python_client/schema/openapi_schema_pydantic/responses.py +2 -1
  50. openapi_python_client/schema/openapi_schema_pydantic/schema.py +7 -6
  51. openapi_python_client/schema/openapi_schema_pydantic/security_requirement.py +3 -1
  52. openapi_python_client/schema/openapi_schema_pydantic/security_scheme.py +8 -7
  53. openapi_python_client/schema/openapi_schema_pydantic/server.py +4 -3
  54. openapi_python_client/schema/openapi_schema_pydantic/server_variable.py +5 -3
  55. openapi_python_client/schema/openapi_schema_pydantic/tag.py +3 -2
  56. openapi_python_client/schema/openapi_schema_pydantic/xml.py +5 -3
  57. openapi_python_client/schema/ref.py +52 -0
  58. openapi_python_client/schema/untrusted_string.py +56 -0
  59. openapi_python_client/strings.py +245 -0
  60. openapi_python_client/templates/README.md.jinja +3 -3
  61. openapi_python_client/templates/client.py.jinja +5 -5
  62. openapi_python_client/templates/endpoint_macros.py.jinja +18 -18
  63. openapi_python_client/templates/endpoint_module.py.jinja +13 -13
  64. openapi_python_client/templates/helpers.jinja +3 -7
  65. openapi_python_client/templates/literal_enum.py.jinja +1 -1
  66. openapi_python_client/templates/model.py.jinja +18 -18
  67. openapi_python_client/templates/package_init.py.jinja +2 -2
  68. openapi_python_client/templates/property_templates/const_property.py.jinja +3 -3
  69. openapi_python_client/templates/property_templates/date_property.py.jinja +1 -1
  70. openapi_python_client/templates/property_templates/datetime_property.py.jinja +1 -1
  71. openapi_python_client/templates/property_templates/enum_property.py.jinja +1 -1
  72. openapi_python_client/templates/property_templates/file_property.py.jinja +1 -1
  73. openapi_python_client/templates/property_templates/list_property.py.jinja +3 -3
  74. openapi_python_client/templates/property_templates/literal_enum_property.py.jinja +2 -2
  75. openapi_python_client/templates/property_templates/model_property.py.jinja +1 -1
  76. openapi_python_client/templates/property_templates/property_macros.py.jinja +1 -1
  77. openapi_python_client/templates/property_templates/union_property.py.jinja +9 -9
  78. openapi_python_client/templates/property_templates/uuid_property.py.jinja +1 -1
  79. openapi_python_client/templates/pyproject_pdm.toml.jinja +2 -2
  80. openapi_python_client/templates/pyproject_poetry.toml.jinja +2 -2
  81. openapi_python_client/templates/pyproject_uv.toml.jinja +3 -3
  82. openapi_python_client/templates/setup.py.jinja +2 -2
  83. openapi_python_client/templates/str_enum.py.jinja +2 -2
  84. {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.29.1.dist-info}/METADATA +4 -5
  85. openapi_python_client-0.29.1.dist-info/RECORD +115 -0
  86. {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.29.1.dist-info}/WHEEL +1 -1
  87. openapi_python_client/utils.py +0 -122
  88. openapi_python_client-0.29.0.dist-info/RECORD +0 -113
  89. {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.29.1.dist-info}/entry_points.txt +0 -0
  90. {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.29.1.dist-info}/licenses/LICENSE +0 -0
@@ -2,23 +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
- from ...utils import PythonIdentifier
7
+ from ... import schema as oai
8
+ from ...strings import PythonIdentifier
8
9
  from ..errors import PropertyError
9
- from .protocol import PropertyProtocol
10
+ from .protocol import PropertyProtocol, convert_example
10
11
 
11
12
 
12
13
  @define
13
14
  class FileProperty(PropertyProtocol):
14
15
  """A property used for uploading files"""
15
16
 
16
- name: str
17
+ name: oai.UntrustedString
17
18
  required: bool
18
19
  default: None
19
20
  python_name: PythonIdentifier
20
- description: str | None
21
- example: str | None
21
+ description: oai.UntrustedString | None
22
+ example: oai.UntrustedString | None = field(converter=convert_example)
22
23
 
23
24
  _type_string: ClassVar[str] = "File"
24
25
  # Return type of File.to_tuple()
@@ -28,12 +29,12 @@ class FileProperty(PropertyProtocol):
28
29
  @classmethod
29
30
  def build(
30
31
  cls,
31
- name: str,
32
+ name: oai.UntrustedString,
32
33
  required: bool,
33
34
  default: Any,
34
35
  python_name: PythonIdentifier,
35
- description: str | None,
36
- example: str | None,
36
+ description: oai.UntrustedString | None,
37
+ example: Any,
37
38
  ) -> FileProperty | PropertyError:
38
39
  default_or_err = cls.convert_value(default)
39
40
  if isinstance(default_or_err, PropertyError):
@@ -49,7 +50,7 @@ class FileProperty(PropertyProtocol):
49
50
  )
50
51
 
51
52
  @classmethod
52
- def convert_value(cls, value: Any) -> None | PropertyError:
53
+ def convert_value(cls, value: Any) -> PropertyError | None:
53
54
  if value is not None:
54
55
  return PropertyError(detail="File properties cannot have a default value")
55
56
  return value
@@ -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 ...utils import PythonIdentifier
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 FloatProperty(PropertyProtocol):
15
15
  """A property of type float"""
16
16
 
17
- name: str
17
+ name: oai.UntrustedString
18
18
  required: bool
19
19
  default: Value | None
20
20
  python_name: PythonIdentifier
21
- description: str | None
22
- example: str | None
21
+ description: oai.UntrustedString | None
22
+ example: oai.UntrustedString | None = field(converter=convert_example)
23
23
 
24
24
  _type_string: ClassVar[str] = "float"
25
25
  _json_type_string: ClassVar[str] = "float"
@@ -34,12 +34,12 @@ class FloatProperty(PropertyProtocol):
34
34
  @classmethod
35
35
  def build(
36
36
  cls,
37
- name: str,
37
+ name: oai.UntrustedString,
38
38
  required: bool,
39
39
  default: Any,
40
40
  python_name: PythonIdentifier,
41
- description: str | None,
42
- example: str | None,
41
+ description: oai.UntrustedString | None,
42
+ example: Any,
43
43
  ) -> FloatProperty | PropertyError:
44
44
  checked_default = cls.convert_value(default)
45
45
  if isinstance(checked_default, PropertyError):
@@ -55,17 +55,17 @@ class FloatProperty(PropertyProtocol):
55
55
  )
56
56
 
57
57
  @classmethod
58
- def convert_value(cls, value: Any) -> Value | None | PropertyError:
58
+ def convert_value(cls, value: Any) -> Value | PropertyError | None:
59
59
  if isinstance(value, Value) or value is None:
60
60
  return value
61
61
  if isinstance(value, str):
62
62
  try:
63
63
  parsed = float(value)
64
- return Value(python_code=str(parsed), raw_value=value)
64
+ return Value(python_code=PythonCode(str(parsed)), raw_value=value)
65
65
  except ValueError:
66
66
  return PropertyError(f"Invalid float value: {value}")
67
67
  if isinstance(value, float):
68
- return Value(python_code=str(value), raw_value=value)
68
+ return Value(python_code=PythonCode(str(value)), raw_value=value)
69
69
  if isinstance(value, int) and not isinstance(value, bool):
70
- return Value(python_code=str(float(value)), raw_value=value)
70
+ return Value(python_code=PythonCode(str(float(value))), raw_value=value)
71
71
  return PropertyError(f"Cannot convert {value} to a float")
@@ -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 ...utils import PythonIdentifier
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 IntProperty(PropertyProtocol):
15
15
  """A property of type int"""
16
16
 
17
- name: str
17
+ name: oai.UntrustedString
18
18
  required: bool
19
19
  default: Value | None
20
20
  python_name: PythonIdentifier
21
- description: str | None
22
- example: str | None
21
+ description: oai.UntrustedString | None
22
+ example: oai.UntrustedString | None = field(converter=convert_example)
23
23
 
24
24
  _type_string: ClassVar[str] = "int"
25
25
  _json_type_string: ClassVar[str] = "int"
@@ -34,12 +34,12 @@ class IntProperty(PropertyProtocol):
34
34
  @classmethod
35
35
  def build(
36
36
  cls,
37
- name: str,
37
+ name: oai.UntrustedString,
38
38
  required: bool,
39
39
  default: Any,
40
40
  python_name: PythonIdentifier,
41
- description: str | None,
42
- example: str | None,
41
+ description: oai.UntrustedString | None,
42
+ example: Any,
43
43
  ) -> IntProperty | PropertyError:
44
44
  checked_default = cls.convert_value(default)
45
45
  if isinstance(checked_default, PropertyError):
@@ -55,7 +55,7 @@ class IntProperty(PropertyProtocol):
55
55
  )
56
56
 
57
57
  @classmethod
58
- def convert_value(cls, value: Any) -> Value | None | PropertyError:
58
+ def convert_value(cls, value: Any) -> Value | PropertyError | None:
59
59
  if value is None or isinstance(value, Value):
60
60
  return value
61
61
  converted = value
@@ -69,5 +69,5 @@ class IntProperty(PropertyProtocol):
69
69
  if converted == as_int:
70
70
  converted = as_int
71
71
  if isinstance(converted, int) and not isinstance(converted, bool):
72
- return Value(python_code=str(converted), raw_value=value)
72
+ return Value(python_code=PythonCode(str(converted)), raw_value=value)
73
73
  return PropertyError(f"Invalid int value: {value}")
@@ -2,12 +2,12 @@ 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
- from ... import Config, utils
7
+ from ... import Config, strings
8
8
  from ... import schema as oai
9
9
  from ..errors import PropertyError
10
- from .protocol import PropertyProtocol, Value
10
+ from .protocol import PropertyProtocol, Value, convert_example
11
11
  from .schemas import ReferencePath, Schemas
12
12
 
13
13
 
@@ -15,12 +15,12 @@ from .schemas import ReferencePath, Schemas
15
15
  class ListProperty(PropertyProtocol):
16
16
  """A property representing a list (array) of other properties"""
17
17
 
18
- name: str
18
+ name: oai.UntrustedString
19
19
  required: bool
20
20
  default: Value | None
21
- python_name: utils.PythonIdentifier
22
- description: str | None
23
- example: str | None
21
+ python_name: strings.PythonIdentifier
22
+ description: oai.UntrustedString | None
23
+ example: oai.UntrustedString | None = field(converter=convert_example)
24
24
  inner_property: PropertyProtocol
25
25
  template: ClassVar[str] = "list_property.py.jinja"
26
26
 
@@ -29,13 +29,13 @@ class ListProperty(PropertyProtocol):
29
29
  cls,
30
30
  *,
31
31
  data: oai.Schema,
32
- name: str,
32
+ name: oai.UntrustedString,
33
33
  required: bool,
34
34
  schemas: Schemas,
35
35
  parent_name: str,
36
36
  config: Config,
37
37
  process_properties: bool,
38
- roots: set[ReferencePath | utils.ClassName],
38
+ roots: set[ReferencePath | strings.ClassName],
39
39
  ) -> tuple[ListProperty | PropertyError, Schemas]:
40
40
  """
41
41
  Build a ListProperty the right way, use this instead of the normal constructor.
@@ -77,7 +77,7 @@ class ListProperty(PropertyProtocol):
77
77
  inner_schema = oai.Schema(anyOf=items)
78
78
 
79
79
  inner_prop, schemas = property_from_data(
80
- name=f"{name}_item",
80
+ name=oai.UntrustedString(f"{name.get_untrusted_value()}_item"),
81
81
  required=True,
82
82
  data=inner_schema,
83
83
  schemas=schemas,
@@ -95,25 +95,25 @@ class ListProperty(PropertyProtocol):
95
95
  required=required,
96
96
  default=None,
97
97
  inner_property=inner_prop,
98
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
98
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
99
99
  description=data.description,
100
100
  example=data.example,
101
101
  ),
102
102
  schemas,
103
103
  )
104
104
 
105
- def convert_value(self, value: Any) -> Value | None | PropertyError:
105
+ def convert_value(self, value: Any) -> Value | PropertyError | None:
106
106
  return None # pragma: no cover
107
107
 
108
- def get_base_type_string(self) -> str:
109
- return f"list[{self.inner_property.get_type_string()}]"
108
+ def get_base_type_string(self) -> strings.PythonCode:
109
+ return strings.PythonCode(f"list[{self.inner_property.get_type_string().as_unembedded_code()}]")
110
110
 
111
- def get_base_json_type_string(self) -> str:
112
- return f"list[{self.inner_property.get_type_string(json=True)}]"
111
+ def get_base_json_type_string(self) -> strings.PythonCode:
112
+ return strings.PythonCode(f"list[{self.inner_property.get_type_string(json=True).as_unembedded_code()}]")
113
113
 
114
- def get_instance_type_string(self) -> str:
115
- """Get a string representation of runtime type that should be used for `isinstance` checks"""
116
- return "list"
114
+ def get_instance_type_string(self) -> strings.PythonCode:
115
+ """Get a code representation of runtime type that should be used for `isinstance` checks"""
116
+ return strings.PythonCode("list")
117
117
 
118
118
  def get_imports(self, *, prefix: str) -> set[str]:
119
119
  """
@@ -137,9 +137,9 @@ class ListProperty(PropertyProtocol):
137
137
  self,
138
138
  no_optional: bool = False,
139
139
  json: bool = False,
140
- ) -> str:
140
+ ) -> strings.PythonCode:
141
141
  """
142
- Get a string representation of type that should be used when declaring this property
142
+ Get a Python code representation of type that should be used when declaring this property
143
143
 
144
144
  Args:
145
145
  no_optional: Do not include Optional or Unset even if the value is optional (needed for isinstance checks)
@@ -152,4 +152,4 @@ class ListProperty(PropertyProtocol):
152
152
 
153
153
  if no_optional or self.required:
154
154
  return type_string
155
- return f"{type_string} | Unset"
155
+ return strings.PythonCode(f"{type_string.as_unembedded_code()} | Unset")
@@ -5,14 +5,14 @@ __all__ = ["LiteralEnumProperty"]
5
5
  from typing import Any, ClassVar, cast
6
6
 
7
7
  from attr import evolve
8
- from attrs import define
8
+ from attrs import define, field
9
9
 
10
- from ... import Config, utils
10
+ from ... import Config, strings
11
11
  from ... import schema as oai
12
12
  from ...schema import DataType
13
13
  from ..errors import PropertyError
14
14
  from .none import NoneProperty
15
- from .protocol import PropertyProtocol, Value
15
+ from .protocol import PropertyProtocol, Value, convert_example
16
16
  from .schemas import Class, Schemas
17
17
  from .union import UnionProperty
18
18
 
@@ -23,12 +23,12 @@ ValueType = str | int
23
23
  class LiteralEnumProperty(PropertyProtocol):
24
24
  """A property that should use a literal enum"""
25
25
 
26
- name: str
26
+ name: oai.UntrustedString
27
27
  required: bool
28
28
  default: Value | None
29
- python_name: utils.PythonIdentifier
30
- description: str | None
31
- example: str | None
29
+ python_name: strings.PythonIdentifier
30
+ description: oai.UntrustedString | None
31
+ example: oai.UntrustedString | None = field(converter=convert_example)
32
32
  values: set[ValueType]
33
33
  class_info: Class
34
34
  value_type: type[ValueType]
@@ -47,7 +47,7 @@ class LiteralEnumProperty(PropertyProtocol):
47
47
  cls,
48
48
  *,
49
49
  data: oai.Schema,
50
- name: str,
50
+ name: oai.UntrustedString,
51
51
  required: bool,
52
52
  schemas: Schemas,
53
53
  parent_name: str,
@@ -82,7 +82,7 @@ class LiteralEnumProperty(PropertyProtocol):
82
82
  name=name,
83
83
  required=required,
84
84
  default="None",
85
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
85
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
86
86
  description=None,
87
87
  example=None,
88
88
  ),
@@ -114,9 +114,9 @@ class LiteralEnumProperty(PropertyProtocol):
114
114
  config=config,
115
115
  )
116
116
 
117
- class_name = data.title or name
117
+ class_name: str | oai.UntrustedString = data.title or name
118
118
  if parent_name:
119
- class_name = f"{utils.pascal_case(parent_name)}{utils.pascal_case(class_name)}"
119
+ class_name = f"{strings.pascal_case(parent_name)}{strings.pascal_case(class_name)}"
120
120
  class_info = Class.from_string(string=class_name, config=config)
121
121
  values: set[str | int] = set(value_list)
122
122
 
@@ -137,7 +137,7 @@ class LiteralEnumProperty(PropertyProtocol):
137
137
  values=values,
138
138
  value_type=value_type,
139
139
  default=None,
140
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
140
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
141
141
  description=data.description,
142
142
  example=data.example,
143
143
  )
@@ -155,19 +155,19 @@ class LiteralEnumProperty(PropertyProtocol):
155
155
  return value
156
156
  if isinstance(value, self.value_type):
157
157
  if value in self.values:
158
- return Value(python_code=repr(value), raw_value=value)
158
+ return Value(python_code=strings.PythonCode(repr(value)), raw_value=value)
159
159
  else:
160
160
  return PropertyError(detail=f"Value {value} is not valid for enum {self.name}")
161
161
  return PropertyError(detail=f"Cannot convert {value} to enum {self.name} of type {self.value_type}")
162
162
 
163
- def get_base_type_string(self) -> str:
164
- return self.class_info.name
163
+ def get_base_type_string(self) -> strings.PythonCode:
164
+ return strings.PythonCode(self.class_info.name)
165
165
 
166
- def get_base_json_type_string(self) -> str:
167
- return self.value_type.__name__
166
+ def get_base_json_type_string(self) -> strings.PythonCode:
167
+ return strings.PythonCode(self.value_type.__name__)
168
168
 
169
- def get_instance_type_string(self) -> str:
170
- return self.value_type.__name__
169
+ def get_instance_type_string(self) -> strings.PythonCode:
170
+ return strings.PythonCode(self.value_type.__name__)
171
171
 
172
172
  def get_imports(self, *, prefix: str) -> set[str]:
173
173
  """
@@ -186,4 +186,4 @@ class LiteralEnumProperty(PropertyProtocol):
186
186
  return imports
187
187
 
188
188
  def get_class_name_snake_case(self) -> str:
189
- return utils.snake_case(self.class_info.name)
189
+ return strings.snake_case(self.class_info.name)
@@ -67,11 +67,12 @@ def merge_properties(prop1: Property, prop2: Property) -> Property | PropertyErr
67
67
  return merged
68
68
 
69
69
  return PropertyError(
70
- detail=f"{prop1.get_type_string(no_optional=True)} can't be merged with {prop2.get_type_string(no_optional=True)}"
70
+ detail=f"{prop1.get_type_string(no_optional=True).as_unembedded_code()} can't be merged with "
71
+ f"{prop2.get_type_string(no_optional=True).as_unembedded_code()}"
71
72
  )
72
73
 
73
74
 
74
- def _merge_same_type(prop1: Property, prop2: Property) -> Property | None | PropertyError:
75
+ def _merge_same_type(prop1: Property, prop2: Property) -> Property | PropertyError | None:
75
76
  if type(prop1) is not type(prop2):
76
77
  return None
77
78
 
@@ -90,7 +91,7 @@ def _merge_same_type(prop1: Property, prop2: Property) -> Property | None | Prop
90
91
  return _merge_common_attributes(prop1, prop2)
91
92
 
92
93
 
93
- def _merge_string_with_format(prop1: Property, prop2: Property) -> Property | None | PropertyError:
94
+ def _merge_string_with_format(prop1: Property, prop2: Property) -> Property | PropertyError | None:
94
95
  """Merge a string that has no format with a string that has a format"""
95
96
  # Here we need to use the DateProperty/DateTimeProperty/FileProperty as the base so that we preserve
96
97
  # its class, but keep the correct override order for merging the attributes.
@@ -103,7 +104,7 @@ def _merge_string_with_format(prop1: Property, prop2: Property) -> Property | No
103
104
  return None
104
105
 
105
106
 
106
- def _merge_numeric(prop1: Property, prop2: Property) -> IntProperty | None | PropertyError:
107
+ def _merge_numeric(prop1: Property, prop2: Property) -> IntProperty | PropertyError | None:
107
108
  """Merge IntProperty with FloatProperty"""
108
109
  if isinstance(prop1, IntProperty) and isinstance(prop2, IntProperty | FloatProperty):
109
110
  return _merge_common_attributes(prop1, prop2)
@@ -136,7 +137,8 @@ def _merge_with_enum(prop1: PropertyProtocol, prop2: PropertyProtocol) -> EnumPr
136
137
  ):
137
138
  return _merge_common_attributes(enum_prop, prop1, prop2)
138
139
  return PropertyError(
139
- detail=f"can't combine enum of type {enum_prop.value_type} with {non_enum_prop.get_type_string(no_optional=True)}"
140
+ detail=f"can't combine enum of type {enum_prop.value_type} with "
141
+ f"{non_enum_prop.get_type_string(no_optional=True).as_unembedded_code()}"
140
142
  )
141
143
 
142
144
 
@@ -162,7 +164,8 @@ def _merge_with_literal_enum(prop1: PropertyProtocol, prop2: PropertyProtocol) -
162
164
  ):
163
165
  return _merge_common_attributes(enum_prop, prop1, prop2)
164
166
  return PropertyError(
165
- detail=f"can't combine literal enum of type {enum_prop.value_type} with {non_enum_prop.get_type_string(no_optional=True)}"
167
+ detail=f"can't combine literal enum of type {enum_prop.value_type} with "
168
+ f"{non_enum_prop.get_type_string(no_optional=True).as_unembedded_code()}"
166
169
  )
167
170
 
168
171
 
@@ -3,14 +3,14 @@ from __future__ import annotations
3
3
  from itertools import chain
4
4
  from typing import Any, ClassVar, NamedTuple
5
5
 
6
- from attrs import define, evolve
6
+ from attrs import define, evolve, field
7
7
 
8
- from ... import Config, utils
8
+ from ... import Config, strings
9
9
  from ... import schema as oai
10
- from ...utils import PythonIdentifier
10
+ from ...strings import PythonIdentifier
11
11
  from ..errors import ParseError, PropertyError
12
12
  from .any import AnyProperty
13
- from .protocol import PropertyProtocol, Value
13
+ from .protocol import PropertyProtocol, Value, convert_example
14
14
  from .schemas import Class, ReferencePath, Schemas, parse_reference_path
15
15
 
16
16
 
@@ -18,15 +18,15 @@ from .schemas import Class, ReferencePath, Schemas, parse_reference_path
18
18
  class ModelProperty(PropertyProtocol):
19
19
  """A property which refers to another Schema"""
20
20
 
21
- name: str
21
+ name: oai.UntrustedString
22
22
  required: bool
23
23
  default: Value | None
24
- python_name: utils.PythonIdentifier
25
- example: str | None
24
+ python_name: strings.PythonIdentifier
25
+ example: oai.UntrustedString | None = field(converter=convert_example)
26
26
  class_info: Class
27
27
  data: oai.Schema
28
- description: str
29
- roots: set[ReferencePath | utils.ClassName]
28
+ description: oai.UntrustedString | None
29
+ roots: set[ReferencePath | strings.ClassName]
30
30
  required_properties: list[Property] | None
31
31
  optional_properties: list[Property] | None
32
32
  relative_imports: set[str] | None
@@ -43,13 +43,13 @@ class ModelProperty(PropertyProtocol):
43
43
  cls,
44
44
  *,
45
45
  data: oai.Schema,
46
- name: str,
46
+ name: oai.UntrustedString,
47
47
  schemas: Schemas,
48
48
  required: bool,
49
49
  parent_name: str | None,
50
50
  config: Config,
51
51
  process_properties: bool,
52
- roots: set[ReferencePath | utils.ClassName],
52
+ roots: set[ReferencePath | strings.ClassName],
53
53
  ) -> tuple[ModelProperty | PropertyError, Schemas]:
54
54
  """
55
55
  A single ModelProperty from its OAI data
@@ -65,12 +65,13 @@ class ModelProperty(PropertyProtocol):
65
65
  roots: Set of strings that identify schema objects on which the new ModelProperty will depend
66
66
  process_properties: Determines whether the new ModelProperty will be initialized with property data
67
67
  """
68
+ class_string: str | oai.UntrustedString
68
69
  if not config.use_path_prefixes_for_title_model_names and data.title:
69
70
  class_string = data.title
70
71
  else:
71
72
  title = data.title or name
72
73
  if parent_name:
73
- class_string = f"{utils.pascal_case(parent_name)}{utils.pascal_case(title)}"
74
+ class_string = f"{strings.pascal_case(parent_name)}{strings.pascal_case(title)}"
74
75
  else:
75
76
  class_string = title
76
77
  class_info = Class.from_string(string=class_string, config=config)
@@ -92,7 +93,7 @@ class ModelProperty(PropertyProtocol):
92
93
  relative_imports = property_data.relative_imports
93
94
  lazy_imports = property_data.lazy_imports
94
95
  for root in roots:
95
- if isinstance(root, utils.ClassName):
96
+ if isinstance(root, strings.ClassName):
96
97
  continue
97
98
  schemas.add_dependencies(root, {class_info.name})
98
99
 
@@ -105,11 +106,11 @@ class ModelProperty(PropertyProtocol):
105
106
  relative_imports=relative_imports,
106
107
  lazy_imports=lazy_imports,
107
108
  additional_properties=additional_properties,
108
- description=data.description or "",
109
+ description=data.description,
109
110
  default=None,
110
111
  required=required,
111
112
  name=name,
112
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
113
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
113
114
  example=data.example,
114
115
  )
115
116
  if class_info.name in schemas.classes_by_name:
@@ -126,7 +127,7 @@ class ModelProperty(PropertyProtocol):
126
127
  return prop, schemas
127
128
 
128
129
  @classmethod
129
- def convert_value(cls, value: Any) -> Value | None | PropertyError:
130
+ def convert_value(cls, value: Any) -> Value | PropertyError | None:
130
131
  if value is not None:
131
132
  return PropertyError(detail="ModelProperty cannot have a default value") # pragma: no cover
132
133
  return None
@@ -140,8 +141,8 @@ class ModelProperty(PropertyProtocol):
140
141
  """Constructs a self import statement from this ModelProperty's attributes"""
141
142
  return f"models.{self.class_info.module_name} import {self.class_info.name}"
142
143
 
143
- def get_base_type_string(self) -> str:
144
- return self.class_info.name
144
+ def get_base_type_string(self) -> strings.PythonCode:
145
+ return strings.PythonCode(self.class_info.name)
145
146
 
146
147
  def get_imports(self, *, prefix: str) -> set[str]:
147
148
  """
@@ -188,9 +189,9 @@ class ModelProperty(PropertyProtocol):
188
189
  self,
189
190
  no_optional: bool = False,
190
191
  json: bool = False,
191
- ) -> str:
192
+ ) -> strings.PythonCode:
192
193
  """
193
- Get a string representation of type that should be used when declaring this property
194
+ Get a Python code representation of type that should be used when declaring this property
194
195
 
195
196
  Args:
196
197
  no_optional: Do not include Optional or Unset even if the value is optional (needed for isinstance checks)
@@ -203,7 +204,7 @@ class ModelProperty(PropertyProtocol):
203
204
 
204
205
  if no_optional or self.required:
205
206
  return type_string
206
- return f"{type_string} | Unset"
207
+ return strings.PythonCode(f"{type_string.as_unembedded_code()} | Unset")
207
208
 
208
209
 
209
210
  from .property import Property # noqa: E402
@@ -232,14 +233,14 @@ def _process_properties( # noqa: PLR0912, PLR0911
232
233
  *,
233
234
  data: oai.Schema,
234
235
  schemas: Schemas,
235
- class_name: utils.ClassName,
236
+ class_name: strings.ClassName,
236
237
  config: Config,
237
- roots: set[ReferencePath | utils.ClassName],
238
+ roots: set[ReferencePath | strings.ClassName],
238
239
  ) -> _PropertyData | PropertyError:
239
240
  from . import property_from_data # noqa: PLC0415
240
241
  from .merge_properties import merge_properties # noqa: PLC0415
241
242
 
242
- properties: dict[str, Property] = {}
243
+ properties: dict[oai.UntrustedString, Property] = {}
243
244
  relative_imports: set[str] = set()
244
245
  lazy_imports: set[str] = set()
245
246
  required_set = set(data.required or [])
@@ -265,7 +266,7 @@ def _process_properties( # noqa: PLR0912, PLR0911
265
266
  properties[merged_prop.name] = merged_prop
266
267
  return None
267
268
 
268
- unprocessed_props: list[tuple[str, oai.Reference | oai.Schema]] = (
269
+ unprocessed_props: list[tuple[oai.UntrustedString, oai.Reference | oai.Schema]] = (
269
270
  list(data.properties.items()) if data.properties else []
270
271
  )
271
272
  for sub_prop in data.allOf:
@@ -275,7 +276,7 @@ def _process_properties( # noqa: PLR0912, PLR0911
275
276
  return PropertyError(detail=ref_path.detail, data=sub_prop)
276
277
  sub_model = schemas.classes_by_reference.get(ref_path)
277
278
  if sub_model is None:
278
- return PropertyError(f"Reference {sub_prop.ref} not found")
279
+ return PropertyError(f"Reference {sub_prop.ref.get_untrusted_value()} not found")
279
280
  if not isinstance(sub_model, ModelProperty):
280
281
  return PropertyError("Cannot take allOf a non-object")
281
282
  # Properties of allOf references first should be processed first
@@ -335,10 +336,10 @@ def _process_properties( # noqa: PLR0912, PLR0911
335
336
 
336
337
 
337
338
  ANY_ADDITIONAL_PROPERTY = AnyProperty.build(
338
- name="additional",
339
+ name=oai.UntrustedString("additional"),
339
340
  required=True,
340
341
  default=None,
341
- description="",
342
+ description=None,
342
343
  python_name=PythonIdentifier(value="additional", prefix=""),
343
344
  example=None,
344
345
  )
@@ -348,10 +349,10 @@ def _get_additional_properties(
348
349
  *,
349
350
  schema_additional: None | (bool | (oai.Reference | oai.Schema)),
350
351
  schemas: Schemas,
351
- class_name: utils.ClassName,
352
+ class_name: strings.ClassName,
352
353
  config: Config,
353
- roots: set[ReferencePath | utils.ClassName],
354
- ) -> tuple[Property | None | PropertyError, Schemas]:
354
+ roots: set[ReferencePath | strings.ClassName],
355
+ ) -> tuple[Property | PropertyError | None, Schemas]:
355
356
  from . import property_from_data # noqa: PLC0415
356
357
 
357
358
  if schema_additional is None:
@@ -367,7 +368,7 @@ def _get_additional_properties(
367
368
  return ANY_ADDITIONAL_PROPERTY, schemas
368
369
 
369
370
  additional_properties, schemas = property_from_data(
370
- name="AdditionalProperty",
371
+ name=oai.UntrustedString("AdditionalProperty"),
371
372
  required=True, # in the sense that if present in the dict will not be None
372
373
  data=schema_additional,
373
374
  schemas=schemas,
@@ -384,7 +385,7 @@ def _process_property_data(
384
385
  schemas: Schemas,
385
386
  class_info: Class,
386
387
  config: Config,
387
- roots: set[ReferencePath | utils.ClassName],
388
+ roots: set[ReferencePath | strings.ClassName],
388
389
  ) -> tuple[tuple[_PropertyData, Property | None] | PropertyError, Schemas]:
389
390
  property_data = _process_properties(
390
391
  data=data, schemas=schemas, class_name=class_info.name, config=config, roots=roots