openapi-python-client 0.29.0__py3-none-any.whl → 0.30.0__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 (91) hide show
  1. openapi_python_client/__init__.py +30 -21
  2. openapi_python_client/cli.py +10 -3
  3. openapi_python_client/config.py +4 -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 +36 -33
  7. openapi_python_client/parser/properties/any.py +12 -10
  8. openapi_python_client/parser/properties/boolean.py +14 -13
  9. openapi_python_client/parser/properties/const.py +11 -10
  10. openapi_python_client/parser/properties/date.py +13 -11
  11. openapi_python_client/parser/properties/datetime.py +13 -11
  12. openapi_python_client/parser/properties/enum_property.py +23 -20
  13. openapi_python_client/parser/properties/file.py +12 -10
  14. openapi_python_client/parser/properties/float.py +14 -13
  15. openapi_python_client/parser/properties/int.py +12 -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 +35 -34
  20. openapi_python_client/parser/properties/none.py +12 -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 +11 -11
  24. openapi_python_client/parser/properties/union.py +24 -22
  25. openapi_python_client/parser/properties/uuid.py +12 -11
  26. openapi_python_client/parser/responses.py +31 -24
  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 +9 -8
  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 +9 -9
  61. openapi_python_client/templates/client.py.jinja +31 -31
  62. openapi_python_client/templates/endpoint_macros.py.jinja +19 -19
  63. openapi_python_client/templates/endpoint_module.py.jinja +18 -21
  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 +3 -3
  80. openapi_python_client/templates/pyproject_poetry.toml.jinja +3 -3
  81. openapi_python_client/templates/pyproject_uv.toml.jinja +4 -4
  82. openapi_python_client/templates/setup.py.jinja +3 -3
  83. openapi_python_client/templates/str_enum.py.jinja +2 -2
  84. openapi_python_client/templates/types.py.jinja +2 -2
  85. {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.30.0.dist-info}/METADATA +10 -9
  86. openapi_python_client-0.30.0.dist-info/RECORD +115 -0
  87. {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.30.0.dist-info}/WHEEL +1 -1
  88. openapi_python_client/utils.py +0 -122
  89. openapi_python_client-0.29.0.dist-info/RECORD +0 -113
  90. {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.30.0.dist-info}/entry_points.txt +0 -0
  91. {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.30.0.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,17 @@ def _string_based_property(
103
105
 
104
106
 
105
107
  def _property_from_ref(
106
- name: str,
108
+ *,
109
+ name: UntrustedString,
107
110
  required: bool,
108
111
  parent: oai.Schema | None,
109
112
  data: oai.Reference,
110
113
  schemas: Schemas,
111
114
  config: Config,
112
- roots: set[ReferencePath | utils.ClassName],
115
+ roots: set[ReferencePath | strings.ClassName],
113
116
  ) -> tuple[Property | PropertyError, Schemas]:
114
117
  ref_path = parse_reference_path(data.ref)
115
- if isinstance(ref_path, ParseError):
118
+ if isinstance(ref_path, ParseError): # pragma: no cover
116
119
  return PropertyError(data=data, detail=ref_path.detail), schemas
117
120
  existing = schemas.classes_by_reference.get(ref_path)
118
121
  if not existing:
@@ -130,7 +133,7 @@ def _property_from_ref(
130
133
  existing,
131
134
  required=required,
132
135
  name=name,
133
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
136
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
134
137
  default=default, # type: ignore # mypy can't tell that default comes from the same class...
135
138
  )
136
139
 
@@ -139,18 +142,18 @@ def _property_from_ref(
139
142
 
140
143
 
141
144
  def property_from_data( # noqa: PLR0911, PLR0912
142
- name: str,
145
+ *,
146
+ name: UntrustedString,
143
147
  required: bool,
144
148
  data: oai.Reference | oai.Schema,
145
149
  schemas: Schemas,
146
150
  parent_name: str,
147
151
  config: Config,
148
152
  process_properties: bool = True,
149
- roots: set[ReferencePath | utils.ClassName] | None = None,
153
+ roots: set[ReferencePath | strings.ClassName] | None = None,
150
154
  ) -> tuple[Property | PropertyError, Schemas]:
151
155
  """Generate a Property from the OpenAPI dictionary representation of it"""
152
156
  roots = roots or set()
153
- name = utils.remove_string_escapes(name)
154
157
  if isinstance(data, oai.Reference):
155
158
  return _property_from_ref(
156
159
  name=name,
@@ -190,7 +193,7 @@ def property_from_data( # noqa: PLR0911, PLR0912
190
193
  name=name,
191
194
  required=required,
192
195
  default=data.default,
193
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
196
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
194
197
  description=data.description,
195
198
  example=data.example,
196
199
  ),
@@ -230,7 +233,7 @@ def property_from_data( # noqa: PLR0911, PLR0912
230
233
  required=required,
231
234
  default=data.default,
232
235
  const=data.const,
233
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
236
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
234
237
  description=data.description,
235
238
  ),
236
239
  schemas,
@@ -246,7 +249,7 @@ def property_from_data( # noqa: PLR0911, PLR0912
246
249
  name=name,
247
250
  default=data.default,
248
251
  required=required,
249
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
252
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
250
253
  description=data.description,
251
254
  example=data.example,
252
255
  ),
@@ -258,7 +261,7 @@ def property_from_data( # noqa: PLR0911, PLR0912
258
261
  name=name,
259
262
  default=data.default,
260
263
  required=required,
261
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
264
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
262
265
  description=data.description,
263
266
  example=data.example,
264
267
  ),
@@ -270,7 +273,7 @@ def property_from_data( # noqa: PLR0911, PLR0912
270
273
  name=name,
271
274
  required=required,
272
275
  default=None,
273
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
276
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
274
277
  description=data.description,
275
278
  example=data.example,
276
279
  ),
@@ -303,7 +306,7 @@ def property_from_data( # noqa: PLR0911, PLR0912
303
306
  name=name,
304
307
  required=required,
305
308
  default=data.default,
306
- python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
309
+ python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
307
310
  description=data.description,
308
311
  example=data.example,
309
312
  ),
@@ -313,11 +316,11 @@ def property_from_data( # noqa: PLR0911, PLR0912
313
316
 
314
317
  def _create_schemas(
315
318
  *,
316
- components: dict[str, oai.Reference | oai.Schema],
319
+ components: dict[UntrustedString, oai.Reference | oai.Schema],
317
320
  schemas: Schemas,
318
321
  config: Config,
319
322
  ) -> Schemas:
320
- to_process: Iterable[tuple[str, oai.Reference | oai.Schema]] = components.items()
323
+ to_process: Iterable[tuple[UntrustedString, oai.Reference | oai.Schema]] = components.items()
321
324
  still_making_progress = True
322
325
  errors: list[PropertyError] = []
323
326
 
@@ -325,12 +328,12 @@ def _create_schemas(
325
328
  while still_making_progress:
326
329
  still_making_progress = False
327
330
  errors = []
328
- next_round: list[tuple[str, oai.Reference | oai.Schema]] = []
331
+ next_round: list[tuple[UntrustedString, oai.Reference | oai.Schema]] = []
329
332
  # Only accumulate errors from the last round, since we might fix some along the way
330
333
  for name, data in to_process:
331
334
  schema_data: oai.Reference | oai.Schema | None = data
332
- ref_path = parse_reference_path(f"#/components/schemas/{name}")
333
- if isinstance(ref_path, ParseError):
335
+ ref_path = parse_reference_path(Ref(f"#/components/schemas/{name.get_untrusted_value()}"))
336
+ if isinstance(ref_path, ParseError): # pragma: no cover
334
337
  schemas.errors.append(PropertyError(detail=ref_path.detail, data=data))
335
338
  continue
336
339
  if isinstance(data, oai.Reference):
@@ -361,13 +364,13 @@ def _create_schemas(
361
364
  return schemas
362
365
 
363
366
 
364
- def _propogate_removal(*, root: ReferencePath | utils.ClassName, schemas: Schemas, error: PropertyError) -> None:
365
- if isinstance(root, utils.ClassName):
367
+ def _propogate_removal(*, root: ReferencePath | strings.ClassName, schemas: Schemas, error: PropertyError) -> None:
368
+ if isinstance(root, strings.ClassName):
366
369
  schemas.classes_by_name.pop(root, None)
367
370
  return
368
371
  if root in schemas.classes_by_reference:
369
372
  error.detail = error.detail or ""
370
- error.detail += f"\n{root}"
373
+ error.detail += f"\n{root.get_untrusted_value()}"
371
374
  del schemas.classes_by_reference[root]
372
375
  for child in schemas.dependencies.get(root, set()):
373
376
  _propogate_removal(root=child, schemas=schemas, error=error)
@@ -399,10 +402,10 @@ def _process_models(*, schemas: Schemas, config: Config) -> Schemas:
399
402
  for model_prop in to_process:
400
403
  schemas_or_err = process_model(model_prop, schemas=schemas, config=config)
401
404
  if isinstance(schemas_or_err, PropertyError):
402
- schemas_or_err.header = f"\nUnable to process schema {model_prop.name}:"
405
+ schemas_or_err.header = f"\nUnable to process schema {model_prop.name.get_untrusted_value()}:"
403
406
  if isinstance(schemas_or_err.data, oai.Reference) and schemas_or_err.data.ref.endswith(
404
407
  f"/{model_prop.class_info.name}"
405
- ):
408
+ ): # pragma: no cover
406
409
  schemas_or_err.detail = schemas_or_err.detail or ""
407
410
  schemas_or_err.detail += "\n\nRecursive allOf reference found"
408
411
  final_model_errors.append((model_prop, schemas_or_err))
@@ -421,7 +424,7 @@ def _process_models(*, schemas: Schemas, config: Config) -> Schemas:
421
424
 
422
425
  def build_schemas(
423
426
  *,
424
- components: dict[str, oai.Reference | oai.Schema],
427
+ components: dict[UntrustedString, oai.Reference | oai.Schema],
425
428
  schemas: Schemas,
426
429
  config: Config,
427
430
  ) -> Schemas:
@@ -433,12 +436,12 @@ def build_schemas(
433
436
 
434
437
  def build_parameters(
435
438
  *,
436
- components: dict[str, oai.Reference | oai.Parameter],
439
+ components: dict[UntrustedString, oai.Reference | oai.Parameter],
437
440
  parameters: Parameters,
438
441
  config: Config,
439
442
  ) -> Parameters:
440
443
  """Get a list of Parameters from an OpenAPI dict"""
441
- to_process: Iterable[tuple[str, oai.Reference | oai.Parameter]] = []
444
+ to_process: Iterable[tuple[UntrustedString, oai.Reference | oai.Parameter]] = []
442
445
  if components is not None:
443
446
  to_process = components.items()
444
447
  still_making_progress = True
@@ -451,17 +454,17 @@ def build_parameters(
451
454
  next_round = []
452
455
  # Only accumulate errors from the last round, since we might fix some along the way
453
456
  for name, data in to_process:
454
- if isinstance(data, oai.Reference):
457
+ if isinstance(data, oai.Reference): # pragma: no cover
455
458
  parameters.errors.append(ParameterError(data=data, detail="Reference parameters are not supported."))
456
459
  continue
457
- ref_path = parse_reference_path(f"#/components/parameters/{name}")
458
- if isinstance(ref_path, ParseError):
460
+ ref_path = parse_reference_path(Ref(f"#/components/parameters/{name.get_untrusted_value()}"))
461
+ if isinstance(ref_path, ParseError): # pragma: no cover
459
462
  parameters.errors.append(ParameterError(detail=ref_path.detail, data=data))
460
463
  continue
461
464
  parameters_or_err = update_parameters_with_data(
462
465
  ref_path=ref_path, data=data, parameters=parameters, config=config
463
466
  )
464
- if isinstance(parameters_or_err, ParameterError):
467
+ if isinstance(parameters_or_err, ParameterError): # pragma: no cover
465
468
  next_round.append((name, data))
466
469
  errors.append(parameters_or_err)
467
470
  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,13 @@ class AnyProperty(PropertyProtocol):
15
16
  @classmethod
16
17
  def build(
17
18
  cls,
18
- name: str,
19
+ *,
20
+ name: UntrustedString,
19
21
  required: bool,
20
22
  default: Any,
21
23
  python_name: PythonIdentifier,
22
- description: str | None,
23
- example: str | None,
24
+ description: UntrustedString | None,
25
+ example: Any,
24
26
  ) -> AnyProperty:
25
27
  return cls(
26
28
  name=name,
@@ -39,13 +41,13 @@ class AnyProperty(PropertyProtocol):
39
41
  return value
40
42
  if isinstance(value, str):
41
43
  return StringProperty.convert_value(value)
42
- return Value(python_code=str(value), raw_value=value)
44
+ return Value(python_code=PythonCode(str(value)), raw_value=value)
43
45
 
44
- name: str
46
+ name: UntrustedString
45
47
  required: bool
46
48
  default: Value | None
47
49
  python_name: PythonIdentifier
48
- description: str | None
49
- example: str | None
50
+ description: UntrustedString | None
51
+ example: UntrustedString | None = field(converter=convert_example)
50
52
  _type_string: ClassVar[str] = "Any"
51
53
  _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,13 @@ class BooleanProperty(PropertyProtocol):
34
34
  @classmethod
35
35
  def build(
36
36
  cls,
37
- name: str,
37
+ *,
38
+ name: oai.UntrustedString,
38
39
  required: bool,
39
40
  default: Any,
40
41
  python_name: PythonIdentifier,
41
- description: str | None,
42
- example: str | None,
42
+ description: oai.UntrustedString | None,
43
+ example: Any,
43
44
  ) -> BooleanProperty | PropertyError:
44
45
  checked_default = cls.convert_value(default)
45
46
  if isinstance(checked_default, PropertyError):
@@ -54,14 +55,14 @@ class BooleanProperty(PropertyProtocol):
54
55
  )
55
56
 
56
57
  @classmethod
57
- def convert_value(cls, value: Any) -> Value | None | PropertyError:
58
+ def convert_value(cls, value: Any) -> Value | PropertyError | None:
58
59
  if isinstance(value, Value) or value is None:
59
60
  return value
60
61
  if isinstance(value, str):
61
62
  if value.lower() == "true":
62
- return Value(python_code="True", raw_value=value)
63
+ return Value(python_code=PythonCode("True"), raw_value=value)
63
64
  elif value.lower() == "false":
64
- return Value(python_code="False", raw_value=value)
65
+ return Value(python_code=PythonCode("False"), raw_value=value)
65
66
  if isinstance(value, bool):
66
- return Value(python_code=str(value), raw_value=value)
67
+ return Value(python_code=PythonCode(str(value)), raw_value=value)
67
68
  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,13 @@ class DateProperty(PropertyProtocol):
28
29
  @classmethod
29
30
  def build(
30
31
  cls,
31
- name: str,
32
+ *,
33
+ name: oai.UntrustedString,
32
34
  required: bool,
33
35
  default: Any,
34
36
  python_name: PythonIdentifier,
35
- description: str | None,
36
- example: str | None,
37
+ description: oai.UntrustedString | None,
38
+ example: Any,
37
39
  ) -> DateProperty | PropertyError:
38
40
  checked_default = cls.convert_value(default)
39
41
  if isinstance(checked_default, PropertyError):
@@ -49,7 +51,7 @@ class DateProperty(PropertyProtocol):
49
51
  )
50
52
 
51
53
  @classmethod
52
- def convert_value(cls, value: Any) -> Value | None | PropertyError:
54
+ def convert_value(cls, value: Any) -> Value | PropertyError | None:
53
55
  if isinstance(value, Value) or value is None:
54
56
  return value
55
57
  if isinstance(value, str):
@@ -57,7 +59,7 @@ class DateProperty(PropertyProtocol):
57
59
  datetime.date.fromisoformat(value) # make sure it's a valid value
58
60
  except ValueError as e:
59
61
  return PropertyError(f"Invalid date: {e}")
60
- return Value(python_code=f"datetime.date.fromisoformat({value!r})", raw_value=value)
62
+ return Value(python_code=PythonCode(f"datetime.date.fromisoformat({value!r})"), raw_value=value)
61
63
  return PropertyError(f"Cannot convert {value} to a date")
62
64
 
63
65
  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,13 @@ class DateTimeProperty(PropertyProtocol):
30
31
  @classmethod
31
32
  def build(
32
33
  cls,
33
- name: str,
34
+ *,
35
+ name: oai.UntrustedString,
34
36
  required: bool,
35
37
  default: Any,
36
38
  python_name: PythonIdentifier,
37
- description: str | None,
38
- example: str | None,
39
+ description: oai.UntrustedString | None,
40
+ example: Any,
39
41
  ) -> DateTimeProperty | PropertyError:
40
42
  checked_default = cls.convert_value(default)
41
43
  if isinstance(checked_default, PropertyError):
@@ -51,7 +53,7 @@ class DateTimeProperty(PropertyProtocol):
51
53
  )
52
54
 
53
55
  @classmethod
54
- def convert_value(cls, value: Any) -> Value | None | PropertyError:
56
+ def convert_value(cls, value: Any) -> Value | PropertyError | None:
55
57
  if value is None or isinstance(value, Value):
56
58
  return value
57
59
  if isinstance(value, str):
@@ -59,7 +61,7 @@ class DateTimeProperty(PropertyProtocol):
59
61
  datetime.datetime.fromisoformat(value) # make sure it's a valid value
60
62
  except ValueError as e:
61
63
  return PropertyError(f"Invalid datetime: {e}")
62
- return Value(python_code=f"datetime.datetime.fromisoformat({value!r})", raw_value=value)
64
+ return Value(python_code=PythonCode(f"datetime.datetime.fromisoformat({value!r})"), raw_value=value)
63
65
  return PropertyError(f"Cannot convert {value} to a datetime")
64
66
 
65
67
  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