openapi-python-client 0.21.7__tar.gz → 0.23.0__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.7 → openapi_python_client-0.23.0}/PKG-INFO +18 -8
  2. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/README.md +10 -0
  3. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/__init__.py +12 -11
  4. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/cli.py +4 -3
  5. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/config.py +10 -7
  6. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/bodies.py +5 -5
  7. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/errors.py +1 -1
  8. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/openapi.py +50 -43
  9. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/__init__.py +2 -2
  10. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/enum_property.py +2 -2
  11. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/list_property.py +4 -4
  12. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/literal_enum_property.py +2 -2
  13. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/model_property.py +2 -3
  14. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/protocol.py +1 -1
  15. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/schemas.py +15 -15
  16. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/responses.py +2 -2
  17. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/__init__.py +13 -2
  18. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/callback.py +2 -2
  19. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/components.py +12 -10
  20. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/discriminator.py +2 -2
  21. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/encoding.py +4 -4
  22. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/header.py +2 -0
  23. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/link.py +2 -2
  24. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/media_type.py +5 -3
  25. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/oauth_flow.py +2 -2
  26. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/open_api.py +9 -11
  27. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/operation.py +8 -14
  28. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/parameter.py +5 -3
  29. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/path_item.py +8 -9
  30. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/paths.py +1 -3
  31. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/request_body.py +4 -2
  32. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/response.py +6 -4
  33. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/responses.py +2 -2
  34. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/schema.py +9 -12
  35. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/security_requirement.py +1 -3
  36. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/server.py +2 -2
  37. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/server_variable.py +2 -2
  38. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/client.py.jinja +6 -6
  39. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/endpoint_macros.py.jinja +2 -2
  40. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/endpoint_module.py.jinja +3 -3
  41. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/literal_enum.py.jinja +2 -2
  42. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/model.py.jinja +7 -12
  43. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/property_templates/enum_property.py.jinja +1 -1
  44. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/property_templates/list_property.py.jinja +1 -1
  45. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/property_templates/literal_enum_property.py.jinja +1 -1
  46. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/pyproject.toml.jinja +6 -6
  47. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/setup.py.jinja +2 -2
  48. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/types.py.jinja +5 -3
  49. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/utils.py +0 -1
  50. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/pyproject.toml +11 -12
  51. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/.gitignore +0 -0
  52. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/LICENSE +0 -0
  53. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/__main__.py +0 -0
  54. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/__init__.py +0 -0
  55. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/any.py +0 -0
  56. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/boolean.py +0 -0
  57. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/const.py +0 -0
  58. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/date.py +0 -0
  59. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/datetime.py +0 -0
  60. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/file.py +0 -0
  61. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/float.py +0 -0
  62. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/int.py +0 -0
  63. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/merge_properties.py +0 -0
  64. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/none.py +0 -0
  65. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/property.py +0 -0
  66. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/string.py +0 -0
  67. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/union.py +0 -0
  68. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/parser/properties/uuid.py +0 -0
  69. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/py.typed +0 -0
  70. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/3.0.3.md +0 -0
  71. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/3.1.0.md +0 -0
  72. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/__init__.py +2 -2
  73. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/data_type.py +0 -0
  74. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/LICENSE +0 -0
  75. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/README.md +0 -0
  76. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/contact.py +0 -0
  77. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/example.py +0 -0
  78. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/external_documentation.py +0 -0
  79. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/info.py +0 -0
  80. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/license.py +0 -0
  81. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/oauth_flows.py +0 -0
  82. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/reference.py +0 -0
  83. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/security_scheme.py +0 -0
  84. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/tag.py +0 -0
  85. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/openapi_schema_pydantic/xml.py +0 -0
  86. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/schema/parameter_location.py +0 -0
  87. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/.gitignore.jinja +0 -0
  88. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/README.md.jinja +0 -0
  89. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/api_init.py.jinja +0 -0
  90. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/endpoint_init.py.jinja +0 -0
  91. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/errors.py.jinja +0 -0
  92. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/helpers.jinja +0 -0
  93. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/int_enum.py.jinja +0 -0
  94. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/models_init.py.jinja +0 -0
  95. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/package_init.py.jinja +0 -0
  96. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/property_templates/any_property.py.jinja +0 -0
  97. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/property_templates/boolean_property.py.jinja +0 -0
  98. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/property_templates/const_property.py.jinja +0 -0
  99. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/property_templates/date_property.py.jinja +0 -0
  100. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/property_templates/datetime_property.py.jinja +0 -0
  101. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/property_templates/file_property.py.jinja +0 -0
  102. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/property_templates/float_property.py.jinja +0 -0
  103. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/property_templates/helpers.jinja +0 -0
  104. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/property_templates/int_property.py.jinja +0 -0
  105. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/property_templates/model_property.py.jinja +0 -0
  106. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/property_templates/property_macros.py.jinja +0 -0
  107. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/property_templates/union_property.py.jinja +0 -0
  108. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/property_templates/uuid_property.py.jinja +0 -0
  109. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/pyproject_ruff.toml.jinja +0 -0
  110. {openapi_python_client-0.21.7 → openapi_python_client-0.23.0}/openapi_python_client/templates/str_enum.py.jinja +0 -0
