openapi-python-client 0.21.5__tar.gz → 0.21.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/PKG-INFO +14 -2
  2. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/README.md +11 -0
  3. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/__init__.py +5 -1
  4. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/config.py +3 -0
  5. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/openapi.py +5 -2
  6. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/__init__.py +22 -1
  7. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/enum_property.py +6 -3
  8. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/list_property.py +19 -3
  9. openapi_python_client-0.21.6/openapi_python_client/parser/properties/literal_enum_property.py +191 -0
  10. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/merge_properties.py +30 -0
  11. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/property.py +4 -0
  12. openapi_python_client-0.21.6/openapi_python_client/parser/properties/uuid.py +80 -0
  13. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/schema.py +1 -0
  14. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/client.py.jinja +1 -1
  15. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/endpoint_module.py.jinja +2 -2
  16. openapi_python_client-0.21.6/openapi_python_client/templates/literal_enum.py.jinja +10 -0
  17. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/model.py.jinja +1 -1
  18. openapi_python_client-0.21.6/openapi_python_client/templates/property_templates/literal_enum_property.py.jinja +38 -0
  19. openapi_python_client-0.21.6/openapi_python_client/templates/property_templates/uuid_property.py.jinja +38 -0
  20. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/pyproject.toml +5 -4
  21. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/.gitignore +0 -0
  22. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/LICENSE +0 -0
  23. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/__main__.py +0 -0
  24. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/cli.py +0 -0
  25. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/__init__.py +0 -0
  26. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/bodies.py +0 -0
  27. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/errors.py +0 -0
  28. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/any.py +0 -0
  29. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/boolean.py +0 -0
  30. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/const.py +0 -0
  31. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/date.py +0 -0
  32. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/datetime.py +0 -0
  33. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/file.py +0 -0
  34. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/float.py +0 -0
  35. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/int.py +0 -0
  36. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/model_property.py +0 -0
  37. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/none.py +0 -0
  38. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/protocol.py +0 -0
  39. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/schemas.py +0 -0
  40. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/string.py +0 -0
  41. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/properties/union.py +0 -0
  42. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/parser/responses.py +0 -0
  43. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/py.typed +0 -0
  44. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/3.0.3.md +0 -0
  45. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/3.1.0.md +0 -0
  46. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/__init__.py +0 -0
  47. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/data_type.py +0 -0
  48. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/LICENSE +0 -0
  49. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/README.md +0 -0
  50. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/__init__.py +0 -0
  51. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/callback.py +0 -0
  52. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/components.py +0 -0
  53. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/contact.py +0 -0
  54. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/discriminator.py +0 -0
  55. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/encoding.py +0 -0
  56. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/example.py +0 -0
  57. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/external_documentation.py +0 -0
  58. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/header.py +0 -0
  59. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/info.py +0 -0
  60. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/license.py +0 -0
  61. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/link.py +0 -0
  62. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/media_type.py +0 -0
  63. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/oauth_flow.py +0 -0
  64. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/oauth_flows.py +0 -0
  65. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/open_api.py +0 -0
  66. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/operation.py +0 -0
  67. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/parameter.py +0 -0
  68. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/path_item.py +0 -0
  69. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/paths.py +0 -0
  70. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/reference.py +0 -0
  71. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/request_body.py +0 -0
  72. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/response.py +0 -0
  73. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/responses.py +0 -0
  74. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/security_requirement.py +0 -0
  75. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/security_scheme.py +0 -0
  76. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/server.py +0 -0
  77. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/server_variable.py +0 -0
  78. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/tag.py +0 -0
  79. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/openapi_schema_pydantic/xml.py +0 -0
  80. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/schema/parameter_location.py +0 -0
  81. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/.gitignore.jinja +0 -0
  82. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/README.md.jinja +0 -0
  83. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/api_init.py.jinja +0 -0
  84. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/endpoint_init.py.jinja +0 -0
  85. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/endpoint_macros.py.jinja +0 -0
  86. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/errors.py.jinja +0 -0
  87. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/helpers.jinja +0 -0
  88. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/int_enum.py.jinja +0 -0
  89. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/models_init.py.jinja +0 -0
  90. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/package_init.py.jinja +0 -0
  91. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/property_templates/any_property.py.jinja +0 -0
  92. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/property_templates/boolean_property.py.jinja +0 -0
  93. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/property_templates/const_property.py.jinja +0 -0
  94. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/property_templates/date_property.py.jinja +0 -0
  95. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/property_templates/datetime_property.py.jinja +0 -0
  96. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/property_templates/enum_property.py.jinja +0 -0
  97. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/property_templates/file_property.py.jinja +0 -0
  98. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/property_templates/float_property.py.jinja +0 -0
  99. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/property_templates/helpers.jinja +0 -0
  100. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/property_templates/int_property.py.jinja +0 -0
  101. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/property_templates/list_property.py.jinja +0 -0
  102. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/property_templates/model_property.py.jinja +0 -0
  103. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/property_templates/property_macros.py.jinja +0 -0
  104. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/property_templates/union_property.py.jinja +0 -0
  105. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/pyproject.toml.jinja +0 -0
  106. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/pyproject_ruff.toml.jinja +0 -0
  107. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/setup.py.jinja +0 -0
  108. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/str_enum.py.jinja +0 -0
  109. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/templates/types.py.jinja +0 -0
  110. {openapi_python_client-0.21.5 → openapi_python_client-0.21.6}/openapi_python_client/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: openapi-python-client
