openapi-python-generator 1.1.2.dev1734089718__py3-none-any.whl → 1.2.1.dev1757310194__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.
- openapi_python_generator/__init__.py +2 -1
- openapi_python_generator/__main__.py +21 -6
- openapi_python_generator/common.py +15 -0
- openapi_python_generator/generate_data.py +112 -65
- openapi_python_generator/language_converters/python/api_config_generator.py +12 -6
- openapi_python_generator/language_converters/python/generator.py +8 -4
- openapi_python_generator/language_converters/python/model_generator.py +191 -54
- openapi_python_generator/language_converters/python/service_generator.py +159 -62
- openapi_python_generator/language_converters/python/templates/aiohttp.jinja2 +10 -9
- openapi_python_generator/language_converters/python/templates/httpx.jinja2 +8 -5
- openapi_python_generator/language_converters/python/templates/models.jinja2 +5 -3
- openapi_python_generator/language_converters/python/templates/models_pydantic_2.jinja2 +5 -3
- openapi_python_generator/language_converters/python/templates/requests.jinja2 +8 -5
- openapi_python_generator/language_converters/python/templates/service.jinja2 +2 -1
- openapi_python_generator/models.py +16 -2
- openapi_python_generator/parsers/__init__.py +13 -0
- openapi_python_generator/parsers/openapi_30.py +65 -0
- openapi_python_generator/parsers/openapi_31.py +65 -0
- openapi_python_generator/py.typed +0 -0
- openapi_python_generator/version_detector.py +70 -0
- {openapi_python_generator-1.1.2.dev1734089718.dist-info → openapi_python_generator-1.2.1.dev1757310194.dist-info}/METADATA +3 -3
- openapi_python_generator-1.2.1.dev1757310194.dist-info/RECORD +32 -0
- openapi_python_generator-1.1.2.dev1734089718.dist-info/RECORD +0 -27
- {openapi_python_generator-1.1.2.dev1734089718.dist-info → openapi_python_generator-1.2.1.dev1757310194.dist-info}/LICENSE +0 -0
- {openapi_python_generator-1.1.2.dev1734089718.dist-info → openapi_python_generator-1.2.1.dev1757310194.dist-info}/WHEEL +0 -0
- {openapi_python_generator-1.1.2.dev1734089718.dist-info → openapi_python_generator-1.2.1.dev1757310194.dist-info}/entry_points.txt +0 -0
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
import itertools
|
|
2
2
|
import re
|
|
3
|
-
from typing import List
|
|
3
|
+
from typing import List, Union
|
|
4
4
|
from typing import Optional
|
|
5
5
|
|
|
6
6
|
import click
|
|
7
|
-
from openapi_pydantic.v3.v3_0 import
|
|
7
|
+
from openapi_pydantic.v3.v3_0 import (
|
|
8
|
+
Schema as Schema30,
|
|
9
|
+
Reference as Reference30,
|
|
10
|
+
Components as Components30,
|
|
11
|
+
)
|
|
12
|
+
from openapi_pydantic.v3.v3_1 import (
|
|
13
|
+
Schema as Schema31,
|
|
14
|
+
Reference as Reference31,
|
|
15
|
+
Components as Components31,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
# Type aliases for compatibility
|
|
19
|
+
Schema = Union[Schema30, Schema31]
|
|
20
|
+
Reference = Union[Reference30, Reference31]
|
|
21
|
+
Components = Union[Components30, Components31]
|
|
8
22
|
|
|
9
23
|
from openapi_python_generator.common import PydanticVersion
|
|
10
24
|
from openapi_python_generator.language_converters.python import common
|
|
11
25
|
from openapi_python_generator.language_converters.python.jinja_config import (
|
|
12
|
-
ENUM_TEMPLATE,
|
|
26
|
+
ENUM_TEMPLATE,
|
|
27
|
+
MODELS_TEMPLATE_PYDANTIC_V2,
|
|
13
28
|
)
|
|
14
29
|
from openapi_python_generator.language_converters.python.jinja_config import (
|
|
15
30
|
MODELS_TEMPLATE,
|
|
@@ -23,17 +38,35 @@ from openapi_python_generator.models import TypeConversion
|
|
|
23
38
|
|
|
24
39
|
|
|
25
40
|
def type_converter( # noqa: C901
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
41
|
+
schema: Union[Schema, Reference],
|
|
42
|
+
required: bool = False,
|
|
43
|
+
model_name: Optional[str] = None,
|
|
29
44
|
) -> TypeConversion:
|
|
30
45
|
"""
|
|
31
46
|
Converts an OpenAPI type to a Python type.
|
|
32
|
-
:param schema: Schema containing the type to be converted
|
|
47
|
+
:param schema: Schema or Reference containing the type to be converted
|
|
33
48
|
:param model_name: Name of the original model on which the type is defined
|
|
34
49
|
:param required: Flag indicating if the type is required by the class
|
|
35
50
|
:return: The converted type
|
|
36
51
|
"""
|
|
52
|
+
# Handle Reference objects by converting them to type references
|
|
53
|
+
if isinstance(schema, Reference):
|
|
54
|
+
import_type = common.normalize_symbol(schema.ref.split("/")[-1])
|
|
55
|
+
if required:
|
|
56
|
+
converted_type = import_type
|
|
57
|
+
else:
|
|
58
|
+
converted_type = f"Optional[{import_type}]"
|
|
59
|
+
|
|
60
|
+
return TypeConversion(
|
|
61
|
+
original_type=schema.ref,
|
|
62
|
+
converted_type=converted_type,
|
|
63
|
+
import_types=(
|
|
64
|
+
[f"from .{import_type} import {import_type}"]
|
|
65
|
+
if import_type != model_name
|
|
66
|
+
else None
|
|
67
|
+
),
|
|
68
|
+
)
|
|
69
|
+
|
|
37
70
|
if required:
|
|
38
71
|
pre_type = ""
|
|
39
72
|
post_type = ""
|
|
@@ -41,7 +74,11 @@ def type_converter( # noqa: C901
|
|
|
41
74
|
pre_type = "Optional["
|
|
42
75
|
post_type = "]"
|
|
43
76
|
|
|
44
|
-
original_type =
|
|
77
|
+
original_type = (
|
|
78
|
+
schema.type.value
|
|
79
|
+
if hasattr(schema.type, "value") and schema.type is not None
|
|
80
|
+
else str(schema.type) if schema.type is not None else "object"
|
|
81
|
+
)
|
|
45
82
|
import_types: Optional[List[str]] = None
|
|
46
83
|
|
|
47
84
|
if schema.allOf is not None:
|
|
@@ -70,19 +107,22 @@ def type_converter( # noqa: C901
|
|
|
70
107
|
)
|
|
71
108
|
|
|
72
109
|
original_type = (
|
|
73
|
-
|
|
110
|
+
"tuple<" + ",".join([i.original_type for i in conversions]) + ">"
|
|
74
111
|
)
|
|
75
112
|
if len(conversions) == 1:
|
|
76
113
|
converted_type = conversions[0].converted_type
|
|
77
114
|
else:
|
|
78
115
|
converted_type = (
|
|
79
|
-
|
|
116
|
+
"Tuple[" + ",".join([i.converted_type for i in conversions]) + "]"
|
|
80
117
|
)
|
|
81
118
|
|
|
82
119
|
converted_type = pre_type + converted_type + post_type
|
|
120
|
+
# Collect first import from referenced sub-schemas only (skip empty lists)
|
|
83
121
|
import_types = [
|
|
84
|
-
i.import_types[0]
|
|
85
|
-
|
|
122
|
+
i.import_types[0]
|
|
123
|
+
for i in conversions
|
|
124
|
+
if i.import_types is not None and len(i.import_types) > 0
|
|
125
|
+
] or None
|
|
86
126
|
|
|
87
127
|
elif schema.oneOf is not None or schema.anyOf is not None:
|
|
88
128
|
used = schema.oneOf if schema.oneOf is not None else schema.anyOf
|
|
@@ -102,14 +142,14 @@ def type_converter( # noqa: C901
|
|
|
102
142
|
)
|
|
103
143
|
)
|
|
104
144
|
original_type = (
|
|
105
|
-
|
|
145
|
+
"union<" + ",".join([i.original_type for i in conversions]) + ">"
|
|
106
146
|
)
|
|
107
147
|
|
|
108
148
|
if len(conversions) == 1:
|
|
109
149
|
converted_type = conversions[0].converted_type
|
|
110
150
|
else:
|
|
111
151
|
converted_type = (
|
|
112
|
-
|
|
152
|
+
"Union[" + ",".join([i.converted_type for i in conversions]) + "]"
|
|
113
153
|
)
|
|
114
154
|
|
|
115
155
|
converted_type = pre_type + converted_type + post_type
|
|
@@ -120,14 +160,15 @@ def type_converter( # noqa: C901
|
|
|
120
160
|
)
|
|
121
161
|
# We only want to auto convert to datetime if orjson is used throghout the code, otherwise we can not
|
|
122
162
|
# serialize it to JSON.
|
|
123
|
-
elif schema.type == "string" and (
|
|
124
|
-
|
|
163
|
+
elif (schema.type == "string" or str(schema.type) == "DataType.STRING") and (
|
|
164
|
+
schema.schema_format is None or not common.get_use_orjson()
|
|
125
165
|
):
|
|
126
166
|
converted_type = pre_type + "str" + post_type
|
|
127
167
|
elif (
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
168
|
+
(schema.type == "string" or str(schema.type) == "DataType.STRING")
|
|
169
|
+
and schema.schema_format is not None
|
|
170
|
+
and schema.schema_format.startswith("uuid")
|
|
171
|
+
and common.get_use_orjson()
|
|
131
172
|
):
|
|
132
173
|
if len(schema.schema_format) > 4 and schema.schema_format[4].isnumeric():
|
|
133
174
|
uuid_type = schema.schema_format.upper()
|
|
@@ -136,41 +177,131 @@ def type_converter( # noqa: C901
|
|
|
136
177
|
else:
|
|
137
178
|
converted_type = pre_type + "UUID" + post_type
|
|
138
179
|
import_types = ["from uuid import UUID"]
|
|
139
|
-
elif
|
|
180
|
+
elif (
|
|
181
|
+
schema.type == "string" or str(schema.type) == "DataType.STRING"
|
|
182
|
+
) and schema.schema_format == "date-time":
|
|
140
183
|
converted_type = pre_type + "datetime" + post_type
|
|
141
184
|
import_types = ["from datetime import datetime"]
|
|
142
|
-
elif schema.type == "integer":
|
|
185
|
+
elif schema.type == "integer" or str(schema.type) == "DataType.INTEGER":
|
|
143
186
|
converted_type = pre_type + "int" + post_type
|
|
144
|
-
elif schema.type == "number":
|
|
187
|
+
elif schema.type == "number" or str(schema.type) == "DataType.NUMBER":
|
|
145
188
|
converted_type = pre_type + "float" + post_type
|
|
146
|
-
elif schema.type == "boolean":
|
|
189
|
+
elif schema.type == "boolean" or str(schema.type) == "DataType.BOOLEAN":
|
|
147
190
|
converted_type = pre_type + "bool" + post_type
|
|
148
|
-
elif schema.type == "array":
|
|
191
|
+
elif schema.type == "array" or str(schema.type) == "DataType.ARRAY":
|
|
149
192
|
retVal = pre_type + "List["
|
|
150
193
|
if isinstance(schema.items, Reference):
|
|
151
194
|
converted_reference = _generate_property_from_reference(
|
|
152
|
-
model_name, "", schema.items, schema, required
|
|
195
|
+
model_name or "", "", schema.items, schema, required
|
|
153
196
|
)
|
|
154
197
|
import_types = converted_reference.type.import_types
|
|
155
198
|
original_type = "array<" + converted_reference.type.original_type + ">"
|
|
156
199
|
retVal += converted_reference.type.converted_type
|
|
157
200
|
elif isinstance(schema.items, Schema):
|
|
158
|
-
|
|
159
|
-
|
|
201
|
+
type_str = schema.items.type
|
|
202
|
+
if hasattr(type_str, "value"):
|
|
203
|
+
type_value = str(type_str.value) if type_str is not None else "unknown"
|
|
204
|
+
else:
|
|
205
|
+
type_value = str(type_str) if type_str is not None else "unknown"
|
|
206
|
+
original_type = "array<" + type_value + ">"
|
|
160
207
|
retVal += type_converter(schema.items, True).converted_type
|
|
161
208
|
else:
|
|
162
209
|
original_type = "array<unknown>"
|
|
163
210
|
retVal += "Any"
|
|
164
211
|
|
|
165
212
|
converted_type = retVal + "]" + post_type
|
|
166
|
-
elif schema.type == "object":
|
|
213
|
+
elif schema.type == "object" or str(schema.type) == "DataType.OBJECT":
|
|
167
214
|
converted_type = pre_type + "Dict[str, Any]" + post_type
|
|
168
|
-
elif schema.type == "null":
|
|
215
|
+
elif schema.type == "null" or str(schema.type) == "DataType.NULL":
|
|
169
216
|
converted_type = pre_type + "None" + post_type
|
|
170
217
|
elif schema.type is None:
|
|
171
218
|
converted_type = pre_type + "Any" + post_type
|
|
172
219
|
else:
|
|
173
|
-
|
|
220
|
+
# Handle DataType enum types as strings
|
|
221
|
+
if hasattr(schema.type, "value"):
|
|
222
|
+
# Single DataType enum
|
|
223
|
+
if schema.type.value == "string":
|
|
224
|
+
# Check for UUID format first
|
|
225
|
+
if (
|
|
226
|
+
schema.schema_format is not None
|
|
227
|
+
and schema.schema_format.startswith("uuid")
|
|
228
|
+
and common.get_use_orjson()
|
|
229
|
+
):
|
|
230
|
+
if (
|
|
231
|
+
len(schema.schema_format) > 4
|
|
232
|
+
and schema.schema_format[4].isnumeric()
|
|
233
|
+
):
|
|
234
|
+
uuid_type = schema.schema_format.upper()
|
|
235
|
+
converted_type = pre_type + uuid_type + post_type
|
|
236
|
+
import_types = ["from pydantic import " + uuid_type]
|
|
237
|
+
else:
|
|
238
|
+
converted_type = pre_type + "UUID" + post_type
|
|
239
|
+
import_types = ["from uuid import UUID"]
|
|
240
|
+
# Check for date-time format
|
|
241
|
+
elif schema.schema_format == "date-time":
|
|
242
|
+
converted_type = pre_type + "datetime" + post_type
|
|
243
|
+
import_types = ["from datetime import datetime"]
|
|
244
|
+
else:
|
|
245
|
+
converted_type = pre_type + "str" + post_type
|
|
246
|
+
elif schema.type.value == "integer":
|
|
247
|
+
converted_type = pre_type + "int" + post_type
|
|
248
|
+
elif schema.type.value == "number":
|
|
249
|
+
converted_type = pre_type + "float" + post_type
|
|
250
|
+
elif schema.type.value == "boolean":
|
|
251
|
+
converted_type = pre_type + "bool" + post_type
|
|
252
|
+
elif schema.type.value == "array":
|
|
253
|
+
converted_type = pre_type + "List[Any]" + post_type
|
|
254
|
+
elif schema.type.value == "object":
|
|
255
|
+
converted_type = pre_type + "Dict[str, Any]" + post_type
|
|
256
|
+
elif schema.type.value == "null":
|
|
257
|
+
converted_type = pre_type + "None" + post_type
|
|
258
|
+
else:
|
|
259
|
+
converted_type = pre_type + "str" + post_type # Default fallback
|
|
260
|
+
elif isinstance(schema.type, list) and len(schema.type) > 0:
|
|
261
|
+
# List of DataType enums - use first one
|
|
262
|
+
first_type = schema.type[0]
|
|
263
|
+
if hasattr(first_type, "value"):
|
|
264
|
+
if first_type.value == "string":
|
|
265
|
+
# Check for UUID format first
|
|
266
|
+
if (
|
|
267
|
+
schema.schema_format is not None
|
|
268
|
+
and schema.schema_format.startswith("uuid")
|
|
269
|
+
and common.get_use_orjson()
|
|
270
|
+
):
|
|
271
|
+
if (
|
|
272
|
+
len(schema.schema_format) > 4
|
|
273
|
+
and schema.schema_format[4].isnumeric()
|
|
274
|
+
):
|
|
275
|
+
uuid_type = schema.schema_format.upper()
|
|
276
|
+
converted_type = pre_type + uuid_type + post_type
|
|
277
|
+
import_types = ["from pydantic import " + uuid_type]
|
|
278
|
+
else:
|
|
279
|
+
converted_type = pre_type + "UUID" + post_type
|
|
280
|
+
import_types = ["from uuid import UUID"]
|
|
281
|
+
# Check for date-time format
|
|
282
|
+
elif schema.schema_format == "date-time":
|
|
283
|
+
converted_type = pre_type + "datetime" + post_type
|
|
284
|
+
import_types = ["from datetime import datetime"]
|
|
285
|
+
else:
|
|
286
|
+
converted_type = pre_type + "str" + post_type
|
|
287
|
+
elif first_type.value == "integer":
|
|
288
|
+
converted_type = pre_type + "int" + post_type
|
|
289
|
+
elif first_type.value == "number":
|
|
290
|
+
converted_type = pre_type + "float" + post_type
|
|
291
|
+
elif first_type.value == "boolean":
|
|
292
|
+
converted_type = pre_type + "bool" + post_type
|
|
293
|
+
elif first_type.value == "array":
|
|
294
|
+
converted_type = pre_type + "List[Any]" + post_type
|
|
295
|
+
elif first_type.value == "object":
|
|
296
|
+
converted_type = pre_type + "Dict[str, Any]" + post_type
|
|
297
|
+
elif first_type.value == "null":
|
|
298
|
+
converted_type = pre_type + "None" + post_type
|
|
299
|
+
else:
|
|
300
|
+
converted_type = pre_type + "str" + post_type # Default fallback
|
|
301
|
+
else:
|
|
302
|
+
converted_type = pre_type + "str" + post_type # Default fallback
|
|
303
|
+
else:
|
|
304
|
+
converted_type = pre_type + "str" + post_type # Default fallback
|
|
174
305
|
|
|
175
306
|
return TypeConversion(
|
|
176
307
|
original_type=original_type,
|
|
@@ -180,7 +311,7 @@ def type_converter( # noqa: C901
|
|
|
180
311
|
|
|
181
312
|
|
|
182
313
|
def _generate_property_from_schema(
|
|
183
|
-
|
|
314
|
+
model_name: str, name: str, schema: Schema, parent_schema: Optional[Schema] = None
|
|
184
315
|
) -> Property:
|
|
185
316
|
"""
|
|
186
317
|
Generates a property from a schema. It takes the type of the schema and converts it to a python type, and then
|
|
@@ -192,9 +323,9 @@ def _generate_property_from_schema(
|
|
|
192
323
|
:return: Property
|
|
193
324
|
"""
|
|
194
325
|
required = (
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
326
|
+
parent_schema is not None
|
|
327
|
+
and parent_schema.required is not None
|
|
328
|
+
and name in parent_schema.required
|
|
198
329
|
)
|
|
199
330
|
|
|
200
331
|
import_type = None
|
|
@@ -211,11 +342,11 @@ def _generate_property_from_schema(
|
|
|
211
342
|
|
|
212
343
|
|
|
213
344
|
def _generate_property_from_reference(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
345
|
+
model_name: str,
|
|
346
|
+
name: str,
|
|
347
|
+
reference: Reference,
|
|
348
|
+
parent_schema: Optional[Schema] = None,
|
|
349
|
+
force_required: bool = False,
|
|
219
350
|
) -> Property:
|
|
220
351
|
"""
|
|
221
352
|
Generates a property from a reference. It takes the name of the reference as the type, and then
|
|
@@ -227,26 +358,26 @@ def _generate_property_from_reference(
|
|
|
227
358
|
:return: Property and model to be imported by the file
|
|
228
359
|
"""
|
|
229
360
|
required = (
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
361
|
+
parent_schema is not None
|
|
362
|
+
and parent_schema.required is not None
|
|
363
|
+
and name in parent_schema.required
|
|
364
|
+
) or force_required
|
|
234
365
|
import_model = common.normalize_symbol(reference.ref.split("/")[-1])
|
|
235
366
|
|
|
236
367
|
if import_model == model_name:
|
|
237
368
|
type_conv = TypeConversion(
|
|
238
369
|
original_type=reference.ref,
|
|
239
|
-
converted_type=
|
|
240
|
-
|
|
241
|
-
|
|
370
|
+
converted_type=(
|
|
371
|
+
import_model if required else 'Optional["' + import_model + '"]'
|
|
372
|
+
),
|
|
242
373
|
import_types=None,
|
|
243
374
|
)
|
|
244
375
|
else:
|
|
245
376
|
type_conv = TypeConversion(
|
|
246
377
|
original_type=reference.ref,
|
|
247
|
-
converted_type=
|
|
248
|
-
|
|
249
|
-
|
|
378
|
+
converted_type=(
|
|
379
|
+
import_model if required else "Optional[" + import_model + "]"
|
|
380
|
+
),
|
|
250
381
|
import_types=[f"from .{import_model} import {import_model}"],
|
|
251
382
|
)
|
|
252
383
|
return Property(
|
|
@@ -258,13 +389,15 @@ def _generate_property_from_reference(
|
|
|
258
389
|
)
|
|
259
390
|
|
|
260
391
|
|
|
261
|
-
def generate_models(
|
|
392
|
+
def generate_models(
|
|
393
|
+
components: Components, pydantic_version: PydanticVersion = PydanticVersion.V2
|
|
394
|
+
) -> List[Model]:
|
|
262
395
|
"""
|
|
263
|
-
Receives components from an OpenAPI 3.0 specification and generates the models from it.
|
|
396
|
+
Receives components from an OpenAPI 3.0+ specification and generates the models from it.
|
|
264
397
|
It does so, by iterating over the components.schemas dictionary. For each schema, it checks if
|
|
265
398
|
it is a normal schema (i.e. simple type like string, integer, etc.), a reference to another schema, or
|
|
266
399
|
an array of types/references. It then computes pydantic models from it using jinja2
|
|
267
|
-
:param components: The components from an OpenAPI 3.0 specification.
|
|
400
|
+
:param components: The components from an OpenAPI 3.0+ specification.
|
|
268
401
|
:param pydantic_version: The version of pydantic to use.
|
|
269
402
|
:return: A list of models.
|
|
270
403
|
"""
|
|
@@ -277,7 +410,7 @@ def generate_models(components: Components, pydantic_version: PydanticVersion =
|
|
|
277
410
|
for schema_name, schema_or_reference in components.schemas.items():
|
|
278
411
|
name = common.normalize_symbol(schema_name)
|
|
279
412
|
if schema_or_reference.enum is not None:
|
|
280
|
-
value_dict = schema_or_reference.
|
|
413
|
+
value_dict = schema_or_reference.model_dump()
|
|
281
414
|
regex = re.compile(r"[\s\/=\*\+]+")
|
|
282
415
|
value_dict["enum"] = [
|
|
283
416
|
re.sub(regex, "_", i) if isinstance(i, str) else f"value_{i}"
|
|
@@ -316,7 +449,11 @@ def generate_models(components: Components, pydantic_version: PydanticVersion =
|
|
|
316
449
|
)
|
|
317
450
|
properties.append(conv_property)
|
|
318
451
|
|
|
319
|
-
template_name =
|
|
452
|
+
template_name = (
|
|
453
|
+
MODELS_TEMPLATE_PYDANTIC_V2
|
|
454
|
+
if pydantic_version == PydanticVersion.V2
|
|
455
|
+
else MODELS_TEMPLATE
|
|
456
|
+
)
|
|
320
457
|
|
|
321
458
|
generated_content = jinja_env.get_template(template_name).render(
|
|
322
459
|
schema_name=name, schema=schema_or_reference, properties=properties
|