@@ -1,16 +1,16 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: openapi-python-client
3
- Version: 0.21.7
3
+ Version: 0.23.0
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>
7
7
  License: MIT
8
+ License-File: LICENSE
8
9
  Keywords: Client,Generator,OpenAPI
9
10
  Classifier: Development Status :: 4 - Beta
10
11
  Classifier: Intended Audience :: Developers
11
12
  Classifier: License :: OSI Approved :: MIT License
12
13
  Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.8
14
14
  Classifier: Programming Language :: Python :: 3.9
15
15
  Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
@@ -18,17 +18,17 @@ Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Programming Language :: Python :: 3.13
19
19
  Classifier: Topic :: Software Development :: Code Generators
20
20
  Classifier: Typing :: Typed
21
- Requires-Python: <4.0,>=3.8.1
22
- Requires-Dist: attrs>=21.3.0
21
+ Requires-Python: <4.0,>=3.9
22
+ Requires-Dist: attrs>=22.2.0
23
23
  Requires-Dist: colorama>=0.4.3; sys_platform == 'win32'
24
- Requires-Dist: httpx<0.28.0,>=0.20.0
24
+ Requires-Dist: httpx<0.29.0,>=0.20.0
25
25
  Requires-Dist: jinja2<4.0.0,>=3.0.0
26
26
  Requires-Dist: pydantic<3.0.0,>=2.1.1
27
27
  Requires-Dist: python-dateutil<3.0.0,>=2.8.1
28
28
  Requires-Dist: ruamel-yaml<0.19.0,>=0.18.6
29
- Requires-Dist: ruff<0.8,>=0.2
29
+ Requires-Dist: ruff<0.9,>=0.2
30
30
  Requires-Dist: shellingham<2.0.0,>=1.3.2
31
- Requires-Dist: typer<0.13,>0.6
31
+ Requires-Dist: typer<0.16,>0.6
32
32
  Requires-Dist: typing-extensions<5.0.0,>=4.8.0
33
33
  Description-Content-Type: text/markdown
34
34
 
@@ -142,6 +142,16 @@ literal_enums: true
142
142
 
143
143
  This is especially useful if enum values, when transformed to their Python names, end up conflicting due to case sensitivity or special symbols.
144
144
 
145
+ ### generate_all_tags
146
+
147
+ `openapi-python-client` generates module names within the `api` module based on the OpenAPI `tags` of each endpoint.
148
+ By default, only the _first_ tag is generated. If you want to generate **duplicate** endpoint functions using _every_ tag
149
+ listed, you can enable this option:
150
+
151
+ ```yaml
152
+ generate_all_tags: true
153
+ ```
154
+
145
155
  ### project_name_override and package_name_override
146
156
 
147
157
  Used to change the name of generated client library project/package. If the project name is changed but an override for the package name
@@ -108,6 +108,16 @@ literal_enums: true
108
108
 
109
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
110
 
111
+ ### generate_all_tags
112
+
113
+ `openapi-python-client` generates module names within the `api` module based on the OpenAPI `tags` of each endpoint.
114
+ By default, only the _first_ tag is generated. If you want to generate **duplicate** endpoint functions using _every_ tag
115
+ listed, you can enable this option:
116
+
117
+ ```yaml
118
+ generate_all_tags: true
119
+ ```
120
+
111
121
  ### project_name_override and package_name_override
112
122
 
113
123
  Used to change the name of generated client library project/package. If the project name is changed but an override for the package name
@@ -4,10 +4,11 @@ import json
4
4
  import mimetypes
5
5
  import shutil
6
6
  import subprocess
7
+ from collections.abc import Sequence
7
8
  from importlib.metadata import version
8
9
  from pathlib import Path
9
10
  from subprocess import CalledProcessError
10
- from typing import Any, Dict, List, Optional, Sequence, Union
11
+ from typing import Any, Optional, Union
11
12
 
12
13
  import httpcore
13
14
  import httpx
@@ -101,19 +102,17 @@ class Project:
101
102
  openapi=self.openapi,