3
- Version: 0.21.5
3
+ Version: 0.21.6
4
4
  Summary: Generate modern Python clients from OpenAPI
5
5
  Project-URL: repository, https://github.com/openapi-generators/openapi-python-client
6
6
  Author-email: Dylan Anthony <contact@dylananthony.com>
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.9
16
16
  Classifier: Programming Language :: Python :: 3.10
17
17
  Classifier: Programming Language :: Python :: 3.11
18
18
  Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
19
20
  Classifier: Topic :: Software Development :: Code Generators
20
21
  Classifier: Typing :: Typed
21
22
  Requires-Python: <4.0,>=3.8.1
@@ -26,7 +27,7 @@ Requires-Dist: jinja2<4.0.0,>=3.0.0
26
27
  Requires-Dist: pydantic<3.0.0,>=2.1.1
27
28
  Requires-Dist: python-dateutil<3.0.0,>=2.8.1
28
29
  Requires-Dist: ruamel-yaml<0.19.0,>=0.18.6
29
- Requires-Dist: ruff<0.7,>=0.2
30
+ Requires-Dist: ruff<0.8,>=0.2
30
31
  Requires-Dist: shellingham<2.0.0,>=1.3.2
31
32
  Requires-Dist: typer<0.13,>0.6
32
33
  Requires-Dist: typing-extensions<5.0.0,>=4.8.0
@@ -131,6 +132,17 @@ class_overrides:
131
132
 
132
133
  The easiest way to find what needs to be overridden is probably to generate your client and go look at everything in the `models` folder.
133
134
 
135
+ ### literal_enums
136
+
137
+ By default, `openapi-python-client` generates classes inheriting for `Enum` for enums. It can instead use `Literal`
138
+ values for enums by setting this to `true`:
139
+
140
+ ```yaml
141
+ literal_enums: true
142
+ ```
143
+
144
+ This is especially useful if enum values, when transformed to their Python names, end up conflicting due to case sensitivity or special symbols.
145
+
134
146
  ### project_name_override and package_name_override
135
147
 
136
148
  Used to change the name of generated client library project/package. If the project name is changed but an override for the package name
@@ -97,6 +97,17 @@ class_overrides:
97
97
 
98
98
  The easiest way to find what needs to be overridden is probably to generate your client and go look at everything in the `models` folder.
99
99
 
100
+ ### literal_enums
101
+
102
+ By default, `openapi-python-client` generates classes inheriting for `Enum` for enums. It can instead use `Literal`
103
+ values for enums by setting this to `true`:
104
+
105
+ ```yaml
106
+ literal_enums: true
107
+ ```
108
+
109
+ This is especially useful if enum values, when transformed to their Python names, end up conflicting due to case sensitivity or special symbols.
110
+
100
111
  ### project_name_override and package_name_override
101
112
 
102
113
  Used to change the name of generated client library project/package. If the project name is changed but an override for the package name
@@ -20,6 +20,7 @@ from openapi_python_client import utils
20
20
  from .config import Config, MetaType
21
21
  from .parser import GeneratorData, import_string_from_class
22
22
  from .parser.errors import ErrorLevel, GeneratorError
23
+ from .parser.properties import LiteralEnumProperty
23
24
 
24
25
  __version__ = version(__package__)
25
26
 
@@ -227,9 +228,12 @@ class Project:
227
228
  # Generate enums
228
229
  str_enum_template = self.env.get_template("str_enum.py.jinja")
229
230
  int_enum_template = self.env.get_template("int_enum.py.jinja")
231
+ literal_enum_template = self.env.get_template("literal_enum.py.jinja")
230
232
  for enum in self.openapi.enums:
231
233
  module_path = models_dir / f"{enum.class_info.module_name}.py"
232
- if enum.value_type is int:
234
+ if isinstance(enum, LiteralEnumProperty):
235
+ module_path.write_text(literal_enum_template.render(enum=enum), encoding=self.config.file_encoding)
236
+ elif enum.value_type is int:
233
237
  module_path.write_text(int_enum_template.render(enum=enum), encoding=self.config.file_encoding)
234
238
  else:
235
239
  module_path.write_text(str_enum_template.render(enum=enum), encoding=self.config.file_encoding)
@@ -43,6 +43,7 @@ class ConfigFile(BaseModel):
43
43
  post_hooks: Optional[List[str]] = None
44
44
  field_prefix: str = "field_"
45
45
  http_timeout: int = 5
46
+ literal_enums: bool = False
46
47
 
47
48
  @staticmethod
48
49
  def load_from_path(path: Path) -> "ConfigFile":
@@ -70,6 +71,7 @@ class Config:
70
71
  post_hooks: List[str]
71
72
  field_prefix: str
72
73
  http_timeout: int
