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
@@ -18,8 +18,10 @@ from collections.abc import Iterable
18
18
 
19
19
  from attrs import evolve
20
20
 
21
- from ... import Config, utils
21
+ from ... import Config, strings
22
22
  from ... import schema as oai
23
+ from ...schema.ref import Ref
24
+ from ...schema.untrusted_string import UntrustedString
23
25
  from ..errors import ParameterError, ParseError, PropertyError
24
26
  from .any import AnyProperty
25
27
  from .boolean import BooleanProperty
@@ -51,11 +53,11 @@ from .uuid import UuidProperty
51
53
 
52
54
 
53
55
  def _string_based_property(
54
- name: str, required: bool, data: oai.Schema, config: Config
56
+ name: UntrustedString, required: bool, data: oai.Schema, config: Config
55
57
  ) -> StringProperty | DateProperty | DateTimeProperty | FileProperty | UuidProperty | PropertyError:
56
58
  """Construct a Property from the type "string" """
57
59
  string_format = data.schema_format
58
- python_name = utils.PythonIdentifier(value=name, prefix=config.field_prefix)
60
+ python_name = strings.PythonIdentifier(value=name, prefix=config.field_prefix)
59
61
  if string_format == "date-time":
60
62
  return DateTimeProperty.build(
61
63
  name=name,
@@ -103,16 +105,16 @@ def _string_based_property(
103
105
 
104
106
 
105
107
  def _property_from_ref(
106
- name: str,
108
+ name: UntrustedString,
107
109
  required: bool,
108
110
  parent: oai.Schema | None,
109
111
  data: oai.Reference,
110
112
  schemas: Schemas,
111
113
  config: Config,
112
- roots: set[ReferencePath | utils.ClassName],
114
+ roots: set[ReferencePath | strings.ClassName],
113
115
  ) -> tuple[Property | PropertyError, Schemas]:
114
116
  ref_path = parse_reference_path(data.ref)
115
- if isinstance(ref_path, ParseError):
117
+ if isinstance(ref_path, ParseError): # pragma: no cover
116
118
  return PropertyError(data=data, detail=ref_path.detail), schemas
117
119
  existing = schemas.classes_by_reference.get(ref_path)
118
120
  if not existing:
@@ -130,7 +132,7 @@ def _property_from_ref(
130
132
  existing,
131
133
  required=required,
132
134
  name=name,
133
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
135
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
134
136
  default=default, # type: ignore # mypy can't tell that default comes from the same class...
135
137
  )
136
138
 
@@ -139,18 +141,17 @@ def _property_from_ref(
139
141
 
140
142
 
141
143
  def property_from_data( # noqa: PLR0911, PLR0912
142
- name: str,
144
+ name: UntrustedString,
143
145
  required: bool,
144
146
  data: oai.Reference | oai.Schema,
145
147
  schemas: Schemas,
146
148
  parent_name: str,
147
149
  config: Config,
148
150
  process_properties: bool = True,
149
- roots: set[ReferencePath | utils.ClassName] | None = None,
151
+ roots: set[ReferencePath | strings.ClassName] | None = None,
150
152
  ) -> tuple[Property | PropertyError, Schemas]:
151
153
  """Generate a Property from the OpenAPI dictionary representation of it"""
152
154
  roots = roots or set()
153
- name = utils.remove_string_escapes(name)
154
155
  if isinstance(data, oai.Reference):
155
156
  return _property_from_ref(
156
157
  name=name,
@@ -190,7 +191,7 @@ def property_from_data( # noqa: PLR0911, PLR0912
190
191
  name=name,
191
192
  required=required,
192
193
  default=data.default,
193
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
194
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
194
195
  description=data.description,
195
196
  example=data.example,
196
197
  ),
@@ -230,7 +231,7 @@ def property_from_data( # noqa: PLR0911, PLR0912
230
231
  required=required,
231
232
  default=data.default,
232
233
  const=data.const,
233
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
234
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
234
235
  description=data.description,
235
236
  ),
236
237
  schemas,
@@ -246,7 +247,7 @@ def property_from_data( # noqa: PLR0911, PLR0912
246
247
  name=name,
247
248
  default=data.default,
248
249
  required=required,
249
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
250
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
250
251
  description=data.description,
251
252
  example=data.example,
252
253
  ),
@@ -258,7 +259,7 @@ def property_from_data( # noqa: PLR0911, PLR0912
258
259
  name=name,
259
260
  default=data.default,
260
261
  required=required,
261
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
262
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
262
263
  description=data.description,
263
264
  example=data.example,
264
265
  ),
@@ -270,7 +271,7 @@ def property_from_data( # noqa: PLR0911, PLR0912
270
271
  name=name,
271
272
  required=required,
272
273
  default=None,
273
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
274
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
274
275
  description=data.description,
275
276
  example=data.example,
276
277
  ),
@@ -303,7 +304,7 @@ def property_from_data( # noqa: PLR0911, PLR0912
303
304
  name=name,
304
305
  required=required,
305
306
  default=data.default,
306
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
307
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
307
308
  description=data.description,
308
309
  example=data.example,
309
310
  ),
@@ -313,11 +314,11 @@ def property_from_data( # noqa: PLR0911, PLR0912
313
314
 
314
315
  def _create_schemas(
315
316
  *,
316
- components: dict[str, oai.Reference | oai.Schema],
317
+ components: dict[UntrustedString, oai.Reference | oai.Schema],
317
318
  schemas: Schemas,
318
319
  config: Config,
319
320
  ) -> Schemas:
320
- to_process: Iterable[tuple[str, oai.Reference | oai.Schema]] = components.items()
321
+ to_process: Iterable[tuple[UntrustedString, oai.Reference | oai.Schema]] = components.items()
321
322
  still_making_progress = True
322
323
  errors: list[PropertyError] = []
323
324
 
@@ -325,12 +326,12 @@ def _create_schemas(
325
326
  while still_making_progress:
326
327
  still_making_progress = False
327
328
  errors = []
328
- next_round: list[tuple[str, oai.Reference | oai.Schema]] = []
329
+ next_round: list[tuple[UntrustedString, oai.Reference | oai.Schema]] = []
329
330
  # Only accumulate errors from the last round, since we might fix some along the way
330
331
  for name, data in to_process:
331
332
  schema_data: oai.Reference | oai.Schema | None = data
332
- ref_path = parse_reference_path(f"#/components/schemas/{name}")
333
- if isinstance(ref_path, ParseError):
333
+ ref_path = parse_reference_path(Ref(f"#/components/schemas/{name.get_untrusted_value()}"))
334
+ if isinstance(ref_path, ParseError): # pragma: no cover
334
335
  schemas.errors.append(PropertyError(detail=ref_path.detail, data=data))
335
336
  continue
336
337
  if isinstance(data, oai.Reference):
@@ -361,13 +362,13 @@ def _create_schemas(
361
362
  return schemas
362
363
 
363
364
 
364
- def _propogate_removal(*, root: ReferencePath | utils.ClassName, schemas: Schemas, error: PropertyError) -> None:
365
- if isinstance(root, utils.ClassName):
365
+ def _propogate_removal(*, root: ReferencePath | strings.ClassName, schemas: Schemas, error: PropertyError) -> None:
366
+ if isinstance(root, strings.ClassName):
366
367
  schemas.classes_by_name.pop(root, None)
367
368
  return
368
369
  if root in schemas.classes_by_reference:
369
370
  error.detail = error.detail or ""
370
- error.detail += f"\n{root}"
371
+ error.detail += f"\n{root.get_untrusted_value()}"
371
372
  del schemas.classes_by_reference[root]
372
373
  for child in schemas.dependencies.get(root, set()):
373
374
  _propogate_removal(root=child, schemas=schemas, error=error)
@@ -399,10 +400,10 @@ def _process_models(*, schemas: Schemas, config: Config) -> Schemas:
399
400
  for model_prop in to_process:
400
401
  schemas_or_err = process_model(model_prop, schemas=schemas, config=config)
401
402
  if isinstance(schemas_or_err, PropertyError):
402
- schemas_or_err.header = f"\nUnable to process schema {model_prop.name}:"
403
+ schemas_or_err.header = f"\nUnable to process schema {model_prop.name.get_untrusted_value()}:"
403
404
  if isinstance(schemas_or_err.data, oai.Reference) and schemas_or_err.data.ref.endswith(
404
405
  f"/{model_prop.class_info.name}"
405
- ):
406
+ ): # pragma: no cover
406
407
  schemas_or_err.detail = schemas_or_err.detail or ""
407
408
  schemas_or_err.detail += "\n\nRecursive allOf reference found"
408
409
  final_model_errors.append((model_prop, schemas_or_err))
@@ -421,7 +422,7 @@ def _process_models(*, schemas: Schemas, config: Config) -> Schemas:
421
422
 
422
423
  def build_schemas(
423
424
  *,
424
- components: dict[str, oai.Reference | oai.Schema],
425
+ components: dict[UntrustedString, oai.Reference | oai.Schema],
425
426
  schemas: Schemas,
426
427
  config: Config,
427
428
  ) -> Schemas:
@@ -433,12 +434,12 @@ def build_schemas(
433
434
 
434
435
  def build_parameters(
435
436
  *,
436
- components: dict[str, oai.Reference | oai.Parameter],
437
+ components: dict[UntrustedString, oai.Reference | oai.Parameter],
437
438
  parameters: Parameters,
438
439
  config: Config,
439
440
  ) -> Parameters:
440
441
  """Get a list of Parameters from an OpenAPI dict"""
441
- to_process: Iterable[tuple[str, oai.Reference | oai.Parameter]] = []
442
+ to_process: Iterable[tuple[UntrustedString, oai.Reference | oai.Parameter]] = []
442
443
  if components is not None:
443
444
  to_process = components.items()
444
445
  still_making_progress = True
@@ -451,17 +452,17 @@ def build_parameters(
451
452
  next_round = []
452
453
  # Only accumulate errors from the last round, since we might fix some along the way
453
454
  for name, data in to_process:
454
- if isinstance(data, oai.Reference):
455
+ if isinstance(data, oai.Reference): # pragma: no cover
455
456
  parameters.errors.append(ParameterError(data=data, detail="Reference parameters are not supported."))
456
457
  continue
457
- ref_path = parse_reference_path(f"#/components/parameters/{name}")
458
- if isinstance(ref_path, ParseError):
458
+ ref_path = parse_reference_path(Ref(f"#/components/parameters/{name.get_untrusted_value()}"))
459
+ if isinstance(ref_path, ParseError): # pragma: no cover
459
460
  parameters.errors.append(ParameterError(detail=ref_path.detail, data=data))
460
461
  continue
461
462
  parameters_or_err = update_parameters_with_data(
462
463
  ref_path=ref_path, data=data, parameters=parameters, config=config
463
464
  )
464
- if isinstance(parameters_or_err, ParameterError):
465
+ if isinstance(parameters_or_err, ParameterError): # pragma: no cover
465
466
  next_round.append((name, data))
466
467
  errors.append(parameters_or_err)
467
468
  continue
@@ -2,10 +2,11 @@ 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
8
- from .protocol import PropertyProtocol, Value
7
+ from ...schema.untrusted_string import UntrustedString
8
+ from ...strings import PythonCode, PythonIdentifier
9
+ from .protocol import PropertyProtocol, Value, convert_example
9
10
 
10
11
 
11
12
  @define
@@ -15,12 +16,12 @@ class AnyProperty(PropertyProtocol):
15
16
  @classmethod
16
17
  def build(
17
18
  cls,
18
- name: str,
19
+ name: UntrustedString,
19
20
  required: bool,
20
21
  default: Any,
21
22
  python_name: PythonIdentifier,
22
- description: str | None,
23
- example: str | None,
23
+ description: UntrustedString | None,
24
+ example: Any,
24
25
  ) -> AnyProperty:
25
26
  return cls(
26
27
  name=name,
@@ -39,13 +40,13 @@ class AnyProperty(PropertyProtocol):
39
40
  return value
40
41
  if isinstance(value, str):
41
42
  return StringProperty.convert_value(value)
42
- return Value(python_code=str(value), raw_value=value)
43
+ return Value(python_code=PythonCode(str(value)), raw_value=value)
43
44
 
44
- name: str
45
+ name: UntrustedString
45
46
  required: bool
46
47
  default: Value | None
47
48
  python_name: PythonIdentifier
48
- description: str | None
49
- example: str | None
49
+ description: UntrustedString | None
50
+ example: UntrustedString | None = field(converter=convert_example)
50
51
  _type_string: ClassVar[str] = "Any"
51
52
  _json_type_string: ClassVar[str] = "Any"
@@ -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 BooleanProperty(PropertyProtocol):
15
15
  """Property for bool"""
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] = "bool"
25
25
  _json_type_string: ClassVar[str] = "bool"
@@ -34,12 +34,12 @@ class BooleanProperty(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
  ) -> BooleanProperty | PropertyError:
44
44
  checked_default = cls.convert_value(default)
45
45
  if isinstance(checked_default, PropertyError):
@@ -54,14 +54,14 @@ class BooleanProperty(PropertyProtocol):
54
54
  )
55
55
 
56
56
  @classmethod
57
- def convert_value(cls, value: Any) -> Value | None | PropertyError:
57
+ def convert_value(cls, value: Any) -> Value | PropertyError | None:
58
58
  if isinstance(value, Value) or value is None:
59
59
  return value
60
60
  if isinstance(value, str):
61
61
  if value.lower() == "true":
62
- return Value(python_code="True", raw_value=value)
62
+ return Value(python_code=PythonCode("True"), raw_value=value)
63
63
  elif value.lower() == "false":
64
- return Value(python_code="False", raw_value=value)
64
+ return Value(python_code=PythonCode("False"), raw_value=value)
65
65
  if isinstance(value, bool):
66
- return Value(python_code=str(value), raw_value=value)
66
+ return Value(python_code=PythonCode(str(value)), raw_value=value)
67
67
  return PropertyError(f"Invalid boolean value: {value}")
@@ -4,7 +4,8 @@ from typing import Any, ClassVar, overload
4
4
 
5
5
  from attr import define
6
6
 
7
- from ...utils import PythonIdentifier
7
+ from ... import schema as oai
8
+ from ...strings import PythonCode, PythonIdentifier
8
9
  from ..errors import PropertyError
9
10
  from .protocol import PropertyProtocol, Value
10
11
  from .string import StringProperty
@@ -14,12 +15,12 @@ from .string import StringProperty
14
15
  class ConstProperty(PropertyProtocol):
15
16
  """A property representing a const value"""
16
17
 
17
- name: str
18
+ name: oai.UntrustedString
18
19
  required: bool
19
20
  value: Value
20
21
  default: Value | None
21
22
  python_name: PythonIdentifier
22
- description: str | None
23
+ description: oai.UntrustedString | None
23
24
  example: None
24
25
  template: ClassVar[str] = "const_property.py.jinja"
25
26
 
@@ -29,10 +30,10 @@ class ConstProperty(PropertyProtocol):
29
30
  *,
30
31
  const: str | int | float | bool,
31
32
  default: Any,
32
- name: str,
33
+ name: oai.UntrustedString,
33
34
  python_name: PythonIdentifier,
34
35
  required: bool,
35
- description: str | None,
36
+ description: oai.UntrustedString | None,
36
37
  ) -> ConstProperty | PropertyError:
37
38
  """
38
39
  Create a `ConstProperty` the right way.
@@ -62,7 +63,7 @@ class ConstProperty(PropertyProtocol):
62
63
  prop.default = converted_default
63
64
  return prop
64
65
 
65
- def convert_value(self, value: Any) -> Value | None | PropertyError:
66
+ def convert_value(self, value: Any) -> Value | PropertyError | None:
66
67
  value = self._convert_value(value)
67
68
  if value is None:
68
69
  return value
@@ -87,16 +88,16 @@ class ConstProperty(PropertyProtocol):
87
88
  return value
88
89
  if isinstance(value, str):
89
90
  return StringProperty.convert_value(value)
90
- return Value(python_code=str(value), raw_value=value)
91
+ return Value(python_code=PythonCode(str(value)), raw_value=value)
91
92
 
92
93
  def get_type_string(
93
94
  self,
94
95
  no_optional: bool = False,
95
96
  json: bool = False,
96
- ) -> str:
97
- lit = f"Literal[{self.value.python_code}]"
97
+ ) -> PythonCode:
98
+ lit = PythonCode(f"Literal[{self.value.python_code.as_unembedded_code()}]")
98
99
  if not no_optional and not self.required:
99
- return f"{lit} | Unset"
100
+ return PythonCode(f"{lit.as_unembedded_code()} | Unset")
100
101
  return lit
101
102
 
102
103
  def get_imports(self, *, prefix: str) -> set[str]:
@@ -3,23 +3,24 @@ from __future__ import annotations
3
3
  import datetime
4
4
  from typing import Any, ClassVar
5
5
 
6
- from attr import define
6
+ from attr import define, field
7
7
 
8
- from ...utils import PythonIdentifier
8
+ from ... import schema as oai
9
+ from ...strings import PythonCode, PythonIdentifier
9
10
  from ..errors import PropertyError
10
- from .protocol import PropertyProtocol, Value
11
+ from .protocol import PropertyProtocol, Value, convert_example
11
12
 
12
13
 
13
14
  @define
14
15
  class DateProperty(PropertyProtocol):
15
16
  """A property of type datetime.date"""
16
17
 
17
- name: str
18
+ name: oai.UntrustedString
18
19
  required: bool
19
20
  default: Value | None
20
21
  python_name: PythonIdentifier
21
- description: str | None
22
- example: str | None
22
+ description: oai.UntrustedString | None
23
+ example: oai.UntrustedString | None = field(converter=convert_example)
23
24
 
24
25
  _type_string: ClassVar[str] = "datetime.date"
25
26
  _json_type_string: ClassVar[str] = "str"
@@ -28,12 +29,12 @@ class DateProperty(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
  ) -> DateProperty | PropertyError:
38
39
  checked_default = cls.convert_value(default)
39
40
  if isinstance(checked_default, PropertyError):
@@ -49,7 +50,7 @@ class DateProperty(PropertyProtocol):
49
50
  )
50
51
 
51
52
  @classmethod
52
- def convert_value(cls, value: Any) -> Value | None | PropertyError:
53
+ def convert_value(cls, value: Any) -> Value | PropertyError | None:
53
54
  if isinstance(value, Value) or value is None:
54
55
  return value
55
56
  if isinstance(value, str):
@@ -57,7 +58,7 @@ class DateProperty(PropertyProtocol):
57
58
  datetime.date.fromisoformat(value) # make sure it's a valid value
58
59
  except ValueError as e:
59
60
  return PropertyError(f"Invalid date: {e}")
60
- return Value(python_code=f"datetime.date.fromisoformat({value!r})", raw_value=value)
61
+ return Value(python_code=PythonCode(f"datetime.date.fromisoformat({value!r})"), raw_value=value)
61
62
  return PropertyError(f"Cannot convert {value} to a date")
62
63
 
63
64
  def get_imports(self, *, prefix: str) -> set[str]:
@@ -3,11 +3,12 @@ from __future__ import annotations
3
3
  import datetime
4
4
  from typing import Any, ClassVar
5
5
 
6
- from attr import define
6
+ from attr import define, field
7
7
 
8
- from ...utils import PythonIdentifier
8
+ from ... import schema as oai
9
+ from ...strings import PythonCode, PythonIdentifier
9
10
  from ..errors import PropertyError
10
- from .protocol import PropertyProtocol, Value
11
+ from .protocol import PropertyProtocol, Value, convert_example
11
12
 
12
13
 
13
14
  @define
@@ -16,12 +17,12 @@ class DateTimeProperty(PropertyProtocol):
16
17
  A property of type datetime.datetime
17
18
  """
18
19
 
19
- name: str
20
+ name: oai.UntrustedString
20
21
  required: bool
21
22
  default: Value | None
22
23
  python_name: PythonIdentifier
23
- description: str | None
24
- example: str | None
24
+ description: oai.UntrustedString | None
25
+ example: oai.UntrustedString | None = field(converter=convert_example)
25
26
 
26
27
  _type_string: ClassVar[str] = "datetime.datetime"
27
28
  _json_type_string: ClassVar[str] = "str"
@@ -30,12 +31,12 @@ class DateTimeProperty(PropertyProtocol):
30
31
  @classmethod
31
32
  def build(
32
33
  cls,
33
- name: str,
34
+ name: oai.UntrustedString,
34
35
  required: bool,
35
36
  default: Any,
36
37
  python_name: PythonIdentifier,
37
- description: str | None,
38
- example: str | None,
38
+ description: oai.UntrustedString | None,
39
+ example: Any,
39
40
  ) -> DateTimeProperty | PropertyError:
40
41
  checked_default = cls.convert_value(default)
41
42
  if isinstance(checked_default, PropertyError):
@@ -51,7 +52,7 @@ class DateTimeProperty(PropertyProtocol):
51
52
  )
52
53
 
53
54
  @classmethod
54
- def convert_value(cls, value: Any) -> Value | None | PropertyError:
55
+ def convert_value(cls, value: Any) -> Value | PropertyError | None:
55
56
  if value is None or isinstance(value, Value):
56
57
  return value
57
58
  if isinstance(value, str):
@@ -59,7 +60,7 @@ class DateTimeProperty(PropertyProtocol):
59
60
  datetime.datetime.fromisoformat(value) # make sure it's a valid value
60
61
  except ValueError as e:
61
62
  return PropertyError(f"Invalid datetime: {e}")
62
- return Value(python_code=f"datetime.datetime.fromisoformat({value!r})", raw_value=value)
63
+ return Value(python_code=PythonCode(f"datetime.datetime.fromisoformat({value!r})"), raw_value=value)
63
64
  return PropertyError(f"Cannot convert {value} to a datetime")
64
65
 
65
66
  def get_imports(self, *, prefix: str) -> set[str]:
@@ -5,14 +5,14 @@ __all__ = ["EnumProperty", "ValueType"]
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 EnumProperty(PropertyProtocol):
24
24
  """A property that should use an 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: dict[str, ValueType]
33
33
  class_info: Class
34
34
  value_type: type[ValueType]
@@ -47,7 +47,7 @@ class EnumProperty(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,
@@ -83,7 +83,7 @@ class EnumProperty(PropertyProtocol):
83
83
  name=name,
84
84
  required=required,
85
85
  default="None",
86
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
86
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
87
87
  description=None,
88
88
  example=None,
89
89
  ),
@@ -115,9 +115,9 @@ class EnumProperty(PropertyProtocol):
115
115
  config=config,
116
116
  )
117
117
 
118
- class_name = data.title or name
118
+ class_name: str | oai.UntrustedString = data.title or name
119
119
  if parent_name:
120
- class_name = f"{utils.pascal_case(parent_name)}{utils.pascal_case(class_name)}"
120
+ class_name = f"{strings.pascal_case(parent_name)}{strings.pascal_case(class_name)}"
121
121
  class_info = Class.from_string(string=class_name, config=config)
122
122
  var_names = data.model_extra.get("x-enum-varnames", []) if data.model_extra else []
123
123
  values = EnumProperty.values_from_list(value_list, class_info, var_names)
@@ -139,7 +139,7 @@ class EnumProperty(PropertyProtocol):
139
139
  values=values,
140
140
  value_type=value_type,
141
141
  default=None,
142
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
142
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
143
143
  description=data.description,
144
144
  example=data.example,
145
145
  )
@@ -158,16 +158,19 @@ class EnumProperty(PropertyProtocol):
158
158
  if isinstance(value, self.value_type):
159
159
  inverse_values = {v: k for k, v in self.values.items()}
160
160
  try:
161
- return Value(python_code=f"{self.class_info.name}.{inverse_values[value]}", raw_value=value)
161
+ return Value(
162
+ python_code=strings.PythonCode(f"{self.class_info.name}.{inverse_values[value]}"),
163
+ raw_value=value,
164
+ )
162
165
  except KeyError:
163
166
  return PropertyError(detail=f"Value {value} is not valid for enum {self.name}")
164
167
  return PropertyError(detail=f"Cannot convert {value} to enum {self.name} of type {self.value_type}")
165
168
 
166
- def get_base_type_string(self) -> str:
167
- return self.class_info.name
169
+ def get_base_type_string(self) -> strings.PythonCode:
170
+ return strings.PythonCode(self.class_info.name)
168
171
 
169
- def get_base_json_type_string(self) -> str:
170
- return self.value_type.__name__
172
+ def get_base_json_type_string(self) -> strings.PythonCode:
173
+ return strings.PythonCode(self.value_type.__name__)
171
174
 
172
175
  def get_imports(self, *, prefix: str) -> set[str]:
173
176
  """
@@ -194,7 +197,7 @@ class EnumProperty(PropertyProtocol):
194
197
  if isinstance(value, int):
195
198
  if use_var_names:
196
199
  key = var_names[i]
197
- sanitized_key = utils.snake_case(key).upper()
200
+ sanitized_key = strings.snake_case(key).upper()
198
201
  output[sanitized_key] = value
199
202
  elif value < 0:
200
203
  output[f"VALUE_NEGATIVE_{-value}"] = value
@@ -212,6 +215,6 @@ class EnumProperty(PropertyProtocol):
212
215
  f"Duplicate key {key} in enum {class_info.module_name}.{class_info.name}; "
213
216
  f"consider setting literal_enums in your config"
214
217
  )
215
- sanitized_key = utils.snake_case(key).upper()
216
- output[sanitized_key] = utils.remove_string_escapes(value)
218
+ sanitized_key = strings.snake_case(key).upper()
219
+ output[sanitized_key] = strings.in_double_quote_literal(value)
217
220
  return output