102
103
  endpoint_collections_by_tag=self.openapi.endpoint_collections_by_tag,
103
104
  )
104
- self.errors: List[GeneratorError] = []
105
+ self.errors: list[GeneratorError] = []
105
106
 
106
107
  def build(self) -> Sequence[GeneratorError]:
107
108
  """Create the project from templates"""
108
109
 
109
110
  print(f"Generating {self.project_dir}")
110
- if self.config.overwrite:
111
- shutil.rmtree(self.project_dir, ignore_errors=True)
112
-
113
111
  try:
114
112
  self.project_dir.mkdir()
115
113
  except FileExistsError:
116
- return [GeneratorError(detail="Directory already exists. Delete it or use the --overwrite option.")]
114
+ if not self.config.overwrite:
115
+ return [GeneratorError(detail="Directory already exists. Delete it or use the --overwrite option.")]
117
116
  self._create_package()
118
117
  self._build_metadata()
119
118
  self._build_models()
@@ -147,8 +146,8 @@ class Project:
147
146
  )
148
147
  )
149
148
 
150
- def _get_errors(self) -> List[GeneratorError]:
151
- errors: List[GeneratorError] = []
149
+ def _get_errors(self) -> list[GeneratorError]:
150
+ errors: list[GeneratorError] = []
152
151
  for collection in self.openapi.endpoint_collections_by_tag.values():
153
152
  errors.extend(collection.parse_errors)
154
153
  errors.extend(self.openapi.errors)
@@ -157,7 +156,7 @@ class Project:
157
156
 
158
157
  def _create_package(self) -> None:
159
158
  if self.package_dir != self.project_dir:
160
- self.package_dir.mkdir()
159
+ self.package_dir.mkdir(exist_ok=True)
161
160
  # Package __init__.py
162
161
  package_init = self.package_dir / "__init__.py"
163
162
 
@@ -213,6 +212,7 @@ class Project:
213
212
  def _build_models(self) -> None:
214
213
  # Generate models
215
214
  models_dir = self.package_dir / "models"
215
+ shutil.rmtree(models_dir, ignore_errors=True)
216
216
  models_dir.mkdir()
217
217
  models_init = models_dir / "__init__.py"
218
218
  imports = []
@@ -258,6 +258,7 @@ class Project:
258
258
 
259
259
  # Generate endpoints
260
260
  api_dir = self.package_dir / "api"
261
+ shutil.rmtree(api_dir, ignore_errors=True)
261
262
  api_dir.mkdir()
262
263
  api_init_path = api_dir / "__init__.py"
263
264
  api_init_template = self.env.get_template("api_init.py.jinja")