74
+ literal_enums: bool
73
75
  document_source: Union[Path, str]
74
76
  file_encoding: str
75
77
  content_type_overrides: Dict[str, str]
@@ -109,6 +111,7 @@ class Config:
109
111
  post_hooks=post_hooks,
110
112
  field_prefix=config_file.field_prefix,
111
113
  http_timeout=config_file.http_timeout,
114
+ literal_enums=config_file.literal_enums,
112
115
  document_source=document_source,
113
116
  file_encoding=file_encoding,
114
117
  overwrite=overwrite,
@@ -15,6 +15,7 @@ from .errors import GeneratorError, ParseError, PropertyError
15
15
  from .properties import (
16
16
  Class,
17
17
  EnumProperty,
18
+ LiteralEnumProperty,
18
19
  ModelProperty,
19
20
  Parameters,
20
21
  Property,
@@ -488,7 +489,7 @@ class GeneratorData:
488
489
  models: Iterator[ModelProperty]
489
490
  errors: List[ParseError]
490
491
  endpoint_collections_by_tag: Dict[utils.PythonIdentifier, EndpointCollection]
491
- enums: Iterator[EnumProperty]
492
+ enums: Iterator[Union[EnumProperty, LiteralEnumProperty]]
492
493
 
493
494
  @staticmethod
494
495
  def from_dict(data: Dict[str, Any], *, config: Config) -> Union["GeneratorData", GeneratorError]:
@@ -517,7 +518,9 @@ class GeneratorData:
517
518
  data=openapi.paths, schemas=schemas, parameters=parameters, request_bodies=request_bodies, config=config
518
519
  )
519
520
 
520
- enums = (prop for prop in schemas.classes_by_name.values() if isinstance(prop, EnumProperty))
521
+ enums = (
522
+ prop for prop in schemas.classes_by_name.values() if isinstance(prop, (EnumProperty, LiteralEnumProperty))
523
+ )
521
524
  models = (prop for prop in schemas.classes_by_name.values() if isinstance(prop, ModelProperty))
522
525
 