@@ -325,7 +326,7 @@ def generate(
325
326
  return project.build()
326
327
 
327
328
 
328
- def _load_yaml_or_json(data: bytes, content_type: Optional[str]) -> Union[Dict[str, Any], GeneratorError]:
329
+ def _load_yaml_or_json(data: bytes, content_type: Optional[str]) -> Union[dict[str, Any], GeneratorError]:
329
330
  if content_type == "application/json":
330
331
  try:
331
332
  return json.loads(data.decode())
@@ -339,7 +340,7 @@ def _load_yaml_or_json(data: bytes, content_type: Optional[str]) -> Union[Dict[s
339
340
  return GeneratorError(header=f"Invalid YAML from provided source: {err}")
340
341
 
341
342
 
342
- def _get_document(*, source: Union[str, Path], timeout: int) -> Union[Dict[str, Any], GeneratorError]:
343
+ def _get_document(*, source: Union[str, Path], timeout: int) -> Union[dict[str, Any], GeneratorError]:
343
344
  yaml_bytes: bytes
344
345
  content_type: Optional[str]
345
346
  if isinstance(source, str):
@@ -1,7 +1,8 @@
1
1
  import codecs
2
+ from collections.abc import Sequence
2
3
  from pathlib import Path
3
4
  from pprint import pformat
4
- from typing import Optional, Sequence, Union
5
+ from typing import Optional, Union
5
6
 
6
7
  import typer
7
8
 
@@ -9,7 +10,7 @@ from openapi_python_client import MetaType
9
10
  from openapi_python_client.config import Config, ConfigFile
10
11
  from openapi_python_client.parser.errors import ErrorLevel, GeneratorError, ParseError
11
12
 
12
- app = typer.Typer()
13
+ app = typer.Typer(name="openapi-python-client")
13
14
 
14
15
 
15
16
  def _version_callback(value: bool) -> None:
@@ -62,7 +63,7 @@ def _process_config(
62
63
  # noinspection PyUnusedLocal
63
64
 
64
65
 
65
- @app.callback(name="openapi-python-client")
66
+ @app.callback()
66
67
  def cli(
67
68
  version: bool = typer.Option(False, "--version", callback=_version_callback, help="Print the version and exit"),
68
69
  ) -> None:
@@ -2,7 +2,7 @@ import json
2
2
  import mimetypes
3
3
  from enum import Enum
4
4
  from pathlib import Path
5
- from typing import Dict, List, Optional, Union
5
+ from typing import Optional, Union
6
6
 
7
7
  from attr import define
8
8
  from pydantic import BaseModel
@@ -34,14 +34,15 @@ class ConfigFile(BaseModel):
34
34
  See https://github.com/openapi-generators/openapi-python-client#configuration
35
35
  """
36
36
 
37
- class_overrides: Optional[Dict[str, ClassOverride]] = None
38
- content_type_overrides: Optional[Dict[str, str]] = None
37
+ class_overrides: Optional[dict[str, ClassOverride]] = None
38
+ content_type_overrides: Optional[dict[str, str]] = None
39
39
  project_name_override: Optional[str] = None
40
40
  package_name_override: Optional[str] = None
41
41
  package_version_override: Optional[str] = None
42
42
  use_path_prefixes_for_title_model_names: bool = True
43
- post_hooks: Optional[List[str]] = None
43
+ post_hooks: Optional[list[str]] = None
44
44
  field_prefix: str = "field_"
45
+ generate_all_tags: bool = False
45
46
  http_timeout: int = 5
46
47
  literal_enums: bool = False
47
48
 
@@ -63,18 +64,19 @@ class Config:
63
64
  """Contains all the config values for the generator, from files, defaults, and CLI arguments."""
64
65
 
65
66
  meta_type: MetaType
66
- class_overrides: Dict[str, ClassOverride]
67
+ class_overrides: dict[str, ClassOverride]
67
68
  project_name_override: Optional[str]
68
69
  package_name_override: Optional[str]
69
70
  package_version_override: Optional[str]
70
71
  use_path_prefixes_for_title_model_names: bool
71
- post_hooks: List[str]
72
+ post_hooks: list[str]
72
73
  field_prefix: str
74
+ generate_all_tags: bool
73
75
  http_timeout: int
74
76
  literal_enums: bool
75
77
  document_source: Union[Path, str]
76
78
  file_encoding: str
77
- content_type_overrides: Dict[str, str]
79
+ content_type_overrides: dict[str, str]
78
80
  overwrite: bool
79
81
  output_path: Optional[Path]
80
82
 
@@ -110,6 +112,7 @@ class Config:
110
112
  use_path_prefixes_for_title_model_names=config_file.use_path_prefixes_for_title_model_names,
111
113
  post_hooks=post_hooks,
112
114
  field_prefix=config_file.field_prefix,
115
+ generate_all_tags=config_file.generate_all_tags,
113
116
  http_timeout=config_file.http_timeout,
114
117
  literal_enums=config_file.literal_enums,
115
118
  document_source=document_source,
@@ -1,5 +1,5 @@
1
1
  import sys
2
- from typing import Dict, List, Tuple, Union
2
+ from typing import Union
3
3
 
4
4
  import attr
5
5
 
@@ -44,10 +44,10 @@ def body_from_data(
44
44
  *,
45
45
  data: oai.Operation,
46
46
  schemas: Schemas,
47
- request_bodies: Dict[str, Union[oai.RequestBody, oai.Reference]],
47
+ request_bodies: dict[str, Union[oai.RequestBody, oai.Reference]],
48
48
  config: Config,
49
49
  endpoint_name: str,
50
- ) -> Tuple[List[Union[Body, ParseError]], Schemas]:
50
+ ) -> tuple[list[Union[Body, ParseError]], Schemas]:
51
51
  """Adds form or JSON body to Endpoint if included in data"""
52
52
  body = _resolve_reference(data.request_body, request_bodies)
53
53
  if isinstance(body, ParseError):
@@ -55,7 +55,7 @@ def body_from_data(
55
55
  if body is None:
56
56
  return [], schemas
57
57
 
58
- bodies: List[Union[Body, ParseError]] = []
58
+ bodies: list[Union[Body, ParseError]] = []
59
59
  body_content = body.content
60
60
  prefix_type_names = len(body_content) > 1
61
61
 
@@ -131,7 +131,7 @@ def body_from_data(
131
131
 
132
132
 
133
133
  def _resolve_reference(
134
- body: Union[oai.RequestBody, oai.Reference, None], request_bodies: Dict[str, Union[oai.RequestBody, oai.Reference]]
134
+ body: Union[oai.RequestBody, oai.Reference, None], request_bodies: dict[str, Union[oai.RequestBody, oai.Reference]]
135
135
  ) -> Union[oai.RequestBody, ParseError, None]:
136
136
  if body is None:
137
137
  return None
@@ -2,7 +2,7 @@ from dataclasses import dataclass
2
2
  from enum import Enum
3
3
  from typing import Optional
4
4
 
5
- __all__ = ["ErrorLevel", "GeneratorError", "ParseError", "PropertyError", "ParameterError"]
5
+ __all__ = ["ErrorLevel", "GeneratorError", "ParameterError", "ParseError", "PropertyError"]
6
6
 
7
7
  from pydantic import BaseModel
8
8
 
@@ -1,8 +1,9 @@
1
1
  import re
2
+ from collections.abc import Iterator
2
3
  from copy import deepcopy
3
4
  from dataclasses import dataclass, field
4
5
  from http import HTTPStatus
5
- from typing import Any, Dict, Iterator, List, Optional, Protocol, Set, Tuple, Union
6
+ from typing import Any, Optional, Protocol, Union
6
7
 
7
8
  from pydantic import ValidationError
8
9
 
@@ -40,20 +41,20 @@ class EndpointCollection:
40
41
  """A bunch of endpoints grouped under a tag that will become a module"""
41
42
 
42
43
  tag: str
43
- endpoints: List["Endpoint"] = field(default_factory=list)
44
- parse_errors: List[ParseError] = field(default_factory=list)
44
+ endpoints: list["Endpoint"] = field(default_factory=list)
45
+ parse_errors: list[ParseError] = field(default_factory=list)
45
46
 
46
47
  @staticmethod
47
48
  def from_data(
48
49
  *,
49
- data: Dict[str, oai.PathItem],
50
+ data: dict[str, oai.PathItem],
50
51
  schemas: Schemas,
51
52
  parameters: Parameters,
52
- request_bodies: Dict[str, Union[oai.RequestBody, oai.Reference]],
53
+ request_bodies: dict[str, Union[oai.RequestBody, oai.Reference]],
53
54
  config: Config,
54
- ) -> Tuple[Dict[utils.PythonIdentifier, "EndpointCollection"], Schemas, Parameters]:
55
+ ) -> tuple[dict[utils.PythonIdentifier, "EndpointCollection"], Schemas, Parameters]:
55
56
  """Parse the openapi paths data to get EndpointCollections by tag"""
56
- endpoints_by_tag: Dict[utils.PythonIdentifier, EndpointCollection] = {}
57
+ endpoints_by_tag: dict[utils.PythonIdentifier, EndpointCollection] = {}
57
58
 
58
59
  methods = ["get", "put", "post", "delete", "options", "head", "patch", "trace"]
59
60
 
@@ -62,13 +63,18 @@ class EndpointCollection:
62
63
  operation: Optional[oai.Operation] = getattr(path_data, method)
63
64
  if operation is None:
64
65
  continue
65
- tag = utils.PythonIdentifier(value=(operation.tags or ["default"])[0], prefix="tag")
66
- collection = endpoints_by_tag.setdefault(tag, EndpointCollection(tag=tag))
66
+
67
+ tags = [utils.PythonIdentifier(value=tag, prefix="tag") for tag in operation.tags or ["default"]]
68
+ if not config.generate_all_tags:
69
+ tags = tags[:1]
70
+
71
+ collections = [endpoints_by_tag.setdefault(tag, EndpointCollection(tag=tag)) for tag in tags]
72
+
67
73
  endpoint, schemas, parameters = Endpoint.from_data(
68
74
  data=operation,
69
75
  path=path,
70
76
  method=method,
71
- tag=tag,
77
+ tags=tags,
72
78
  schemas=schemas,
73
79
  parameters=parameters,
74
80
  request_bodies=request_bodies,
@@ -86,15 +92,16 @@ class EndpointCollection:
86
92
  if not isinstance(endpoint, ParseError):
87
93
  endpoint = Endpoint.sort_parameters(endpoint=endpoint)
88
94
  if isinstance(endpoint, ParseError):
89
- endpoint.header = (
90
- f"WARNING parsing {method.upper()} {path} within {tag}. Endpoint will not be generated."
91
- )
92
- collection.parse_errors.append(endpoint)
95
+ endpoint.header = f"WARNING parsing {method.upper()} {path} within {'/'.join(tags)}. Endpoint will not be generated."
96
+ for collection in collections:
97
+ collection.parse_errors.append(endpoint)
93
98
  continue
94
99
  for error in endpoint.errors:
95
- error.header = f"WARNING parsing {method.upper()} {path} within {tag}."
96
- collection.parse_errors.append(error)
97
- collection.endpoints.append(endpoint)
100
+ error.header = f"WARNING parsing {method.upper()} {path} within {'/'.join(tags)}."
101
+ for collection in collections:
102
+ collection.parse_errors.append(error)
103
+ for collection in collections:
104
+ collection.endpoints.append(endpoint)
98
105
 
99
106
  return endpoints_by_tag, schemas, parameters
100
107
 
@@ -117,7 +124,7 @@ class RequestBodyParser(Protocol):
117
124
 
118
125
  def __call__(
119
126
  self, *, body: oai.RequestBody, schemas: Schemas, parent_name: str, config: Config
120
- ) -> Tuple[Union[Property, PropertyError, None], Schemas]: ... # pragma: no cover
127
+ ) -> tuple[Union[Property, PropertyError, None], Schemas]: ... # pragma: no cover
121
128
 
122
129
 
123
130
  @dataclass
@@ -131,21 +138,21 @@ class Endpoint:
131
138
  description: Optional[str]
132
139
  name: str
133
140
  requires_security: bool
134
- tag: str
141
+ tags: list[PythonIdentifier]
135
142
  summary: Optional[str] = ""
136
- relative_imports: Set[str] = field(default_factory=set)
137
- query_parameters: List[Property] = field(default_factory=list)
138
- path_parameters: List[Property] = field(default_factory=list)
139
- header_parameters: List[Property] = field(default_factory=list)
140
- cookie_parameters: List[Property] = field(default_factory=list)
141
- responses: List[Response] = field(default_factory=list)
142
- bodies: List[Body] = field(default_factory=list)
143
- errors: List[ParseError] = field(default_factory=list)
143
+ relative_imports: set[str] = field(default_factory=set)
144
+ query_parameters: list[Property] = field(default_factory=list)
145
+ path_parameters: list[Property] = field(default_factory=list)
146
+ header_parameters: list[Property] = field(default_factory=list)
147
+ cookie_parameters: list[Property] = field(default_factory=list)
148
+ responses: list[Response] = field(default_factory=list)
149
+ bodies: list[Body] = field(default_factory=list)
150
+ errors: list[ParseError] = field(default_factory=list)
144
151
 
145
152
  @staticmethod
146
153
  def _add_responses(
147
154
  *, endpoint: "Endpoint", data: oai.Responses, schemas: Schemas, config: Config
148
- ) -> Tuple["Endpoint", Schemas]:
155
+ ) -> tuple["Endpoint", Schemas]:
149
156
  endpoint = deepcopy(endpoint)
150
157
  for code, response_data in data.items():
151
158
  status_code: HTTPStatus
@@ -197,7 +204,7 @@ class Endpoint:
197
204
  schemas: Schemas,
198
205
  parameters: Parameters,
199
206
  config: Config,
200
- ) -> Tuple[Union["Endpoint", ParseError], Schemas, Parameters]:
207
+ ) -> tuple[Union["Endpoint", ParseError], Schemas, Parameters]:
201
208
  """Process the defined `parameters` for an Endpoint.
202
209
 
203
210
  Any existing parameters will be ignored, so earlier instances of a parameter take precedence. PathItem
@@ -226,8 +233,8 @@ class Endpoint:
226
233
 
227
234
  endpoint = deepcopy(endpoint)
228
235
 
229
- unique_parameters: Set[Tuple[str, oai.ParameterLocation]] = set()
230
- parameters_by_location: Dict[str, List[Property]] = {
236
+ unique_parameters: set[tuple[str, oai.ParameterLocation]] = set()
237
+ parameters_by_location: dict[str, list[Property]] = {
231
238
  oai.ParameterLocation.QUERY: endpoint.query_parameters,
232
239
  oai.ParameterLocation.PATH: endpoint.path_parameters,
233
240
  oai.ParameterLocation.HEADER: endpoint.header_parameters,
@@ -304,7 +311,7 @@ class Endpoint:
304
311
  self,
305
312
  *,
306
313
  config: Config,
307
- previously_modified_params: Optional[Set[Tuple[oai.ParameterLocation, str]]] = None,
314
+ previously_modified_params: Optional[set[tuple[oai.ParameterLocation, str]]] = None,
308
315
  ) -> Union["Endpoint", ParseError]:
309
316
  """Check for conflicting parameters
310
317
 
@@ -316,7 +323,7 @@ class Endpoint:
316
323
  unique python_name.
317
324
  """
318
325
  modified_params = previously_modified_params or set()
319
- used_python_names: Dict[PythonIdentifier, Tuple[oai.ParameterLocation, Property]] = {}
326
+ used_python_names: dict[PythonIdentifier, tuple[oai.ParameterLocation, Property]] = {}
320
327
  reserved_names = ["client", "url"]
321
328
  for parameter in self.iter_all_parameters():
322
329
  location, prop = parameter
@@ -392,12 +399,12 @@ class Endpoint:
392
399
  data: oai.Operation,
393
400
  path: str,
394
401
  method: str,
395
- tag: str,
402
+ tags: list[PythonIdentifier],
396
403
  schemas: Schemas,
397
404
  parameters: Parameters,
398
- request_bodies: Dict[str, Union[oai.RequestBody, oai.Reference]],
405
+ request_bodies: dict[str, Union[oai.RequestBody, oai.Reference]],
399
406
  config: Config,
400
- ) -> Tuple[Union["Endpoint", ParseError], Schemas, Parameters]:
407
+ ) -> tuple[Union["Endpoint", ParseError], Schemas, Parameters]:
401
408
  """Construct an endpoint from the OpenAPI data"""
402
409
 
403
410
  if data.operationId is None:
@@ -412,7 +419,7 @@ class Endpoint:
412
419
  description=utils.remove_string_escapes(data.description) if data.description else "",
413
420
  name=name,
414
421
  requires_security=bool(data.security),
415
- tag=tag,
422
+ tags=tags,
416
423
  )
417
424
 
418
425
  result, schemas, parameters = Endpoint.add_parameters(
@@ -461,15 +468,15 @@ class Endpoint:
461
468
  return self.responses[0].prop.get_type_string(quoted=False)
462
469
  return f"Union[{', '.join(types)}]"
463
470
 
464
- def iter_all_parameters(self) -> Iterator[Tuple[oai.ParameterLocation, Property]]:
471
+ def iter_all_parameters(self) -> Iterator[tuple[oai.ParameterLocation, Property]]:
465
472
  """Iterate through all the parameters of this endpoint"""
466
473
  yield from ((oai.ParameterLocation.PATH, param) for param in self.path_parameters)
467
474
  yield from ((oai.ParameterLocation.QUERY, param) for param in self.query_parameters)
468
475
  yield from ((oai.ParameterLocation.HEADER, param) for param in self.header_parameters)
469
476
  yield from ((oai.ParameterLocation.COOKIE, param) for param in self.cookie_parameters)
470
477
 
471
- def list_all_parameters(self) -> List[Property]:
472
- """Return a List of all the parameters of this endpoint"""
478
+ def list_all_parameters(self) -> list[Property]:
479
+ """Return a list of all the parameters of this endpoint"""
473
480
  return (
474
481
  self.path_parameters
475
482
  + self.query_parameters
@@ -487,12 +494,12 @@ class GeneratorData:
487
494
  description: Optional[str]
488
495
  version: str
489
496
  models: Iterator[ModelProperty]
490
- errors: List[ParseError]
491
- endpoint_collections_by_tag: Dict[utils.PythonIdentifier, EndpointCollection]
497
+ errors: list[ParseError]
498
+ endpoint_collections_by_tag: dict[utils.PythonIdentifier, EndpointCollection]
492
499
  enums: Iterator[Union[EnumProperty, LiteralEnumProperty]]
493
500
 
494
501
  @staticmethod
495
- def from_dict(data: Dict[str, Any], *, config: Config) -> Union["GeneratorData", GeneratorError]:
502
+ def from_dict(data: dict[str, Any], *, config: Config) -> Union["GeneratorData", GeneratorError]:
496
503
  """Create an OpenAPI from dict"""
497
504
  try:
498
505
  openapi = oai.OpenAPI.model_validate(data)
@@ -9,12 +9,12 @@ __all__ = [
9
9
  "Parameters",
10
10
  "Property",
11
11
  "Schemas",
12
- "build_schemas",
13
12
  "build_parameters",
13
+ "build_schemas",
14
14
  "property_from_data",
15
15
  ]
16
16
 
17
- from typing import Iterable
17
+ from collections.abc import Iterable
18
18
 
19
19
  from attrs import evolve
20
20
 
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  __all__ = ["EnumProperty", "ValueType"]
4
4
 
5
- from typing import Any, ClassVar, List, Union, cast
5
+ from typing import Any, ClassVar, Union, cast
6
6
 
7
7
  from attr import evolve
8
8
  from attrs import define
@@ -99,7 +99,7 @@ class EnumProperty(PropertyProtocol):
99
99
  if value_type not in (str, int):
100
100
  return PropertyError(header=f"Unsupported enum type {value_type}", data=data), schemas
101
101
  value_list = cast(
102
- Union[List[int], List[str]], unchecked_value_list
102
+ Union[list[int], list[str]], unchecked_value_list
103
103
  ) # We checked this with all the value_types stuff
104
104
 
105
105
  if len(value_list) < len(enum): # Only one of the values was None, that becomes a union
@@ -106,10 +106,10 @@ class ListProperty(PropertyProtocol):
106
106
  return None # pragma: no cover
107
107
 
108
108
  def get_base_type_string(self, *, quoted: bool = False) -> str:
109
- return f"List[{self.inner_property.get_type_string(quoted=not self.inner_property.is_base_type)}]"
109
+ return f"list[{self.inner_property.get_type_string(quoted=not self.inner_property.is_base_type)}]"
110
110
 
111
111
  def get_base_json_type_string(self, *, quoted: bool = False) -> str:
112
- return f"List[{self.inner_property.get_type_string(json=True, quoted=not self.inner_property.is_base_type)}]"
112
+ return f"list[{self.inner_property.get_type_string(json=True, quoted=not self.inner_property.is_base_type)}]"
113
113
 
114
114
  def get_instance_type_string(self) -> str:
115
115
  """Get a string representation of runtime type that should be used for `isinstance` checks"""
@@ -125,7 +125,7 @@ class ListProperty(PropertyProtocol):
125
125
  """
126
126
  imports = super().get_imports(prefix=prefix)
127
127
  imports.update(self.inner_property.get_imports(prefix=prefix))
128
- imports.add("from typing import cast, List")
128
+ imports.add("from typing import cast")
129
129
  return imports
130
130
 
131
131
  def get_lazy_imports(self, *, prefix: str) -> set[str]:
@@ -151,7 +151,7 @@ class ListProperty(PropertyProtocol):
151
151
  if json:
152
152
  type_string = self.get_base_json_type_string()
153
153
  elif multipart:
154
- type_string = "Tuple[None, bytes, str]"
154
+ type_string = "tuple[None, bytes, str]"
155
155
  else:
156
156
  type_string = self.get_base_type_string()
157
157
 
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  __all__ = ["LiteralEnumProperty"]
4
4
 
5
- from typing import Any, ClassVar, List, Union, cast
5
+ from typing import Any, ClassVar, Union, cast
6
6
 
7
7
  from attr import evolve
8
8
  from attrs import define
@@ -98,7 +98,7 @@ class LiteralEnumProperty(PropertyProtocol):
98
98
  if value_type not in (str, int):
99
99
  return PropertyError(header=f"Unsupported enum type {value_type}", data=data), schemas
100
100
  value_list = cast(
101
- Union[List[int], List[str]], unchecked_value_list
101
+ Union[list[int], list[str]], unchecked_value_list
102
102
  ) # We checked this with all the value_types stuff
103
103
 
104
104
  if len(value_list) < len(enum): # Only one of the values was None, that becomes a union
@@ -32,7 +32,7 @@ class ModelProperty(PropertyProtocol):
32
32
  relative_imports: set[str] | None
33
33
  lazy_imports: set[str] | None
34
34
  additional_properties: Property | None
35
- _json_type_string: ClassVar[str] = "Dict[str, Any]"
35
+ _json_type_string: ClassVar[str] = "dict[str, Any]"
36
36
 
37
37
  template: ClassVar[str] = "model_property.py.jinja"
38
38
  json_is_dict: ClassVar[bool] = True
@@ -154,7 +154,6 @@ class ModelProperty(PropertyProtocol):
154
154
  imports = super().get_imports(prefix=prefix)
155
155
  imports.update(
156
156
  {
157
- "from typing import Dict",
158
157
  "from typing import cast",
159
158
  }
160
159
  )
@@ -203,7 +202,7 @@ class ModelProperty(PropertyProtocol):
203
202
  if json:
204
203
  type_string = self.get_base_json_type_string()
205
204
  elif multipart:
206
- type_string = "Tuple[None, bytes, str]"
205
+ type_string = "tuple[None, bytes, str]"
207
206
  else:
208
207
  type_string = self.get_base_type_string()
209
208
 
@@ -116,7 +116,7 @@ class PropertyProtocol(Protocol):
116
116
  if json:
117
117
  type_string = self.get_base_json_type_string(quoted=quoted)
118
118
  elif multipart:
119
- type_string = "Tuple[None, bytes, str]"
119
+ type_string = "tuple[None, bytes, str]"
120
120
  else:
121
121
  type_string = self.get_base_type_string(quoted=quoted)
122
122