523
526
  return GeneratorData(
@@ -4,6 +4,7 @@ __all__ = [
4
4
  "AnyProperty",
5
5
  "Class",
6
6
  "EnumProperty",
7
+ "LiteralEnumProperty",
7
8
  "ModelProperty",
8
9
  "Parameters",
9
10
  "Property",
@@ -30,6 +31,7 @@ from .file import FileProperty
30
31
  from .float import FloatProperty
31
32
  from .int import IntProperty
32
33
  from .list_property import ListProperty
34
+ from .literal_enum_property import LiteralEnumProperty
33
35
  from .model_property import ModelProperty, process_model
34
36
  from .none import NoneProperty
35
37
  from .property import Property
@@ -44,11 +46,12 @@ from .schemas import (
44
46
  )
45
47
  from .string import StringProperty
46
48
  from .union import UnionProperty
49
+ from .uuid import UuidProperty
47
50
 
48
51
 
49
52
  def _string_based_property(
50
53
  name: str, required: bool, data: oai.Schema, config: Config
51
- ) -> StringProperty | DateProperty | DateTimeProperty | FileProperty | PropertyError:
54
+ ) -> StringProperty | DateProperty | DateTimeProperty | FileProperty | UuidProperty | PropertyError:
52
55
  """Construct a Property from the type "string" """
53
56
  string_format = data.schema_format
54
57
  python_name = utils.PythonIdentifier(value=name, prefix=config.field_prefix)
@@ -79,6 +82,15 @@ def _string_based_property(
79
82
  description=data.description,
80
83
  example=data.example,
81
84
  )
85
+ if string_format == "uuid":
86
+ return UuidProperty.build(
87
+ name=name,
88
+ required=required,
89
+ default=data.default,
90
+ python_name=python_name,
91
+ description=data.description,
92
+ example=data.example,
93
+ )
82
94
  return StringProperty.build(
83
95
  name=name,
84
96
  default=data.default,
@@ -184,6 +196,15 @@ def property_from_data( # noqa: PLR0911, PLR0912
184
196
  schemas,
185
197
  )
186
198
  if data.enum:
199
+ if config.literal_enums:
200
+ return LiteralEnumProperty.build(
201
+ data=data,
202
+ name=name,
203
+ required=required,
204
+ schemas=schemas,
205
+ parent_name=parent_name,
206
+ config=config,
207
+ )
187
208
  return EnumProperty.build(
188
209
  data=data,
189
210
  name=name,
@@ -121,7 +121,7 @@ class EnumProperty(PropertyProtocol):
121
121
  if parent_name:
122
122
  class_name = f"{utils.pascal_case(parent_name)}{utils.pascal_case(class_name)}"
123
123
  class_info = Class.from_string(string=class_name, config=config)
124
- values = EnumProperty.values_from_list(value_list)
124
+ values = EnumProperty.values_from_list(value_list, class_info)
125
125
 
126
126
  if class_info.name in schemas.classes_by_name:
127
127
  existing = schemas.classes_by_name[class_info.name]
@@ -183,7 +183,7 @@ class EnumProperty(PropertyProtocol):
183
183
  return imports
184
184
 
185
185
  @staticmethod
186
- def values_from_list(values: list[str] | list[int]) -> dict[str, ValueType]:
186
+ def values_from_list(values: list[str] | list[int], class_info: Class) -> dict[str, ValueType]:
187
187
  """Convert a list of values into dict of {name: value}, where value can sometimes be None"""
188
188
  output: dict[str, ValueType] = {}
189
189
 
@@ -200,7 +200,10 @@ class EnumProperty(PropertyProtocol):
200
200
  else:
201
201
  key = f"VALUE_{i}"
202
202
  if key in output:
203
- raise ValueError(f"Duplicate key {key} in Enum")
203
+ raise ValueError(
204
+ f"Duplicate key {key} in enum {class_info.module_name}.{class_info.name}; "
205
+ f"consider setting literal_enums in your config"
206
+ )
204
207
  sanitized_key = utils.snake_case(key).upper()
205
208
  output[sanitized_key] = utils.remove_string_escapes(value)
206
209
  return output
@@ -58,12 +58,28 @@ class ListProperty(PropertyProtocol):
58
58
  """
59
59
  from . import property_from_data
60
60
 
61
- if data.items is None:
62
- return PropertyError(data=data, detail="type array must have items defined"), schemas
61
+ if data.items is None and not data.prefixItems:
62
+ return (
63
+ PropertyError(
64
+ data=data,
65
+ detail="type array must have items or prefixItems defined",
66
+ ),
67
+ schemas,
68
+ )
69
+
70
+ items = data.prefixItems or []
71
+ if data.items:
72
+ items.append(data.items)
73
+
74
+ if len(items) == 1:
75
+ inner_schema = items[0]
76
+ else:
77
+ inner_schema = oai.Schema(anyOf=items)
78
+
63
79
  inner_prop, schemas = property_from_data(
64
80
  name=f"{name}_item",
65
81
  required=True,
66
- data=data.items,
82
+ data=inner_schema,
67
83
  schemas=schemas,
68
84
  parent_name=parent_name,
69
85
  config=config,
@@ -0,0 +1,191 @@
1
+ from __future__ import annotations
2
+
3
+ __all__ = ["LiteralEnumProperty"]
4
+
5
+ from typing import Any, ClassVar, List, Union, cast
6
+
7
+ from attr import evolve
8
+ from attrs import define
9
+
10
+ from ... import Config, utils
11
+ from ... import schema as oai
12
+ from ...schema import DataType
13
+ from ..errors import PropertyError
14
+ from .none import NoneProperty
15
+ from .protocol import PropertyProtocol, Value
16
+ from .schemas import Class, Schemas
17
+ from .union import UnionProperty
18
+
19
+ ValueType = Union[str, int]
20
+
21
+
22
+ @define
23
+ class LiteralEnumProperty(PropertyProtocol):
24
+ """A property that should use a literal enum"""
25
+
26
+ name: str
27
+ required: bool
28
+ default: Value | None
29
+ python_name: utils.PythonIdentifier
30
+ description: str | None
31
+ example: str | None
32
+ values: set[ValueType]
33
+ class_info: Class
34
+ value_type: type[ValueType]
35
+
36
+ template: ClassVar[str] = "literal_enum_property.py.jinja"
37
+
38
+ _allowed_locations: ClassVar[set[oai.ParameterLocation]] = {
39
+ oai.ParameterLocation.QUERY,
40
+ oai.ParameterLocation.PATH,
41
+ oai.ParameterLocation.COOKIE,
42
+ oai.ParameterLocation.HEADER,
43
+ }
44
+
45
+ @classmethod
46
+ def build( # noqa: PLR0911
47
+ cls,
48
+ *,
49
+ data: oai.Schema,
50
+ name: str,
51
+ required: bool,
52
+ schemas: Schemas,
53
+ parent_name: str,
54
+ config: Config,
55
+ ) -> tuple[LiteralEnumProperty | NoneProperty | UnionProperty | PropertyError, Schemas]:
56
+ """
57
+ Create a LiteralEnumProperty from schema data.
58
+
59
+ Args:
60
+ data: The OpenAPI Schema which defines this enum.
61
+ name: The name to use for variables which receive this Enum's value (e.g. model property name)
62
+ required: Whether or not this Property is required in the calling context
63
+ schemas: The Schemas which have been defined so far (used to prevent naming collisions)
64
+ parent_name: The context in which this LiteralEnumProperty is defined, used to create more specific class names.
65
+ config: The global config for this run of the generator
66
+
67
+ Returns:
68
+ A tuple containing either the created property or a PropertyError AND update schemas.
69
+ """
70
+
71
+ enum = data.enum or [] # The outer function checks for this, but mypy doesn't know that
72
+
73
+ # OpenAPI allows for null as an enum value, but it doesn't make sense with how enums are constructed in Python.
74
+ # So instead, if null is a possible value, make the property nullable.
75
+ # Mypy is not smart enough to know that the type is right though
76
+ unchecked_value_list = [value for value in enum if value is not None] # type: ignore
77
+
78
+ # It's legal to have an enum that only contains null as a value, we don't bother constructing an enum for that
79
+ if len(unchecked_value_list) == 0:
80
+ return (
81
+ NoneProperty.build(
82
+ name=name,
83
+ required=required,
84
+ default="None",
85
+ python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
86
+ description=None,
87
+ example=None,
88
+ ),
89
+ schemas,
90
+ )
91
+
92
+ value_types = {type(value) for value in unchecked_value_list}
93
+ if len(value_types) > 1:
94
+ return PropertyError(
95
+ header="Enum values must all be the same type", detail=f"Got {value_types}", data=data
96
+ ), schemas
97
+ value_type = next(iter(value_types))
98
+ if value_type not in (str, int):
99
+ return PropertyError(header=f"Unsupported enum type {value_type}", data=data), schemas
100
+ value_list = cast(
101
+ Union[List[int], List[str]], unchecked_value_list
102
+ ) # We checked this with all the value_types stuff
103
+
104
+ if len(value_list) < len(enum): # Only one of the values was None, that becomes a union
105
+ data.oneOf = [
106
+ oai.Schema(type=DataType.NULL),
107
+ data.model_copy(update={"enum": value_list, "default": data.default}),
108
+ ]
109
+ data.enum = None
110
+ return UnionProperty.build(
111
+ data=data,
112
+ name=name,
113
+ required=required,
114
+ schemas=schemas,
115
+ parent_name=parent_name,
116
+ config=config,
117
+ )
118
+
119
+ class_name = data.title or name
120
+ if parent_name:
121
+ class_name = f"{utils.pascal_case(parent_name)}{utils.pascal_case(class_name)}"
122
+ class_info = Class.from_string(string=class_name, config=config)
123
+ values: set[str | int] = set(value_list)
124
+
125
+ if class_info.name in schemas.classes_by_name:
126
+ existing = schemas.classes_by_name[class_info.name]
127
+ if not isinstance(existing, LiteralEnumProperty) or values != existing.values:
128
+ return (
129
+ PropertyError(
130
+ detail=f"Found conflicting enums named {class_info.name} with incompatible values.", data=data
131
+ ),
132
+ schemas,
133
+ )
134
+
135
+ prop = LiteralEnumProperty(
136
+ name=name,
137
+ required=required,
138
+ class_info=class_info,
139
+ values=values,
140
+ value_type=value_type,
141
+ default=None,
142
+ python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
143
+ description=data.description,
144
+ example=data.example,
145
+ )
146
+ checked_default = prop.convert_value(data.default)
147
+ if isinstance(checked_default, PropertyError):
148
+ checked_default.data = data
149
+ return checked_default, schemas
150
+ prop = evolve(prop, default=checked_default)
151
+
152
+ schemas = evolve(schemas, classes_by_name={**schemas.classes_by_name, class_info.name: prop})
153
+ return prop, schemas
154
+
155
+ def convert_value(self, value: Any) -> Value | PropertyError | None:
156
+ if value is None or isinstance(value, Value):
157
+ return value
158
+ if isinstance(value, self.value_type):
159
+ if value in self.values:
160
+ return Value(python_code=repr(value), raw_value=value)
161
+ else:
162
+ return PropertyError(detail=f"Value {value} is not valid for enum {self.name}")
163
+ return PropertyError(detail=f"Cannot convert {value} to enum {self.name} of type {self.value_type}")
164
+
165
+ def get_base_type_string(self, *, quoted: bool = False) -> str:
166
+ return self.class_info.name
167
+
168
+ def get_base_json_type_string(self, *, quoted: bool = False) -> str:
169
+ return self.value_type.__name__
170
+
171
+ def get_instance_type_string(self) -> str:
172
+ return self.value_type.__name__
173
+
174
+ def get_imports(self, *, prefix: str) -> set[str]:
175
+ """
176
+ Get a set of import strings that should be included when this property is used somewhere
177
+
178
+ Args:
179
+ prefix: A prefix to put before any relative (local) module names. This should be the number of . to get
180
+ back to the root of the generated client.
181
+ """
182
+ imports = super().get_imports(prefix=prefix)
183
+ imports.add("from typing import cast")
184
+ imports.add(f"from {prefix}models.{self.class_info.module_name} import {self.class_info.name}")
185
+ imports.add(
186
+ f"from {prefix}models.{self.class_info.module_name} import check_{self.get_class_name_snake_case()}"
187
+ )
188
+ return imports
189
+
190
+ def get_class_name_snake_case(self) -> str:
191
+ return utils.snake_case(self.class_info.name)
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  from openapi_python_client.parser.properties.date import DateProperty
4
4
  from openapi_python_client.parser.properties.datetime import DateTimeProperty
5
5
  from openapi_python_client.parser.properties.file import FileProperty
6
+ from openapi_python_client.parser.properties.literal_enum_property import LiteralEnumProperty
6
7
 
7
8
  __all__ = ["merge_properties"]
8
9
 
@@ -53,6 +54,9 @@ def merge_properties(prop1: Property, prop2: Property) -> Property | PropertyErr
53
54
  if isinstance(prop1, EnumProperty) or isinstance(prop2, EnumProperty):
54
55
  return _merge_with_enum(prop1, prop2)
55
56
 
57
+ if isinstance(prop1, LiteralEnumProperty) or isinstance(prop2, LiteralEnumProperty):
58
+ return _merge_with_literal_enum(prop1, prop2)
59
+
56
60
  if (merged := _merge_same_type(prop1, prop2)) is not None:
57
61
  return merged
58
62
 
@@ -136,6 +140,32 @@ def _merge_with_enum(prop1: PropertyProtocol, prop2: PropertyProtocol) -> EnumPr
136
140
  )
137
141
 
138
142
 
143
+ def _merge_with_literal_enum(prop1: PropertyProtocol, prop2: PropertyProtocol) -> LiteralEnumProperty | PropertyError:
144
+ if isinstance(prop1, LiteralEnumProperty) and isinstance(prop2, LiteralEnumProperty):
145
+ # We want the narrowest validation rules that fit both, so use whichever values list is a
146
+ # subset of the other.
147
+ if prop1.values <= prop2.values:
148
+ values = prop1.values
149
+ class_info = prop1.class_info
150
+ elif prop2.values <= prop1.values:
151
+ values = prop2.values
152
+ class_info = prop2.class_info
153
+ else:
154
+ return PropertyError(detail="can't redefine a literal enum property with incompatible lists of values")
155
+ return _merge_common_attributes(evolve(prop1, values=values, class_info=class_info), prop2)
156
+
157
+ # If enum values were specified for just one of the properties, use those.
158
+ enum_prop = prop1 if isinstance(prop1, LiteralEnumProperty) else cast(LiteralEnumProperty, prop2)
159
+ non_enum_prop = prop2 if isinstance(prop1, LiteralEnumProperty) else prop1
160
+ if (isinstance(non_enum_prop, IntProperty) and enum_prop.value_type is int) or (
161
+ isinstance(non_enum_prop, StringProperty) and enum_prop.value_type is str
162
+ ):
163
+ return _merge_common_attributes(enum_prop, prop1, prop2)
164
+ 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)}"
166
+ )
167
+
168
+
139
169
  def _merge_common_attributes(base: PropertyT, *extend_with: PropertyProtocol) -> PropertyT | PropertyError:
140
170
  """Create a new instance based on base, overriding basic attributes with values from extend_with, in order.
141
171
 
@@ -14,10 +14,12 @@ from .file import FileProperty
14
14
  from .float import FloatProperty
15
15
  from .int import IntProperty
16
16
  from .list_property import ListProperty
17
+ from .literal_enum_property import LiteralEnumProperty
17
18
  from .model_property import ModelProperty
18
19
  from .none import NoneProperty
19
20
  from .string import StringProperty
20
21
  from .union import UnionProperty
22
+ from .uuid import UuidProperty
21
23
 
22
24
  Property: TypeAlias = Union[
23
25
  AnyProperty,
@@ -26,6 +28,7 @@ Property: TypeAlias = Union[
26
28
  DateProperty,
27
29
  DateTimeProperty,
28
30
  EnumProperty,
31
+ LiteralEnumProperty,
29
32
  FileProperty,
30
33
  FloatProperty,
31
34
  IntProperty,
@@ -34,4 +37,5 @@ Property: TypeAlias = Union[
34
37
  NoneProperty,
35
38
  StringProperty,
36
39
  UnionProperty,
40
+ UuidProperty,
37
41
  ]
@@ -0,0 +1,80 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, ClassVar
4
+ from uuid import UUID
5
+
6
+ from attr import define
7
+
8
+ from ... import schema as oai
9
+ from ...utils import PythonIdentifier
10
+ from ..errors import PropertyError
11
+ from .protocol import PropertyProtocol, Value
12
+
13
+
14
+ @define
15
+ class UuidProperty(PropertyProtocol):
16
+ """A property of type uuid.UUID"""
17
+
18
+ name: str
19
+ required: bool
20
+ default: Value | None
21
+ python_name: PythonIdentifier
22
+ description: str | None
23
+ example: str | None
24
+
25
+ _type_string: ClassVar[str] = "UUID"
26
+ _json_type_string: ClassVar[str] = "str"
27
+ _allowed_locations: ClassVar[set[oai.ParameterLocation]] = {
28
+ oai.ParameterLocation.QUERY,
29
+ oai.ParameterLocation.PATH,
30
+ oai.ParameterLocation.COOKIE,
31
+ oai.ParameterLocation.HEADER,
32
+ }
33
+ template: ClassVar[str] = "uuid_property.py.jinja"
34
+
35
+ @classmethod
36
+ def build(
37
+ cls,
38
+ name: str,
39
+ required: bool,
40
+ default: Any,
41
+ python_name: PythonIdentifier,
42
+ description: str | None,
43
+ example: str | None,
44
+ ) -> UuidProperty | PropertyError:
45
+ checked_default = cls.convert_value(default)
46
+ if isinstance(checked_default, PropertyError):
47
+ return checked_default
48
+
49
+ return cls(
50
+ name=name,
51
+ required=required,
52
+ default=checked_default,
53
+ python_name=python_name,
54
+ description=description,
55
+ example=example,
56
+ )
57
+
58
+ @classmethod
59
+ def convert_value(cls, value: Any) -> Value | None | PropertyError:
60
+ if value is None or isinstance(value, Value):
61
+ return value
62
+ if isinstance(value, str):
63
+ try:
64
+ UUID(value)
65
+ except ValueError:
66
+ return PropertyError(f"Invalid UUID value: {value}")
67
+ return Value(python_code=f"UUID('{value}')", raw_value=value)
68
+ return PropertyError(f"Invalid UUID value: {value}")
69
+
70
+ def get_imports(self, *, prefix: str) -> set[str]:
71
+ """
72
+ Get a set of import strings that should be included when this property is used somewhere
73
+
74
+ Args:
75
+ prefix: A prefix to put before any relative (local) module names. This should be the number of . to get
76
+ back to the root of the generated client.
77
+ """
78
+ imports = super().get_imports(prefix=prefix)
79
+ imports.update({"from uuid import UUID"})
80
+ return imports
@@ -43,6 +43,7 @@ class Schema(BaseModel):
43
43
  anyOf: List[Union[Reference, "Schema"]] = Field(default_factory=list)
44
44
  schema_not: Optional[Union[Reference, "Schema"]] = Field(default=None, alias="not")
45
45
  items: Optional[Union[Reference, "Schema"]] = None
46
+ prefixItems: Optional[List[Union[Reference, "Schema"]]] = Field(default_factory=list)
46
47
  properties: Optional[Dict[str, Union[Reference, "Schema"]]] = None
47
48
  additionalProperties: Optional[Union[bool, Reference, "Schema"]] = None
48
49
  description: Optional[str] = None
@@ -74,7 +74,7 @@ class Client:
74
74
  {% endmacro %}{{ builders("Client") }}
75
75
  {% macro httpx_stuff(name, custom_constructor=None) %}
76
76
  def set_httpx_client(self, client: httpx.Client) -> "{{ name }}":
77
- """Manually the underlying httpx.Client
77
+ """Manually set the underlying httpx.Client
78
78
 
79
79
  **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
80
80
  """
@@ -7,7 +7,7 @@ from ...client import AuthenticatedClient, Client
7
7
  from ...types import Response, UNSET
8
8
  from ... import errors
9
9
 
10
- {% for relative in endpoint.relative_imports %}
10
+ {% for relative in endpoint.relative_imports | sort %}
11
11
  {{ relative }}
12
12
  {% endfor %}
13
13
 
@@ -70,7 +70,7 @@ def _get_kwargs(
70
70
 
71
71
  def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[{{ return_string }}]:
72
72
  {% for response in endpoint.responses %}
73
- if response.status_code == HTTPStatus.{{ response.status_code.name }}:
73
+ if response.status_code == {{ response.status_code.value }}:
74
74
  {% if parsed_responses %}{% import "property_templates/" + response.prop.template as prop_template %}
75
75
  {% if prop_template.construct %}
76
76
  {{ prop_template.construct(response.prop, response.source.attribute) | indent(8) }}
@@ -0,0 +1,10 @@
1
+ from typing import Literal, Set, cast
2
+
3
+ {{ enum.class_info.name }} = Literal{{ "%r" | format(enum.values|list|sort) }}
4
+
5
+ {{ enum.get_class_name_snake_case() | upper }}_VALUES: Set[{{ enum.class_info.name }}] = { {% for v in enum.values|list|sort %}{{"%r"|format(v)}}, {% endfor %} }
6
+
7
+ def check_{{ enum.get_class_name_snake_case() }}(value: {{ enum.get_instance_type_string() }}) -> {{ enum.class_info.name}}:
8
+ if value in {{ enum.get_class_name_snake_case() | upper }}_VALUES:
9
+ return cast({{enum.class_info.name}}, value)
10
+ raise TypeError(f"Unexpected value {value!r}. Expected one of {{"{"}}{{ enum.get_class_name_snake_case() | upper }}_VALUES!r}")
@@ -13,7 +13,7 @@ import json
13
13
 
14
14
  from ..types import UNSET, Unset
15
15
 
16
- {% for relative in model.relative_imports %}
16
+ {% for relative in model.relative_imports | sort %}
17
17
  {{ relative }}
18
18
  {% endfor %}
19
19
 
@@ -0,0 +1,38 @@
1
+ {% macro construct_function(property, source) %}
2
+ check_{{ property.get_class_name_snake_case() }}({{ source }})
3
+ {% endmacro %}
4
+
5
+ {% from "property_templates/property_macros.py.jinja" import construct_template %}
6
+
7
+ {% macro construct(property, source) %}
8
+ {{ construct_template(construct_function, property, source) }}
9
+ {% endmacro %}
10
+
11
+ {% macro check_type_for_construct(property, source) %}isinstance({{ source }}, {{ property.get_instance_type_string() }}){% endmacro %}
12
+
13
+ {% macro transform(property, source, destination, declare_type=True, multipart=False) %}
14
+ {% set type_string = property.get_type_string(json=True) %}
15
+ {% if property.required %}
16
+ {{ destination }}{% if declare_type %}: {{ type_string }}{% endif %} = {{ source }}
17
+ {%- else %}
18
+ {{ destination }}{% if declare_type %}: {{ type_string }}{% endif %} = UNSET
19
+ if not isinstance({{ source }}, Unset):
20
+ {{ destination }} = {{ source }}
21
+ {% endif %}
22
+ {% endmacro %}
23
+
24
+ {% macro transform_multipart(property, source, destination) %}
25
+ {% set transformed = "(None, str(" + source + ").encode(), \"text/plain\")" %}
26
+ {% set type_string = "Union[Unset, Tuple[None, bytes, str]]" %}
27
+ {% if property.required %}
28
+ {{ destination }} = {{ transformed }}
29
+ {%- else %}
30
+ {{ destination }}: {{ type_string }} = UNSET
31
+ if not isinstance({{ source }}, Unset):
32
+ {{ destination }} = {{ transformed }}
33
+ {% endif %}
34
+ {% endmacro %}
35
+
36
+ {% macro transform_header(source) %}
37
+ str({{ source }})
38
+ {% endmacro %}
@@ -0,0 +1,38 @@
1
+ {% macro construct_function(property, source) %}
2
+ UUID({{ source }})
3
+ {% endmacro %}
4
+
5
+ {% from "property_templates/property_macros.py.jinja" import construct_template %}
6
+
7
+ {% macro construct(property, source) %}
8
+ {{ construct_template(construct_function, property, source) }}
9
+ {% endmacro %}
10
+
11
+ {% macro check_type_for_construct(property, source) %}isinstance({{ source }}, str){% endmacro %}
12
+
13
+ {% macro transform(property, source, destination, declare_type=True) %}
14
+ {% set transformed = "str(" + source + ")" %}
15
+ {% if property.required %}
16
+ {{ destination }} = {{ transformed }}
17
+ {%- else %}
18
+ {% if declare_type %}
19
+ {% set type_annotation = property.get_type_string(json=True) %}
20
+ {{ destination }}: {{ type_annotation }} = UNSET
21
+ {% else %}
22
+ {{ destination }} = UNSET
23
+ {% endif %}
24
+ if not isinstance({{ source }}, Unset):
25
+ {{ destination }} = {{ transformed }}
26
+ {%- endif %}
27
+ {% endmacro %}
28
+
29
+ {% macro transform_multipart(property, source, destination) %}
30
+ {% if property.required %}
31
+ {{ destination }} = str({{ source }})
32
+ {%- else %}
33
+ {% set type_annotation = property.get_type_string(json=True) | replace("str", "bytes") %}
34
+ {{ destination }}: {{ type_annotation }} = UNSET
35
+ if not isinstance({{ source }}, Unset):
36
+ {{ destination }} = str({{ source }})
37
+ {%- endif %}
38
+ {% endmacro %}
@@ -14,11 +14,11 @@ dependencies = [
14
14
  "python-dateutil>=2.8.1,<3.0.0",
15
15
  "httpx>=0.20.0,<0.28.0",
16
16
  "ruamel.yaml>=0.18.6,<0.19.0",
17
- "ruff>=0.2,<0.7",
17
+ "ruff>=0.2,<0.8",
18
18
  "typing-extensions>=4.8.0,<5.0.0",
19
19
  ]
20
20
  name = "openapi-python-client"
21
- version = "0.21.5"
21
+ version = "0.21.6"
22
22
  description = "Generate modern Python clients from OpenAPI"
23
23
  keywords = [
24
24
  "OpenAPI",
@@ -35,6 +35,7 @@ classifiers = [
35
35
  "Programming Language :: Python :: 3.10",
36
36
  "Programming Language :: Python :: 3.11",
37
37
  "Programming Language :: Python :: 3.12",
38
+ "Programming Language :: Python :: 3.13",
38
39
  "Topic :: Software Development :: Code Generators",
39
40
  "Typing :: Typed",
40
41
  ]
@@ -59,7 +60,7 @@ exclude = [
59
60
 
60
61
  [tool.ruff.lint]
61
62
  select = ["E", "F", "I", "UP", "B", "PL", "RUF"]
62
- ignore = ["E501", "PLR0913"]
63
+ ignore = ["E501", "PLR0913", "PLR2004"]
63
64
 
64
65
  [tool.ruff.lint.per-file-ignores]
65
66
  "openapi_python_client/cli.py" = ["B008"]
@@ -114,7 +115,7 @@ mypy = "mypy openapi_python_client"
114
115
  check = { composite = ["lint", "format", "mypy", "test"] }
115
116
  regen = {composite = ["regen_e2e", "regen_integration"]}
116
117
  e2e = "pytest openapi_python_client end_to_end_tests/test_end_to_end.py"
117
- re = {composite = ["regen_e2e", "e2e"]}
118
+ re = {composite = ["regen_e2e", "e2e --snapshot-update"]}
118
119
  regen_e2e = "python -m end_to_end_tests.regen_golden_record"
119
120
 
120
121
  [tool.pdm.scripts.test]