datamodel-code-generator 0.27.1__py3-none-any.whl → 0.27.3__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.
Potentially problematic release.
This version of datamodel-code-generator might be problematic. Click here for more details.
- datamodel_code_generator/__init__.py +159 -190
- datamodel_code_generator/__main__.py +151 -173
- datamodel_code_generator/arguments.py +227 -230
- datamodel_code_generator/format.py +77 -99
- datamodel_code_generator/http.py +9 -10
- datamodel_code_generator/imports.py +57 -64
- datamodel_code_generator/model/__init__.py +26 -31
- datamodel_code_generator/model/base.py +94 -127
- datamodel_code_generator/model/dataclass.py +58 -59
- datamodel_code_generator/model/enum.py +34 -30
- datamodel_code_generator/model/imports.py +13 -11
- datamodel_code_generator/model/msgspec.py +112 -126
- datamodel_code_generator/model/pydantic/__init__.py +14 -27
- datamodel_code_generator/model/pydantic/base_model.py +120 -139
- datamodel_code_generator/model/pydantic/custom_root_type.py +2 -2
- datamodel_code_generator/model/pydantic/dataclass.py +6 -4
- datamodel_code_generator/model/pydantic/imports.py +35 -33
- datamodel_code_generator/model/pydantic/types.py +86 -117
- datamodel_code_generator/model/pydantic_v2/__init__.py +17 -17
- datamodel_code_generator/model/pydantic_v2/base_model.py +118 -119
- datamodel_code_generator/model/pydantic_v2/imports.py +5 -3
- datamodel_code_generator/model/pydantic_v2/root_model.py +6 -6
- datamodel_code_generator/model/pydantic_v2/types.py +8 -7
- datamodel_code_generator/model/rootmodel.py +1 -1
- datamodel_code_generator/model/scalar.py +33 -32
- datamodel_code_generator/model/typed_dict.py +42 -41
- datamodel_code_generator/model/types.py +19 -17
- datamodel_code_generator/model/union.py +21 -17
- datamodel_code_generator/parser/__init__.py +12 -11
- datamodel_code_generator/parser/base.py +320 -492
- datamodel_code_generator/parser/graphql.py +80 -111
- datamodel_code_generator/parser/jsonschema.py +422 -580
- datamodel_code_generator/parser/openapi.py +175 -204
- datamodel_code_generator/pydantic_patch.py +8 -9
- datamodel_code_generator/reference.py +192 -274
- datamodel_code_generator/types.py +147 -182
- datamodel_code_generator/util.py +22 -26
- {datamodel_code_generator-0.27.1.dist-info → datamodel_code_generator-0.27.3.dist-info}/METADATA +12 -11
- datamodel_code_generator-0.27.3.dist-info/RECORD +59 -0
- datamodel_code_generator-0.27.1.dist-info/RECORD +0 -59
- {datamodel_code_generator-0.27.1.dist-info → datamodel_code_generator-0.27.3.dist-info}/WHEEL +0 -0
- {datamodel_code_generator-0.27.1.dist-info → datamodel_code_generator-0.27.3.dist-info}/entry_points.txt +0 -0
- {datamodel_code_generator-0.27.1.dist-info → datamodel_code_generator-0.27.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import operator
|
|
1
4
|
import re
|
|
2
5
|
import sys
|
|
3
6
|
from abc import ABC, abstractmethod
|
|
@@ -7,20 +10,15 @@ from pathlib import Path
|
|
|
7
10
|
from typing import (
|
|
8
11
|
Any,
|
|
9
12
|
Callable,
|
|
10
|
-
DefaultDict,
|
|
11
13
|
Dict,
|
|
12
14
|
Iterable,
|
|
13
15
|
Iterator,
|
|
14
|
-
List,
|
|
15
16
|
Mapping,
|
|
16
17
|
NamedTuple,
|
|
17
18
|
Optional,
|
|
18
19
|
Sequence,
|
|
19
20
|
Set,
|
|
20
|
-
Tuple,
|
|
21
|
-
Type,
|
|
22
21
|
TypeVar,
|
|
23
|
-
Union,
|
|
24
22
|
)
|
|
25
23
|
from urllib.parse import ParseResult
|
|
26
24
|
|
|
@@ -56,25 +54,23 @@ from datamodel_code_generator.reference import ModelResolver, Reference
|
|
|
56
54
|
from datamodel_code_generator.types import DataType, DataTypeManager, StrictTypes
|
|
57
55
|
from datamodel_code_generator.util import Protocol, runtime_checkable
|
|
58
56
|
|
|
59
|
-
SPECIAL_PATH_FORMAT: str =
|
|
57
|
+
SPECIAL_PATH_FORMAT: str = "#-datamodel-code-generator-#-{}-#-special-#"
|
|
60
58
|
|
|
61
59
|
|
|
62
|
-
def get_special_path(keyword: str, path:
|
|
60
|
+
def get_special_path(keyword: str, path: list[str]) -> list[str]:
|
|
63
61
|
return [*path, SPECIAL_PATH_FORMAT.format(keyword)]
|
|
64
62
|
|
|
65
63
|
|
|
66
|
-
escape_characters = str.maketrans(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
)
|
|
64
|
+
escape_characters = str.maketrans({
|
|
65
|
+
"\u0000": r"\x00", # Null byte
|
|
66
|
+
"\\": r"\\",
|
|
67
|
+
"'": r"\'",
|
|
68
|
+
"\b": r"\b",
|
|
69
|
+
"\f": r"\f",
|
|
70
|
+
"\n": r"\n",
|
|
71
|
+
"\r": r"\r",
|
|
72
|
+
"\t": r"\t",
|
|
73
|
+
})
|
|
78
74
|
|
|
79
75
|
|
|
80
76
|
def to_hashable(item: Any) -> Any:
|
|
@@ -86,7 +82,7 @@ def to_hashable(item: Any) -> Any:
|
|
|
86
82
|
),
|
|
87
83
|
):
|
|
88
84
|
return tuple(sorted(to_hashable(i) for i in item))
|
|
89
|
-
|
|
85
|
+
if isinstance(item, dict):
|
|
90
86
|
return tuple(
|
|
91
87
|
sorted(
|
|
92
88
|
(
|
|
@@ -96,15 +92,15 @@ def to_hashable(item: Any) -> Any:
|
|
|
96
92
|
for k, v in item.items()
|
|
97
93
|
)
|
|
98
94
|
)
|
|
99
|
-
|
|
95
|
+
if isinstance(item, set): # pragma: no cover
|
|
100
96
|
return frozenset(to_hashable(i) for i in item)
|
|
101
|
-
|
|
97
|
+
if isinstance(item, BaseModel):
|
|
102
98
|
return to_hashable(item.dict())
|
|
103
99
|
return item
|
|
104
100
|
|
|
105
101
|
|
|
106
|
-
def dump_templates(templates:
|
|
107
|
-
return
|
|
102
|
+
def dump_templates(templates: list[DataModel]) -> str:
|
|
103
|
+
return "\n\n\n".join(str(m) for m in templates)
|
|
108
104
|
|
|
109
105
|
|
|
110
106
|
ReferenceMapSet = Dict[str, Set[str]]
|
|
@@ -113,25 +109,23 @@ SortedDataModels = Dict[str, DataModel]
|
|
|
113
109
|
MAX_RECURSION_COUNT: int = sys.getrecursionlimit()
|
|
114
110
|
|
|
115
111
|
|
|
116
|
-
def sort_data_models(
|
|
117
|
-
unsorted_data_models:
|
|
118
|
-
sorted_data_models:
|
|
119
|
-
require_update_action_models:
|
|
112
|
+
def sort_data_models( # noqa: PLR0912
|
|
113
|
+
unsorted_data_models: list[DataModel],
|
|
114
|
+
sorted_data_models: SortedDataModels | None = None,
|
|
115
|
+
require_update_action_models: list[str] | None = None,
|
|
120
116
|
recursion_count: int = MAX_RECURSION_COUNT,
|
|
121
|
-
) ->
|
|
117
|
+
) -> tuple[list[DataModel], SortedDataModels, list[str]]:
|
|
122
118
|
if sorted_data_models is None:
|
|
123
119
|
sorted_data_models = OrderedDict()
|
|
124
120
|
if require_update_action_models is None:
|
|
125
121
|
require_update_action_models = []
|
|
126
122
|
sorted_model_count: int = len(sorted_data_models)
|
|
127
123
|
|
|
128
|
-
unresolved_references:
|
|
124
|
+
unresolved_references: list[DataModel] = []
|
|
129
125
|
for model in unsorted_data_models:
|
|
130
126
|
if not model.reference_classes:
|
|
131
127
|
sorted_data_models[model.path] = model
|
|
132
|
-
elif (
|
|
133
|
-
model.path in model.reference_classes and len(model.reference_classes) == 1
|
|
134
|
-
): # only self-referencing
|
|
128
|
+
elif model.path in model.reference_classes and len(model.reference_classes) == 1: # only self-referencing
|
|
135
129
|
sorted_data_models[model.path] = model
|
|
136
130
|
require_update_action_models.append(model.path)
|
|
137
131
|
elif (
|
|
@@ -156,32 +150,25 @@ def sort_data_models(
|
|
|
156
150
|
|
|
157
151
|
# sort on base_class dependency
|
|
158
152
|
while True:
|
|
159
|
-
ordered_models:
|
|
153
|
+
ordered_models: list[tuple[int, DataModel]] = []
|
|
160
154
|
unresolved_reference_model_names = [m.path for m in unresolved_references]
|
|
161
155
|
for model in unresolved_references:
|
|
162
156
|
indexes = [
|
|
163
157
|
unresolved_reference_model_names.index(b.reference.path)
|
|
164
158
|
for b in model.base_classes
|
|
165
|
-
if b.reference
|
|
166
|
-
and b.reference.path in unresolved_reference_model_names
|
|
159
|
+
if b.reference and b.reference.path in unresolved_reference_model_names
|
|
167
160
|
]
|
|
168
161
|
if indexes:
|
|
169
|
-
ordered_models.append(
|
|
170
|
-
(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
)
|
|
174
|
-
)
|
|
162
|
+
ordered_models.append((
|
|
163
|
+
max(indexes),
|
|
164
|
+
model,
|
|
165
|
+
))
|
|
175
166
|
else:
|
|
176
|
-
ordered_models.append(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
)
|
|
182
|
-
sorted_unresolved_models = [
|
|
183
|
-
m[1] for m in sorted(ordered_models, key=lambda m: m[0])
|
|
184
|
-
]
|
|
167
|
+
ordered_models.append((
|
|
168
|
+
-1,
|
|
169
|
+
model,
|
|
170
|
+
))
|
|
171
|
+
sorted_unresolved_models = [m[1] for m in sorted(ordered_models, key=operator.itemgetter(0))]
|
|
185
172
|
if sorted_unresolved_models == unresolved_references:
|
|
186
173
|
break
|
|
187
174
|
unresolved_references = sorted_unresolved_models
|
|
@@ -189,15 +176,9 @@ def sort_data_models(
|
|
|
189
176
|
# circular reference
|
|
190
177
|
unsorted_data_model_names = set(unresolved_reference_model_names)
|
|
191
178
|
for model in unresolved_references:
|
|
192
|
-
unresolved_model = (
|
|
193
|
-
|
|
194
|
-
)
|
|
195
|
-
base_models = [
|
|
196
|
-
getattr(s.reference, 'path', None) for s in model.base_classes
|
|
197
|
-
]
|
|
198
|
-
update_action_parent = set(require_update_action_models).intersection(
|
|
199
|
-
base_models
|
|
200
|
-
)
|
|
179
|
+
unresolved_model = model.reference_classes - {model.path} - set(sorted_data_models)
|
|
180
|
+
base_models = [getattr(s.reference, "path", None) for s in model.base_classes]
|
|
181
|
+
update_action_parent = set(require_update_action_models).intersection(base_models)
|
|
201
182
|
if not unresolved_model:
|
|
202
183
|
sorted_data_models[model.path] = model
|
|
203
184
|
if update_action_parent:
|
|
@@ -208,22 +189,22 @@ def sort_data_models(
|
|
|
208
189
|
require_update_action_models.append(model.path)
|
|
209
190
|
continue
|
|
210
191
|
# unresolved
|
|
211
|
-
unresolved_classes =
|
|
212
|
-
f
|
|
213
|
-
for item in unresolved_references
|
|
192
|
+
unresolved_classes = ", ".join(
|
|
193
|
+
f"[class: {item.path} references: {item.reference_classes}]" for item in unresolved_references
|
|
214
194
|
)
|
|
215
|
-
|
|
195
|
+
msg = f"A Parser can not resolve classes: {unresolved_classes}."
|
|
196
|
+
raise Exception(msg) # noqa: TRY002
|
|
216
197
|
return unresolved_references, sorted_data_models, require_update_action_models
|
|
217
198
|
|
|
218
199
|
|
|
219
|
-
def relative(current_module: str, reference: str) ->
|
|
200
|
+
def relative(current_module: str, reference: str) -> tuple[str, str]:
|
|
220
201
|
"""Find relative module path."""
|
|
221
202
|
|
|
222
|
-
current_module_path = current_module.split(
|
|
223
|
-
*reference_path, name = reference.split(
|
|
203
|
+
current_module_path = current_module.split(".") if current_module else []
|
|
204
|
+
*reference_path, name = reference.split(".")
|
|
224
205
|
|
|
225
206
|
if current_module_path == reference_path:
|
|
226
|
-
return
|
|
207
|
+
return "", ""
|
|
227
208
|
|
|
228
209
|
i = 0
|
|
229
210
|
for x, y in zip(current_module_path, reference_path):
|
|
@@ -231,65 +212,58 @@ def relative(current_module: str, reference: str) -> Tuple[str, str]:
|
|
|
231
212
|
break
|
|
232
213
|
i += 1
|
|
233
214
|
|
|
234
|
-
left =
|
|
235
|
-
right =
|
|
215
|
+
left = "." * (len(current_module_path) - i)
|
|
216
|
+
right = ".".join(reference_path[i:])
|
|
236
217
|
|
|
237
218
|
if not left:
|
|
238
|
-
left =
|
|
219
|
+
left = "."
|
|
239
220
|
if not right:
|
|
240
221
|
right = name
|
|
241
|
-
elif
|
|
242
|
-
extra, right = right.rsplit(
|
|
222
|
+
elif "." in right:
|
|
223
|
+
extra, right = right.rsplit(".", 1)
|
|
243
224
|
left += extra
|
|
244
225
|
|
|
245
226
|
return left, right
|
|
246
227
|
|
|
247
228
|
|
|
248
|
-
def exact_import(from_: str, import_: str, short_name: str) ->
|
|
249
|
-
if from_ == len(from_) *
|
|
229
|
+
def exact_import(from_: str, import_: str, short_name: str) -> tuple[str, str]:
|
|
230
|
+
if from_ == len(from_) * ".":
|
|
250
231
|
# Prevents "from . import foo" becoming "from ..foo import Foo"
|
|
251
232
|
# or "from .. import foo" becoming "from ...foo import Foo"
|
|
252
233
|
# when our imported module has the same parent
|
|
253
|
-
return f
|
|
254
|
-
return f
|
|
234
|
+
return f"{from_}{import_}", short_name
|
|
235
|
+
return f"{from_}.{import_}", short_name
|
|
255
236
|
|
|
256
237
|
|
|
257
238
|
@runtime_checkable
|
|
258
239
|
class Child(Protocol):
|
|
259
240
|
@property
|
|
260
|
-
def parent(self) ->
|
|
241
|
+
def parent(self) -> Any | None:
|
|
261
242
|
raise NotImplementedError
|
|
262
243
|
|
|
263
244
|
|
|
264
|
-
T = TypeVar(
|
|
245
|
+
T = TypeVar("T")
|
|
265
246
|
|
|
266
247
|
|
|
267
|
-
def get_most_of_parent(value: Any, type_:
|
|
248
|
+
def get_most_of_parent(value: Any, type_: type[T] | None = None) -> T | None:
|
|
268
249
|
if isinstance(value, Child) and (type_ is None or not isinstance(value, type_)):
|
|
269
250
|
return get_most_of_parent(value.parent, type_)
|
|
270
251
|
return value
|
|
271
252
|
|
|
272
253
|
|
|
273
254
|
def title_to_class_name(title: str) -> str:
|
|
274
|
-
classname = re.sub(
|
|
275
|
-
|
|
276
|
-
return classname
|
|
255
|
+
classname = re.sub(r"[^A-Za-z0-9]+", " ", title)
|
|
256
|
+
return "".join(x for x in classname.title() if not x.isspace())
|
|
277
257
|
|
|
278
258
|
|
|
279
|
-
def _find_base_classes(model: DataModel) ->
|
|
280
|
-
return [
|
|
281
|
-
b.reference.source
|
|
282
|
-
for b in model.base_classes
|
|
283
|
-
if b.reference and isinstance(b.reference.source, DataModel)
|
|
284
|
-
]
|
|
259
|
+
def _find_base_classes(model: DataModel) -> list[DataModel]:
|
|
260
|
+
return [b.reference.source for b in model.base_classes if b.reference and isinstance(b.reference.source, DataModel)]
|
|
285
261
|
|
|
286
262
|
|
|
287
|
-
def _find_field(
|
|
288
|
-
original_name: str, models: List[DataModel]
|
|
289
|
-
) -> Optional[DataModelFieldBase]:
|
|
263
|
+
def _find_field(original_name: str, models: list[DataModel]) -> DataModelFieldBase | None:
|
|
290
264
|
def _find_field_and_base_classes(
|
|
291
265
|
model_: DataModel,
|
|
292
|
-
) ->
|
|
266
|
+
) -> tuple[DataModelFieldBase | None, list[DataModel]]:
|
|
293
267
|
for field_ in model_.fields:
|
|
294
268
|
if field_.original_name == original_name:
|
|
295
269
|
return field_, []
|
|
@@ -299,18 +273,16 @@ def _find_field(
|
|
|
299
273
|
field, base_models = _find_field_and_base_classes(model)
|
|
300
274
|
if field:
|
|
301
275
|
return field
|
|
302
|
-
models.extend(base_models) # pragma: no cover
|
|
276
|
+
models.extend(base_models) # pragma: no cover # noqa: B909
|
|
303
277
|
|
|
304
278
|
return None # pragma: no cover
|
|
305
279
|
|
|
306
280
|
|
|
307
|
-
def _copy_data_types(data_types:
|
|
308
|
-
copied_data_types:
|
|
281
|
+
def _copy_data_types(data_types: list[DataType]) -> list[DataType]:
|
|
282
|
+
copied_data_types: list[DataType] = []
|
|
309
283
|
for data_type_ in data_types:
|
|
310
284
|
if data_type_.reference:
|
|
311
|
-
copied_data_types.append(
|
|
312
|
-
data_type_.__class__(reference=data_type_.reference)
|
|
313
|
-
)
|
|
285
|
+
copied_data_types.append(data_type_.__class__(reference=data_type_.reference))
|
|
314
286
|
elif data_type_.data_types: # pragma: no cover
|
|
315
287
|
copied_data_type = data_type_.copy()
|
|
316
288
|
copied_data_type.data_types = _copy_data_types(data_type_.data_types)
|
|
@@ -322,7 +294,7 @@ def _copy_data_types(data_types: List[DataType]) -> List[DataType]:
|
|
|
322
294
|
|
|
323
295
|
class Result(BaseModel):
|
|
324
296
|
body: str
|
|
325
|
-
source: Optional[Path] = None
|
|
297
|
+
source: Optional[Path] = None # noqa: UP045
|
|
326
298
|
|
|
327
299
|
|
|
328
300
|
class Source(BaseModel):
|
|
@@ -330,7 +302,7 @@ class Source(BaseModel):
|
|
|
330
302
|
text: str
|
|
331
303
|
|
|
332
304
|
@classmethod
|
|
333
|
-
def from_path(cls, path: Path, base_path: Path, encoding: str) ->
|
|
305
|
+
def from_path(cls, path: Path, base_path: Path, encoding: str) -> Source:
|
|
334
306
|
return cls(
|
|
335
307
|
path=path.relative_to(base_path),
|
|
336
308
|
text=path.read_text(encoding=encoding),
|
|
@@ -338,80 +310,78 @@ class Source(BaseModel):
|
|
|
338
310
|
|
|
339
311
|
|
|
340
312
|
class Parser(ABC):
|
|
341
|
-
def __init__(
|
|
313
|
+
def __init__( # noqa: PLR0913, PLR0915
|
|
342
314
|
self,
|
|
343
|
-
source:
|
|
315
|
+
source: str | Path | list[Path] | ParseResult,
|
|
344
316
|
*,
|
|
345
|
-
data_model_type:
|
|
346
|
-
data_model_root_type:
|
|
347
|
-
data_type_manager_type:
|
|
348
|
-
data_model_field_type:
|
|
349
|
-
base_class:
|
|
350
|
-
additional_imports:
|
|
351
|
-
custom_template_dir:
|
|
352
|
-
extra_template_data:
|
|
317
|
+
data_model_type: type[DataModel] = pydantic_model.BaseModel,
|
|
318
|
+
data_model_root_type: type[DataModel] = pydantic_model.CustomRootType,
|
|
319
|
+
data_type_manager_type: type[DataTypeManager] = pydantic_model.DataTypeManager,
|
|
320
|
+
data_model_field_type: type[DataModelFieldBase] = pydantic_model.DataModelField,
|
|
321
|
+
base_class: str | None = None,
|
|
322
|
+
additional_imports: list[str] | None = None,
|
|
323
|
+
custom_template_dir: Path | None = None,
|
|
324
|
+
extra_template_data: defaultdict[str, dict[str, Any]] | None = None,
|
|
353
325
|
target_python_version: PythonVersion = PythonVersion.PY_38,
|
|
354
|
-
dump_resolve_reference_action:
|
|
326
|
+
dump_resolve_reference_action: Callable[[Iterable[str]], str] | None = None,
|
|
355
327
|
validation: bool = False,
|
|
356
328
|
field_constraints: bool = False,
|
|
357
329
|
snake_case_field: bool = False,
|
|
358
330
|
strip_default_none: bool = False,
|
|
359
|
-
aliases:
|
|
331
|
+
aliases: Mapping[str, str] | None = None,
|
|
360
332
|
allow_population_by_field_name: bool = False,
|
|
361
333
|
apply_default_values_for_required_fields: bool = False,
|
|
362
334
|
allow_extra_fields: bool = False,
|
|
363
335
|
force_optional_for_required_fields: bool = False,
|
|
364
|
-
class_name:
|
|
336
|
+
class_name: str | None = None,
|
|
365
337
|
use_standard_collections: bool = False,
|
|
366
|
-
base_path:
|
|
338
|
+
base_path: Path | None = None,
|
|
367
339
|
use_schema_description: bool = False,
|
|
368
340
|
use_field_description: bool = False,
|
|
369
341
|
use_default_kwarg: bool = False,
|
|
370
342
|
reuse_model: bool = False,
|
|
371
|
-
encoding: str =
|
|
372
|
-
enum_field_as_literal:
|
|
343
|
+
encoding: str = "utf-8",
|
|
344
|
+
enum_field_as_literal: LiteralType | None = None,
|
|
373
345
|
set_default_enum_member: bool = False,
|
|
374
346
|
use_subclass_enum: bool = False,
|
|
375
347
|
strict_nullable: bool = False,
|
|
376
348
|
use_generic_container_types: bool = False,
|
|
377
349
|
enable_faux_immutability: bool = False,
|
|
378
|
-
remote_text_cache:
|
|
350
|
+
remote_text_cache: DefaultPutDict[str, str] | None = None,
|
|
379
351
|
disable_appending_item_suffix: bool = False,
|
|
380
|
-
strict_types:
|
|
381
|
-
empty_enum_field_name:
|
|
382
|
-
custom_class_name_generator:
|
|
383
|
-
|
|
384
|
-
] = title_to_class_name,
|
|
385
|
-
field_extra_keys: Optional[Set[str]] = None,
|
|
352
|
+
strict_types: Sequence[StrictTypes] | None = None,
|
|
353
|
+
empty_enum_field_name: str | None = None,
|
|
354
|
+
custom_class_name_generator: Callable[[str], str] | None = title_to_class_name,
|
|
355
|
+
field_extra_keys: set[str] | None = None,
|
|
386
356
|
field_include_all_keys: bool = False,
|
|
387
|
-
field_extra_keys_without_x_prefix:
|
|
388
|
-
wrap_string_literal:
|
|
357
|
+
field_extra_keys_without_x_prefix: set[str] | None = None,
|
|
358
|
+
wrap_string_literal: bool | None = None,
|
|
389
359
|
use_title_as_name: bool = False,
|
|
390
360
|
use_operation_id_as_name: bool = False,
|
|
391
361
|
use_unique_items_as_set: bool = False,
|
|
392
|
-
http_headers:
|
|
362
|
+
http_headers: Sequence[tuple[str, str]] | None = None,
|
|
393
363
|
http_ignore_tls: bool = False,
|
|
394
364
|
use_annotated: bool = False,
|
|
395
365
|
use_non_positive_negative_number_constrained_types: bool = False,
|
|
396
|
-
original_field_name_delimiter:
|
|
366
|
+
original_field_name_delimiter: str | None = None,
|
|
397
367
|
use_double_quotes: bool = False,
|
|
398
368
|
use_union_operator: bool = False,
|
|
399
369
|
allow_responses_without_content: bool = False,
|
|
400
370
|
collapse_root_models: bool = False,
|
|
401
|
-
special_field_name_prefix:
|
|
371
|
+
special_field_name_prefix: str | None = None,
|
|
402
372
|
remove_special_field_name_prefix: bool = False,
|
|
403
373
|
capitalise_enum_members: bool = False,
|
|
404
374
|
keep_model_order: bool = False,
|
|
405
375
|
use_one_literal_as_default: bool = False,
|
|
406
|
-
known_third_party:
|
|
407
|
-
custom_formatters:
|
|
408
|
-
custom_formatters_kwargs:
|
|
376
|
+
known_third_party: list[str] | None = None,
|
|
377
|
+
custom_formatters: list[str] | None = None,
|
|
378
|
+
custom_formatters_kwargs: dict[str, Any] | None = None,
|
|
409
379
|
use_pendulum: bool = False,
|
|
410
|
-
http_query_parameters:
|
|
380
|
+
http_query_parameters: Sequence[tuple[str, str]] | None = None,
|
|
411
381
|
treat_dots_as_module: bool = False,
|
|
412
382
|
use_exact_imports: bool = False,
|
|
413
|
-
default_field_extras:
|
|
414
|
-
target_datetime_class:
|
|
383
|
+
default_field_extras: dict[str, Any] | None = None,
|
|
384
|
+
target_datetime_class: DatetimeClassType | None = DatetimeClassType.Datetime,
|
|
415
385
|
keyword_only: bool = False,
|
|
416
386
|
no_alias: bool = False,
|
|
417
387
|
) -> None:
|
|
@@ -425,55 +395,43 @@ class Parser(ABC):
|
|
|
425
395
|
use_pendulum=use_pendulum,
|
|
426
396
|
target_datetime_class=target_datetime_class,
|
|
427
397
|
)
|
|
428
|
-
self.data_model_type:
|
|
429
|
-
self.data_model_root_type:
|
|
430
|
-
self.data_model_field_type:
|
|
398
|
+
self.data_model_type: type[DataModel] = data_model_type
|
|
399
|
+
self.data_model_root_type: type[DataModel] = data_model_root_type
|
|
400
|
+
self.data_model_field_type: type[DataModelFieldBase] = data_model_field_type
|
|
431
401
|
|
|
432
402
|
self.imports: Imports = Imports(use_exact_imports)
|
|
433
403
|
self.use_exact_imports: bool = use_exact_imports
|
|
434
404
|
self._append_additional_imports(additional_imports=additional_imports)
|
|
435
405
|
|
|
436
|
-
self.base_class:
|
|
406
|
+
self.base_class: str | None = base_class
|
|
437
407
|
self.target_python_version: PythonVersion = target_python_version
|
|
438
|
-
self.results:
|
|
439
|
-
self.dump_resolve_reference_action:
|
|
440
|
-
dump_resolve_reference_action
|
|
441
|
-
)
|
|
408
|
+
self.results: list[DataModel] = []
|
|
409
|
+
self.dump_resolve_reference_action: Callable[[Iterable[str]], str] | None = dump_resolve_reference_action
|
|
442
410
|
self.validation: bool = validation
|
|
443
411
|
self.field_constraints: bool = field_constraints
|
|
444
412
|
self.snake_case_field: bool = snake_case_field
|
|
445
413
|
self.strip_default_none: bool = strip_default_none
|
|
446
|
-
self.apply_default_values_for_required_fields: bool =
|
|
447
|
-
|
|
448
|
-
)
|
|
449
|
-
self.force_optional_for_required_fields: bool = (
|
|
450
|
-
force_optional_for_required_fields
|
|
451
|
-
)
|
|
414
|
+
self.apply_default_values_for_required_fields: bool = apply_default_values_for_required_fields
|
|
415
|
+
self.force_optional_for_required_fields: bool = force_optional_for_required_fields
|
|
452
416
|
self.use_schema_description: bool = use_schema_description
|
|
453
417
|
self.use_field_description: bool = use_field_description
|
|
454
418
|
self.use_default_kwarg: bool = use_default_kwarg
|
|
455
419
|
self.reuse_model: bool = reuse_model
|
|
456
420
|
self.encoding: str = encoding
|
|
457
|
-
self.enum_field_as_literal:
|
|
421
|
+
self.enum_field_as_literal: LiteralType | None = enum_field_as_literal
|
|
458
422
|
self.set_default_enum_member: bool = set_default_enum_member
|
|
459
423
|
self.use_subclass_enum: bool = use_subclass_enum
|
|
460
424
|
self.strict_nullable: bool = strict_nullable
|
|
461
425
|
self.use_generic_container_types: bool = use_generic_container_types
|
|
462
426
|
self.use_union_operator: bool = use_union_operator
|
|
463
427
|
self.enable_faux_immutability: bool = enable_faux_immutability
|
|
464
|
-
self.custom_class_name_generator:
|
|
465
|
-
|
|
466
|
-
)
|
|
467
|
-
self.field_extra_keys: Set[str] = field_extra_keys or set()
|
|
468
|
-
self.field_extra_keys_without_x_prefix: Set[str] = (
|
|
469
|
-
field_extra_keys_without_x_prefix or set()
|
|
470
|
-
)
|
|
428
|
+
self.custom_class_name_generator: Callable[[str], str] | None = custom_class_name_generator
|
|
429
|
+
self.field_extra_keys: set[str] = field_extra_keys or set()
|
|
430
|
+
self.field_extra_keys_without_x_prefix: set[str] = field_extra_keys_without_x_prefix or set()
|
|
471
431
|
self.field_include_all_keys: bool = field_include_all_keys
|
|
472
432
|
|
|
473
|
-
self.remote_text_cache: DefaultPutDict[str, str] = (
|
|
474
|
-
|
|
475
|
-
)
|
|
476
|
-
self.current_source_path: Optional[Path] = None
|
|
433
|
+
self.remote_text_cache: DefaultPutDict[str, str] = remote_text_cache or DefaultPutDict()
|
|
434
|
+
self.current_source_path: Path | None = None
|
|
477
435
|
self.use_title_as_name: bool = use_title_as_name
|
|
478
436
|
self.use_operation_id_as_name: bool = use_operation_id_as_name
|
|
479
437
|
self.use_unique_items_as_set: bool = use_unique_items_as_set
|
|
@@ -481,30 +439,26 @@ class Parser(ABC):
|
|
|
481
439
|
if base_path:
|
|
482
440
|
self.base_path = base_path
|
|
483
441
|
elif isinstance(source, Path):
|
|
484
|
-
self.base_path = (
|
|
485
|
-
source.absolute() if source.is_dir() else source.absolute().parent
|
|
486
|
-
)
|
|
442
|
+
self.base_path = source.absolute() if source.is_dir() else source.absolute().parent
|
|
487
443
|
else:
|
|
488
444
|
self.base_path = Path.cwd()
|
|
489
445
|
|
|
490
|
-
self.source:
|
|
446
|
+
self.source: str | Path | list[Path] | ParseResult = source
|
|
491
447
|
self.custom_template_dir = custom_template_dir
|
|
492
|
-
self.extra_template_data:
|
|
493
|
-
extra_template_data or defaultdict(dict)
|
|
494
|
-
)
|
|
448
|
+
self.extra_template_data: defaultdict[str, Any] = extra_template_data or defaultdict(dict)
|
|
495
449
|
|
|
496
450
|
if allow_population_by_field_name:
|
|
497
|
-
self.extra_template_data[ALL_MODEL][
|
|
451
|
+
self.extra_template_data[ALL_MODEL]["allow_population_by_field_name"] = True
|
|
498
452
|
|
|
499
453
|
if allow_extra_fields:
|
|
500
|
-
self.extra_template_data[ALL_MODEL][
|
|
454
|
+
self.extra_template_data[ALL_MODEL]["allow_extra_fields"] = True
|
|
501
455
|
|
|
502
456
|
if enable_faux_immutability:
|
|
503
|
-
self.extra_template_data[ALL_MODEL][
|
|
457
|
+
self.extra_template_data[ALL_MODEL]["allow_mutation"] = False
|
|
504
458
|
|
|
505
459
|
self.model_resolver = ModelResolver(
|
|
506
460
|
base_url=source.geturl() if isinstance(source, ParseResult) else None,
|
|
507
|
-
singular_name_suffix=
|
|
461
|
+
singular_name_suffix="" if disable_appending_item_suffix else None,
|
|
508
462
|
aliases=aliases,
|
|
509
463
|
empty_field_name=empty_enum_field_name,
|
|
510
464
|
snake_case_field=snake_case_field,
|
|
@@ -516,21 +470,16 @@ class Parser(ABC):
|
|
|
516
470
|
capitalise_enum_members=capitalise_enum_members,
|
|
517
471
|
no_alias=no_alias,
|
|
518
472
|
)
|
|
519
|
-
self.class_name:
|
|
520
|
-
self.wrap_string_literal:
|
|
521
|
-
self.http_headers:
|
|
522
|
-
self.http_query_parameters:
|
|
523
|
-
http_query_parameters
|
|
524
|
-
)
|
|
473
|
+
self.class_name: str | None = class_name
|
|
474
|
+
self.wrap_string_literal: bool | None = wrap_string_literal
|
|
475
|
+
self.http_headers: Sequence[tuple[str, str]] | None = http_headers
|
|
476
|
+
self.http_query_parameters: Sequence[tuple[str, str]] | None = http_query_parameters
|
|
525
477
|
self.http_ignore_tls: bool = http_ignore_tls
|
|
526
478
|
self.use_annotated: bool = use_annotated
|
|
527
479
|
if self.use_annotated and not self.field_constraints: # pragma: no cover
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
self.use_non_positive_negative_number_constrained_types = (
|
|
532
|
-
use_non_positive_negative_number_constrained_types
|
|
533
|
-
)
|
|
480
|
+
msg = "`use_annotated=True` has to be used with `field_constraints=True`"
|
|
481
|
+
raise Exception(msg) # noqa: TRY002
|
|
482
|
+
self.use_non_positive_negative_number_constrained_types = use_non_positive_negative_number_constrained_types
|
|
534
483
|
self.use_double_quotes = use_double_quotes
|
|
535
484
|
self.allow_responses_without_content = allow_responses_without_content
|
|
536
485
|
self.collapse_root_models = collapse_root_models
|
|
@@ -541,7 +490,7 @@ class Parser(ABC):
|
|
|
541
490
|
self.custom_formatter = custom_formatters
|
|
542
491
|
self.custom_formatters_kwargs = custom_formatters_kwargs
|
|
543
492
|
self.treat_dots_as_module = treat_dots_as_module
|
|
544
|
-
self.default_field_extras:
|
|
493
|
+
self.default_field_extras: dict[str, Any] | None = default_field_extras
|
|
545
494
|
|
|
546
495
|
@property
|
|
547
496
|
def iter_source(self) -> Iterator[Source]:
|
|
@@ -549,7 +498,7 @@ class Parser(ABC):
|
|
|
549
498
|
yield Source(path=Path(), text=self.source)
|
|
550
499
|
elif isinstance(self.source, Path): # pragma: no cover
|
|
551
500
|
if self.source.is_dir():
|
|
552
|
-
for path in sorted(self.source.rglob(
|
|
501
|
+
for path in sorted(self.source.rglob("*"), key=lambda p: p.name):
|
|
553
502
|
if path.is_file():
|
|
554
503
|
yield Source.from_path(path, self.base_path, self.encoding)
|
|
555
504
|
else:
|
|
@@ -560,14 +509,10 @@ class Parser(ABC):
|
|
|
560
509
|
else:
|
|
561
510
|
yield Source(
|
|
562
511
|
path=Path(self.source.path),
|
|
563
|
-
text=self.remote_text_cache.get_or_put(
|
|
564
|
-
self.source.geturl(), default_factory=self._get_text_from_url
|
|
565
|
-
),
|
|
512
|
+
text=self.remote_text_cache.get_or_put(self.source.geturl(), default_factory=self._get_text_from_url),
|
|
566
513
|
)
|
|
567
514
|
|
|
568
|
-
def _append_additional_imports(
|
|
569
|
-
self, additional_imports: Optional[List[str]]
|
|
570
|
-
) -> None:
|
|
515
|
+
def _append_additional_imports(self, additional_imports: list[str] | None) -> None:
|
|
571
516
|
if additional_imports is None:
|
|
572
517
|
additional_imports = []
|
|
573
518
|
|
|
@@ -578,36 +523,34 @@ class Parser(ABC):
|
|
|
578
523
|
self.imports.append(new_import)
|
|
579
524
|
|
|
580
525
|
def _get_text_from_url(self, url: str) -> str:
|
|
581
|
-
from datamodel_code_generator.http import get_body
|
|
526
|
+
from datamodel_code_generator.http import get_body # noqa: PLC0415
|
|
582
527
|
|
|
583
528
|
return self.remote_text_cache.get_or_put(
|
|
584
529
|
url,
|
|
585
|
-
default_factory=lambda url_: get_body(
|
|
530
|
+
default_factory=lambda url_: get_body( # noqa: ARG005
|
|
586
531
|
url, self.http_headers, self.http_ignore_tls, self.http_query_parameters
|
|
587
532
|
),
|
|
588
533
|
)
|
|
589
534
|
|
|
590
535
|
@classmethod
|
|
591
|
-
def get_url_path_parts(cls, url: ParseResult) ->
|
|
536
|
+
def get_url_path_parts(cls, url: ParseResult) -> list[str]:
|
|
592
537
|
return [
|
|
593
|
-
f
|
|
594
|
-
*url.path.split(
|
|
538
|
+
f"{url.scheme}://{url.hostname}",
|
|
539
|
+
*url.path.split("/")[1:],
|
|
595
540
|
]
|
|
596
541
|
|
|
597
542
|
@property
|
|
598
|
-
def data_type(self) ->
|
|
543
|
+
def data_type(self) -> type[DataType]:
|
|
599
544
|
return self.data_type_manager.data_type
|
|
600
545
|
|
|
601
546
|
@abstractmethod
|
|
602
547
|
def parse_raw(self) -> None:
|
|
603
548
|
raise NotImplementedError
|
|
604
549
|
|
|
605
|
-
def __delete_duplicate_models(self, models:
|
|
606
|
-
model_class_names:
|
|
607
|
-
model_to_duplicate_models:
|
|
608
|
-
|
|
609
|
-
)
|
|
610
|
-
for model in models[:]:
|
|
550
|
+
def __delete_duplicate_models(self, models: list[DataModel]) -> None: # noqa: PLR0912
|
|
551
|
+
model_class_names: dict[str, DataModel] = {}
|
|
552
|
+
model_to_duplicate_models: defaultdict[DataModel, list[DataModel]] = defaultdict(list)
|
|
553
|
+
for model in models.copy(): # noqa: PLR1702
|
|
611
554
|
if isinstance(model, self.data_model_root_type):
|
|
612
555
|
root_data_type = model.fields[0].data_type
|
|
613
556
|
|
|
@@ -619,9 +562,7 @@ class Parser(ABC):
|
|
|
619
562
|
and not root_data_type.is_list
|
|
620
563
|
and root_data_type.reference.source in models
|
|
621
564
|
and root_data_type.reference.name
|
|
622
|
-
== self.model_resolver.get_class_name(
|
|
623
|
-
model.reference.original_name, unique=False
|
|
624
|
-
).name
|
|
565
|
+
== self.model_resolver.get_class_name(model.reference.original_name, unique=False).name
|
|
625
566
|
):
|
|
626
567
|
# Replace referenced duplicate model to original model
|
|
627
568
|
for child in model.reference.children[:]:
|
|
@@ -655,9 +596,7 @@ class Parser(ABC):
|
|
|
655
596
|
original_model_key = tuple(
|
|
656
597
|
to_hashable(v)
|
|
657
598
|
for v in (
|
|
658
|
-
original_model.render(
|
|
659
|
-
class_name=original_model.duplicate_class_name
|
|
660
|
-
),
|
|
599
|
+
original_model.render(class_name=original_model.duplicate_class_name),
|
|
661
600
|
original_model.imports,
|
|
662
601
|
)
|
|
663
602
|
)
|
|
@@ -673,26 +612,21 @@ class Parser(ABC):
|
|
|
673
612
|
# simplify if introduce duplicate base classes
|
|
674
613
|
if isinstance(child, DataModel):
|
|
675
614
|
child.base_classes = list(
|
|
676
|
-
{
|
|
677
|
-
f'{c.module_name}.{c.type_hint}': c
|
|
678
|
-
for c in child.base_classes
|
|
679
|
-
}.values()
|
|
615
|
+
{f"{c.module_name}.{c.type_hint}": c for c in child.base_classes}.values()
|
|
680
616
|
)
|
|
681
617
|
models.remove(duplicate_model)
|
|
682
618
|
|
|
683
619
|
@classmethod
|
|
684
|
-
def __replace_duplicate_name_in_module(cls, models:
|
|
620
|
+
def __replace_duplicate_name_in_module(cls, models: list[DataModel]) -> None:
|
|
685
621
|
scoped_model_resolver = ModelResolver(
|
|
686
622
|
exclude_names={i.alias or i.import_ for m in models for i in m.imports},
|
|
687
|
-
duplicate_name_suffix=
|
|
623
|
+
duplicate_name_suffix="Model",
|
|
688
624
|
)
|
|
689
625
|
|
|
690
|
-
model_names:
|
|
626
|
+
model_names: dict[str, DataModel] = {}
|
|
691
627
|
for model in models:
|
|
692
628
|
class_name: str = model.class_name
|
|
693
|
-
generated_name: str = scoped_model_resolver.add(
|
|
694
|
-
[model.path], class_name, unique=True, class_name=True
|
|
695
|
-
).name
|
|
629
|
+
generated_name: str = scoped_model_resolver.add([model.path], class_name, unique=True, class_name=True).name
|
|
696
630
|
if class_name != generated_name:
|
|
697
631
|
model.class_name = generated_name
|
|
698
632
|
model_names[model.class_name] = model
|
|
@@ -707,10 +641,10 @@ class Parser(ABC):
|
|
|
707
641
|
|
|
708
642
|
def __change_from_import(
|
|
709
643
|
self,
|
|
710
|
-
models:
|
|
644
|
+
models: list[DataModel],
|
|
711
645
|
imports: Imports,
|
|
712
646
|
scoped_model_resolver: ModelResolver,
|
|
713
|
-
init: bool,
|
|
647
|
+
init: bool, # noqa: FBT001
|
|
714
648
|
) -> None:
|
|
715
649
|
for model in models:
|
|
716
650
|
scoped_model_resolver.add([model.path], model.class_name)
|
|
@@ -727,42 +661,30 @@ class Parser(ABC):
|
|
|
727
661
|
|
|
728
662
|
if isinstance(data_type, BaseClassDataType):
|
|
729
663
|
left, right = relative(model.module_name, data_type.full_name)
|
|
730
|
-
from_ = (
|
|
731
|
-
''.join([left, right])
|
|
732
|
-
if left.endswith('.')
|
|
733
|
-
else '.'.join([left, right])
|
|
734
|
-
)
|
|
664
|
+
from_ = f"{left}{right}" if left.endswith(".") else f"{left}.{right}"
|
|
735
665
|
import_ = data_type.reference.short_name
|
|
736
666
|
full_path = from_, import_
|
|
737
667
|
else:
|
|
738
|
-
from_, import_ = full_path = relative(
|
|
739
|
-
model.module_name, data_type.full_name
|
|
740
|
-
)
|
|
668
|
+
from_, import_ = full_path = relative(model.module_name, data_type.full_name)
|
|
741
669
|
if imports.use_exact: # pragma: no cover
|
|
742
|
-
from_, import_ = exact_import(
|
|
743
|
-
|
|
744
|
-
)
|
|
745
|
-
import_ = import_.replace('-', '_')
|
|
670
|
+
from_, import_ = exact_import(from_, import_, data_type.reference.short_name)
|
|
671
|
+
import_ = import_.replace("-", "_")
|
|
746
672
|
if (
|
|
747
673
|
len(model.module_path) > 1
|
|
748
|
-
and model.module_path[-1].count(
|
|
674
|
+
and model.module_path[-1].count(".") > 0
|
|
749
675
|
and not self.treat_dots_as_module
|
|
750
676
|
):
|
|
751
|
-
rel_path_depth = model.module_path[-1].count(
|
|
677
|
+
rel_path_depth = model.module_path[-1].count(".")
|
|
752
678
|
from_ = from_[rel_path_depth:]
|
|
753
679
|
|
|
754
680
|
alias = scoped_model_resolver.add(full_path, import_).name
|
|
755
681
|
|
|
756
682
|
name = data_type.reference.short_name
|
|
757
683
|
if from_ and import_ and alias != name:
|
|
758
|
-
data_type.alias =
|
|
759
|
-
alias
|
|
760
|
-
if data_type.reference.short_name == import_
|
|
761
|
-
else f'{alias}.{name}'
|
|
762
|
-
)
|
|
684
|
+
data_type.alias = alias if data_type.reference.short_name == import_ else f"{alias}.{name}"
|
|
763
685
|
|
|
764
686
|
if init:
|
|
765
|
-
from_ =
|
|
687
|
+
from_ = "." + from_
|
|
766
688
|
imports.append(
|
|
767
689
|
Import(
|
|
768
690
|
from_=from_,
|
|
@@ -776,11 +698,11 @@ class Parser(ABC):
|
|
|
776
698
|
imports.append(after_import)
|
|
777
699
|
|
|
778
700
|
@classmethod
|
|
779
|
-
def __extract_inherited_enum(cls, models:
|
|
780
|
-
for model in models
|
|
701
|
+
def __extract_inherited_enum(cls, models: list[DataModel]) -> None:
|
|
702
|
+
for model in models.copy():
|
|
781
703
|
if model.fields:
|
|
782
704
|
continue
|
|
783
|
-
enums:
|
|
705
|
+
enums: list[Enum] = []
|
|
784
706
|
for base_model in model.base_classes:
|
|
785
707
|
if not base_model.reference:
|
|
786
708
|
continue
|
|
@@ -798,20 +720,20 @@ class Parser(ABC):
|
|
|
798
720
|
)
|
|
799
721
|
models.remove(model)
|
|
800
722
|
|
|
801
|
-
def __apply_discriminator_type(
|
|
723
|
+
def __apply_discriminator_type( # noqa: PLR0912, PLR0915
|
|
802
724
|
self,
|
|
803
|
-
models:
|
|
725
|
+
models: list[DataModel],
|
|
804
726
|
imports: Imports,
|
|
805
727
|
) -> None:
|
|
806
|
-
for model in models:
|
|
728
|
+
for model in models: # noqa: PLR1702
|
|
807
729
|
for field in model.fields:
|
|
808
|
-
discriminator = field.extras.get(
|
|
730
|
+
discriminator = field.extras.get("discriminator")
|
|
809
731
|
if not discriminator or not isinstance(discriminator, dict):
|
|
810
732
|
continue
|
|
811
|
-
property_name = discriminator.get(
|
|
733
|
+
property_name = discriminator.get("propertyName")
|
|
812
734
|
if not property_name: # pragma: no cover
|
|
813
735
|
continue
|
|
814
|
-
mapping = discriminator.get(
|
|
736
|
+
mapping = discriminator.get("mapping", {})
|
|
815
737
|
for data_type in field.data_type.data_types:
|
|
816
738
|
if not data_type.reference: # pragma: no cover
|
|
817
739
|
continue
|
|
@@ -828,80 +750,55 @@ class Parser(ABC):
|
|
|
828
750
|
):
|
|
829
751
|
continue # pragma: no cover
|
|
830
752
|
|
|
831
|
-
type_names:
|
|
753
|
+
type_names: list[str] = []
|
|
832
754
|
|
|
833
755
|
def check_paths(
|
|
834
|
-
model:
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
Reference,
|
|
838
|
-
],
|
|
839
|
-
mapping: Dict[str, str],
|
|
840
|
-
type_names: List[str] = type_names,
|
|
756
|
+
model: pydantic_model.BaseModel | pydantic_model_v2.BaseModel | Reference,
|
|
757
|
+
mapping: dict[str, str],
|
|
758
|
+
type_names: list[str] = type_names,
|
|
841
759
|
) -> None:
|
|
842
760
|
"""Helper function to validate paths for a given model."""
|
|
843
761
|
for name, path in mapping.items():
|
|
844
|
-
if (
|
|
845
|
-
|
|
846
|
-
) and (
|
|
847
|
-
path.startswith('#/')
|
|
848
|
-
or model.path[:-1] != path.split('/')[-1]
|
|
762
|
+
if (model.path.split("#/")[-1] != path.split("#/")[-1]) and (
|
|
763
|
+
path.startswith("#/") or model.path[:-1] != path.split("/")[-1]
|
|
849
764
|
):
|
|
850
|
-
t_path = path[str(path).find(
|
|
851
|
-
t_disc = model.path[: str(model.path).find(
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
t_disc_2 = '/'.join(t_disc.split('/')[1:])
|
|
855
|
-
if t_path != t_disc and t_path != t_disc_2:
|
|
765
|
+
t_path = path[str(path).find("/") + 1 :]
|
|
766
|
+
t_disc = model.path[: str(model.path).find("#")].lstrip("../") # noqa: B005
|
|
767
|
+
t_disc_2 = "/".join(t_disc.split("/")[1:])
|
|
768
|
+
if t_path not in {t_disc, t_disc_2}:
|
|
856
769
|
continue
|
|
857
770
|
type_names.append(name)
|
|
858
771
|
|
|
859
772
|
# Check the main discriminator model path
|
|
860
773
|
if mapping:
|
|
861
|
-
check_paths(discriminator_model, mapping) # pyright: ignore
|
|
774
|
+
check_paths(discriminator_model, mapping) # pyright: ignore[reportArgumentType]
|
|
862
775
|
|
|
863
776
|
# Check the base_classes if they exist
|
|
864
777
|
if len(type_names) == 0:
|
|
865
778
|
for base_class in discriminator_model.base_classes:
|
|
866
|
-
check_paths(base_class.reference, mapping) # pyright: ignore
|
|
779
|
+
check_paths(base_class.reference, mapping) # pyright: ignore[reportArgumentType]
|
|
867
780
|
else:
|
|
868
|
-
type_names = [discriminator_model.path.split(
|
|
781
|
+
type_names = [discriminator_model.path.split("/")[-1]]
|
|
869
782
|
if not type_names: # pragma: no cover
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
)
|
|
783
|
+
msg = f"Discriminator type is not found. {data_type.reference.path}"
|
|
784
|
+
raise RuntimeError(msg)
|
|
873
785
|
has_one_literal = False
|
|
874
786
|
for discriminator_field in discriminator_model.fields:
|
|
875
|
-
if (
|
|
876
|
-
discriminator_field.original_name
|
|
877
|
-
or discriminator_field.name
|
|
878
|
-
) != property_name:
|
|
787
|
+
if (discriminator_field.original_name or discriminator_field.name) != property_name:
|
|
879
788
|
continue
|
|
880
789
|
literals = discriminator_field.data_type.literals
|
|
881
|
-
if len(literals) == 1 and literals[0] == (
|
|
882
|
-
type_names[0] if type_names else None
|
|
883
|
-
):
|
|
790
|
+
if len(literals) == 1 and literals[0] == (type_names[0] if type_names else None):
|
|
884
791
|
has_one_literal = True
|
|
885
|
-
if isinstance(
|
|
886
|
-
discriminator_model,
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
'tag_field', f"'{property_name}'"
|
|
890
|
-
)
|
|
891
|
-
discriminator_model.add_base_class_kwarg(
|
|
892
|
-
'tag', discriminator_field.represented_default
|
|
893
|
-
)
|
|
894
|
-
discriminator_field.extras['is_classvar'] = True
|
|
792
|
+
if isinstance(discriminator_model, msgspec_model.Struct): # pragma: no cover
|
|
793
|
+
discriminator_model.add_base_class_kwarg("tag_field", f"'{property_name}'")
|
|
794
|
+
discriminator_model.add_base_class_kwarg("tag", discriminator_field.represented_default)
|
|
795
|
+
discriminator_field.extras["is_classvar"] = True
|
|
895
796
|
# Found the discriminator field, no need to keep looking
|
|
896
797
|
break
|
|
897
|
-
for
|
|
898
|
-
field_data_type
|
|
899
|
-
) in discriminator_field.data_type.all_data_types:
|
|
798
|
+
for field_data_type in discriminator_field.data_type.all_data_types:
|
|
900
799
|
if field_data_type.reference: # pragma: no cover
|
|
901
800
|
field_data_type.remove_reference()
|
|
902
|
-
discriminator_field.data_type = self.data_type(
|
|
903
|
-
literals=type_names
|
|
904
|
-
)
|
|
801
|
+
discriminator_field.data_type = self.data_type(literals=type_names)
|
|
905
802
|
discriminator_field.data_type.parent = discriminator_field
|
|
906
803
|
discriminator_field.required = True
|
|
907
804
|
imports.append(discriminator_field.imports)
|
|
@@ -914,20 +811,13 @@ class Parser(ABC):
|
|
|
914
811
|
required=True,
|
|
915
812
|
)
|
|
916
813
|
)
|
|
917
|
-
literal =
|
|
918
|
-
|
|
919
|
-
if self.target_python_version.has_literal_type
|
|
920
|
-
else IMPORT_LITERAL_BACKPORT
|
|
921
|
-
)
|
|
922
|
-
has_imported_literal = any(
|
|
923
|
-
literal == import_ # type: ignore [comparison-overlap]
|
|
924
|
-
for import_ in imports
|
|
925
|
-
)
|
|
814
|
+
literal = IMPORT_LITERAL if self.target_python_version.has_literal_type else IMPORT_LITERAL_BACKPORT
|
|
815
|
+
has_imported_literal = any(literal == import_ for import_ in imports)
|
|
926
816
|
if has_imported_literal: # pragma: no cover
|
|
927
817
|
imports.append(literal)
|
|
928
818
|
|
|
929
819
|
@classmethod
|
|
930
|
-
def _create_set_from_list(cls, data_type: DataType) ->
|
|
820
|
+
def _create_set_from_list(cls, data_type: DataType) -> DataType | None:
|
|
931
821
|
if data_type.is_list:
|
|
932
822
|
new_data_type = data_type.copy()
|
|
933
823
|
new_data_type.is_list = False
|
|
@@ -935,7 +825,7 @@ class Parser(ABC):
|
|
|
935
825
|
for data_type_ in new_data_type.data_types:
|
|
936
826
|
data_type_.parent = new_data_type
|
|
937
827
|
return new_data_type
|
|
938
|
-
|
|
828
|
+
if data_type.data_types: # pragma: no cover
|
|
939
829
|
for index, nested_data_type in enumerate(data_type.data_types[:]):
|
|
940
830
|
set_data_type = cls._create_set_from_list(nested_data_type)
|
|
941
831
|
if set_data_type: # pragma: no cover
|
|
@@ -943,15 +833,13 @@ class Parser(ABC):
|
|
|
943
833
|
return data_type
|
|
944
834
|
return None # pragma: no cover
|
|
945
835
|
|
|
946
|
-
def __replace_unique_list_to_set(self, models:
|
|
836
|
+
def __replace_unique_list_to_set(self, models: list[DataModel]) -> None:
|
|
947
837
|
for model in models:
|
|
948
838
|
for model_field in model.fields:
|
|
949
839
|
if not self.use_unique_items_as_set:
|
|
950
840
|
continue
|
|
951
841
|
|
|
952
|
-
if not (
|
|
953
|
-
model_field.constraints and model_field.constraints.unique_items
|
|
954
|
-
):
|
|
842
|
+
if not (model_field.constraints and model_field.constraints.unique_items):
|
|
955
843
|
continue
|
|
956
844
|
set_data_type = self._create_set_from_list(model_field.data_type)
|
|
957
845
|
if set_data_type: # pragma: no cover
|
|
@@ -960,30 +848,25 @@ class Parser(ABC):
|
|
|
960
848
|
set_data_type.parent = model_field
|
|
961
849
|
|
|
962
850
|
@classmethod
|
|
963
|
-
def __set_reference_default_value_to_field(cls, models:
|
|
851
|
+
def __set_reference_default_value_to_field(cls, models: list[DataModel]) -> None:
|
|
964
852
|
for model in models:
|
|
965
853
|
for model_field in model.fields:
|
|
966
854
|
if not model_field.data_type.reference or model_field.has_default:
|
|
967
855
|
continue
|
|
968
|
-
if
|
|
969
|
-
model_field.data_type.reference.source, DataModel
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
)
|
|
856
|
+
if (
|
|
857
|
+
isinstance(model_field.data_type.reference.source, DataModel)
|
|
858
|
+
and model_field.data_type.reference.source.default != UNDEFINED
|
|
859
|
+
):
|
|
860
|
+
# pragma: no cover
|
|
861
|
+
model_field.default = model_field.data_type.reference.source.default
|
|
975
862
|
|
|
976
|
-
def __reuse_model(
|
|
977
|
-
self, models: List[DataModel], require_update_action_models: List[str]
|
|
978
|
-
) -> None:
|
|
863
|
+
def __reuse_model(self, models: list[DataModel], require_update_action_models: list[str]) -> None:
|
|
979
864
|
if not self.reuse_model:
|
|
980
|
-
return
|
|
981
|
-
model_cache:
|
|
865
|
+
return
|
|
866
|
+
model_cache: dict[tuple[str, ...], Reference] = {}
|
|
982
867
|
duplicates = []
|
|
983
|
-
for model in models
|
|
984
|
-
model_key = tuple(
|
|
985
|
-
to_hashable(v) for v in (model.render(class_name='M'), model.imports)
|
|
986
|
-
)
|
|
868
|
+
for model in models.copy():
|
|
869
|
+
model_key = tuple(to_hashable(v) for v in (model.render(class_name="M"), model.imports))
|
|
987
870
|
cached_model_reference = model_cache.get(model_key)
|
|
988
871
|
if cached_model_reference:
|
|
989
872
|
if isinstance(model, Enum):
|
|
@@ -1002,9 +885,9 @@ class Parser(ABC):
|
|
|
1002
885
|
description=model.description,
|
|
1003
886
|
reference=Reference(
|
|
1004
887
|
name=model.name,
|
|
1005
|
-
path=model.reference.path +
|
|
888
|
+
path=model.reference.path + "/reuse",
|
|
1006
889
|
),
|
|
1007
|
-
custom_template_dir=model._custom_template_dir,
|
|
890
|
+
custom_template_dir=model._custom_template_dir, # noqa: SLF001
|
|
1008
891
|
)
|
|
1009
892
|
if cached_model_reference.path in require_update_action_models:
|
|
1010
893
|
require_update_action_models.append(inherited_model.path)
|
|
@@ -1017,23 +900,21 @@ class Parser(ABC):
|
|
|
1017
900
|
for duplicate in duplicates:
|
|
1018
901
|
models.remove(duplicate)
|
|
1019
902
|
|
|
1020
|
-
def __collapse_root_models(
|
|
903
|
+
def __collapse_root_models( # noqa: PLR0912
|
|
1021
904
|
self,
|
|
1022
|
-
models:
|
|
1023
|
-
unused_models:
|
|
905
|
+
models: list[DataModel],
|
|
906
|
+
unused_models: list[DataModel],
|
|
1024
907
|
imports: Imports,
|
|
1025
908
|
scoped_model_resolver: ModelResolver,
|
|
1026
909
|
) -> None:
|
|
1027
910
|
if not self.collapse_root_models:
|
|
1028
|
-
return
|
|
911
|
+
return
|
|
1029
912
|
|
|
1030
|
-
for model in models:
|
|
913
|
+
for model in models: # noqa: PLR1702
|
|
1031
914
|
for model_field in model.fields:
|
|
1032
915
|
for data_type in model_field.data_type.all_data_types:
|
|
1033
916
|
reference = data_type.reference
|
|
1034
|
-
if not reference or not isinstance(
|
|
1035
|
-
reference.source, self.data_model_root_type
|
|
1036
|
-
):
|
|
917
|
+
if not reference or not isinstance(reference.source, self.data_model_root_type):
|
|
1037
918
|
continue
|
|
1038
919
|
|
|
1039
920
|
# Use root-type as model_field type
|
|
@@ -1044,11 +925,7 @@ class Parser(ABC):
|
|
|
1044
925
|
self.field_constraints
|
|
1045
926
|
and isinstance(root_type_field.constraints, ConstraintsBase)
|
|
1046
927
|
and root_type_field.constraints.has_constraints
|
|
1047
|
-
and any(
|
|
1048
|
-
d
|
|
1049
|
-
for d in model_field.data_type.all_data_types
|
|
1050
|
-
if d.is_dict or d.is_union or d.is_list
|
|
1051
|
-
)
|
|
928
|
+
and any(d for d in model_field.data_type.all_data_types if d.is_dict or d.is_union or d.is_list)
|
|
1052
929
|
):
|
|
1053
930
|
continue # pragma: no cover
|
|
1054
931
|
|
|
@@ -1078,23 +955,19 @@ class Parser(ABC):
|
|
|
1078
955
|
if isinstance(
|
|
1079
956
|
root_type_field,
|
|
1080
957
|
pydantic_model.DataModelField,
|
|
1081
|
-
) and not model_field.extras.get(
|
|
1082
|
-
discriminator = root_type_field.extras.get(
|
|
958
|
+
) and not model_field.extras.get("discriminator"):
|
|
959
|
+
discriminator = root_type_field.extras.get("discriminator")
|
|
1083
960
|
if discriminator:
|
|
1084
|
-
model_field.extras[
|
|
961
|
+
model_field.extras["discriminator"] = discriminator
|
|
1085
962
|
assert isinstance(data_type.parent, DataType)
|
|
1086
|
-
data_type.parent.data_types.remove(
|
|
1087
|
-
data_type
|
|
1088
|
-
) # pragma: no cover
|
|
963
|
+
data_type.parent.data_types.remove(data_type) # pragma: no cover
|
|
1089
964
|
data_type.parent.data_types.append(copied_data_type)
|
|
1090
965
|
|
|
1091
966
|
elif isinstance(data_type.parent, DataType):
|
|
1092
967
|
# for data_type
|
|
1093
968
|
data_type_id = id(data_type)
|
|
1094
969
|
data_type.parent.data_types = [
|
|
1095
|
-
d
|
|
1096
|
-
for d in (*data_type.parent.data_types, copied_data_type)
|
|
1097
|
-
if id(d) != data_type_id
|
|
970
|
+
d for d in (*data_type.parent.data_types, copied_data_type) if id(d) != data_type_id
|
|
1098
971
|
]
|
|
1099
972
|
else: # pragma: no cover
|
|
1100
973
|
continue
|
|
@@ -1102,26 +975,22 @@ class Parser(ABC):
|
|
|
1102
975
|
for d in root_type_field.data_type.data_types:
|
|
1103
976
|
if d.reference is None:
|
|
1104
977
|
continue
|
|
1105
|
-
from_, import_ = full_path = relative(
|
|
1106
|
-
model.module_name, d.full_name
|
|
1107
|
-
)
|
|
978
|
+
from_, import_ = full_path = relative(model.module_name, d.full_name)
|
|
1108
979
|
if from_ and import_:
|
|
1109
980
|
alias = scoped_model_resolver.add(full_path, import_)
|
|
1110
981
|
d.alias = (
|
|
1111
982
|
alias.name
|
|
1112
983
|
if d.reference.short_name == import_
|
|
1113
|
-
else f
|
|
1114
|
-
)
|
|
1115
|
-
imports.append(
|
|
1116
|
-
[
|
|
1117
|
-
Import(
|
|
1118
|
-
from_=from_,
|
|
1119
|
-
import_=import_,
|
|
1120
|
-
alias=alias.name,
|
|
1121
|
-
reference_path=d.reference.path,
|
|
1122
|
-
)
|
|
1123
|
-
]
|
|
984
|
+
else f"{alias.name}.{d.reference.short_name}"
|
|
1124
985
|
)
|
|
986
|
+
imports.append([
|
|
987
|
+
Import(
|
|
988
|
+
from_=from_,
|
|
989
|
+
import_=import_,
|
|
990
|
+
alias=alias.name,
|
|
991
|
+
reference_path=d.reference.path,
|
|
992
|
+
)
|
|
993
|
+
])
|
|
1125
994
|
|
|
1126
995
|
original_field = get_most_of_parent(data_type, DataModelFieldBase)
|
|
1127
996
|
if original_field: # pragma: no cover
|
|
@@ -1131,42 +1000,31 @@ class Parser(ABC):
|
|
|
1131
1000
|
data_type.remove_reference()
|
|
1132
1001
|
|
|
1133
1002
|
root_type_model.reference.children = [
|
|
1134
|
-
c
|
|
1135
|
-
for c in root_type_model.reference.children
|
|
1136
|
-
if getattr(c, 'parent', None)
|
|
1003
|
+
c for c in root_type_model.reference.children if getattr(c, "parent", None)
|
|
1137
1004
|
]
|
|
1138
1005
|
|
|
1139
1006
|
imports.remove_referenced_imports(root_type_model.path)
|
|
1140
1007
|
if not root_type_model.reference.children:
|
|
1141
1008
|
unused_models.append(root_type_model)
|
|
1142
1009
|
|
|
1143
|
-
def __set_default_enum_member(
|
|
1010
|
+
def __set_default_enum_member( # noqa: PLR0912
|
|
1144
1011
|
self,
|
|
1145
|
-
models:
|
|
1012
|
+
models: list[DataModel],
|
|
1146
1013
|
) -> None:
|
|
1147
1014
|
if not self.set_default_enum_member:
|
|
1148
|
-
return
|
|
1149
|
-
for model in models:
|
|
1015
|
+
return
|
|
1016
|
+
for model in models: # noqa: PLR1702
|
|
1150
1017
|
for model_field in model.fields:
|
|
1151
1018
|
if not model_field.default:
|
|
1152
1019
|
continue
|
|
1153
1020
|
for data_type in model_field.data_type.all_data_types:
|
|
1154
|
-
if data_type.reference and isinstance(
|
|
1155
|
-
data_type.reference.source, Enum
|
|
1156
|
-
): # pragma: no cover
|
|
1021
|
+
if data_type.reference and isinstance(data_type.reference.source, Enum): # pragma: no cover
|
|
1157
1022
|
if isinstance(model_field.default, list):
|
|
1158
|
-
enum_member:
|
|
1159
|
-
e
|
|
1160
|
-
for e in (
|
|
1161
|
-
data_type.reference.source.find_member(d)
|
|
1162
|
-
for d in model_field.default
|
|
1163
|
-
)
|
|
1164
|
-
if e
|
|
1023
|
+
enum_member: list[Member] | (Member | None) = [
|
|
1024
|
+
e for e in (data_type.reference.source.find_member(d) for d in model_field.default) if e
|
|
1165
1025
|
]
|
|
1166
1026
|
else:
|
|
1167
|
-
enum_member = data_type.reference.source.find_member(
|
|
1168
|
-
model_field.default
|
|
1169
|
-
)
|
|
1027
|
+
enum_member = data_type.reference.source.find_member(model_field.default)
|
|
1170
1028
|
if not enum_member:
|
|
1171
1029
|
continue
|
|
1172
1030
|
model_field.default = enum_member
|
|
@@ -1179,7 +1037,7 @@ class Parser(ABC):
|
|
|
1179
1037
|
|
|
1180
1038
|
def __override_required_field(
|
|
1181
1039
|
self,
|
|
1182
|
-
models:
|
|
1040
|
+
models: list[DataModel],
|
|
1183
1041
|
) -> None:
|
|
1184
1042
|
for model in models:
|
|
1185
1043
|
if isinstance(model, (Enum, self.data_model_root_type)):
|
|
@@ -1187,7 +1045,7 @@ class Parser(ABC):
|
|
|
1187
1045
|
for index, model_field in enumerate(model.fields[:]):
|
|
1188
1046
|
data_type = model_field.data_type
|
|
1189
1047
|
if (
|
|
1190
|
-
not model_field.original_name
|
|
1048
|
+
not model_field.original_name # noqa: PLR0916
|
|
1191
1049
|
or data_type.data_types
|
|
1192
1050
|
or data_type.reference
|
|
1193
1051
|
or data_type.type
|
|
@@ -1196,9 +1054,7 @@ class Parser(ABC):
|
|
|
1196
1054
|
):
|
|
1197
1055
|
continue
|
|
1198
1056
|
|
|
1199
|
-
original_field = _find_field(
|
|
1200
|
-
model_field.original_name, _find_base_classes(model)
|
|
1201
|
-
)
|
|
1057
|
+
original_field = _find_field(model_field.original_name, _find_base_classes(model))
|
|
1202
1058
|
if not original_field: # pragma: no cover
|
|
1203
1059
|
model.fields.remove(model_field)
|
|
1204
1060
|
continue
|
|
@@ -1209,9 +1065,7 @@ class Parser(ABC):
|
|
|
1209
1065
|
)
|
|
1210
1066
|
elif original_field.data_type.data_types:
|
|
1211
1067
|
data_type = original_field.data_type.copy()
|
|
1212
|
-
data_type.data_types = _copy_data_types(
|
|
1213
|
-
original_field.data_type.data_types
|
|
1214
|
-
)
|
|
1068
|
+
data_type.data_types = _copy_data_types(original_field.data_type.data_types)
|
|
1215
1069
|
for data_type_ in data_type.data_types:
|
|
1216
1070
|
data_type_.parent = data_type
|
|
1217
1071
|
else:
|
|
@@ -1225,7 +1079,7 @@ class Parser(ABC):
|
|
|
1225
1079
|
|
|
1226
1080
|
def __sort_models(
|
|
1227
1081
|
self,
|
|
1228
|
-
models:
|
|
1082
|
+
models: list[DataModel],
|
|
1229
1083
|
imports: Imports,
|
|
1230
1084
|
) -> None:
|
|
1231
1085
|
if not self.keep_model_order:
|
|
@@ -1234,7 +1088,7 @@ class Parser(ABC):
|
|
|
1234
1088
|
models.sort(key=lambda x: x.class_name)
|
|
1235
1089
|
|
|
1236
1090
|
imported = {i for v in imports.values() for i in v}
|
|
1237
|
-
model_class_name_baseclasses:
|
|
1091
|
+
model_class_name_baseclasses: dict[DataModel, tuple[str, set[str]]] = {}
|
|
1238
1092
|
for model in models:
|
|
1239
1093
|
class_name = model.class_name
|
|
1240
1094
|
model_class_name_baseclasses[model] = (
|
|
@@ -1255,9 +1109,9 @@ class Parser(ABC):
|
|
|
1255
1109
|
models[i], models[i + 1] = models[i + 1], model
|
|
1256
1110
|
changed = True
|
|
1257
1111
|
|
|
1258
|
-
def __set_one_literal_on_default(self, models:
|
|
1112
|
+
def __set_one_literal_on_default(self, models: list[DataModel]) -> None:
|
|
1259
1113
|
if not self.use_one_literal_as_default:
|
|
1260
|
-
return
|
|
1114
|
+
return
|
|
1261
1115
|
for model in models:
|
|
1262
1116
|
for model_field in model.fields:
|
|
1263
1117
|
if not model_field.required or len(model_field.data_type.literals) != 1:
|
|
@@ -1268,41 +1122,39 @@ class Parser(ABC):
|
|
|
1268
1122
|
model_field.nullable = False
|
|
1269
1123
|
|
|
1270
1124
|
@classmethod
|
|
1271
|
-
def __postprocess_result_modules(cls, results):
|
|
1272
|
-
def process(input_tuple) ->
|
|
1125
|
+
def __postprocess_result_modules(cls, results: dict[tuple[str, ...], Result]) -> dict[tuple[str, ...], Result]:
|
|
1126
|
+
def process(input_tuple: tuple[str, ...]) -> tuple[str, ...]:
|
|
1273
1127
|
r = []
|
|
1274
1128
|
for item in input_tuple:
|
|
1275
|
-
p = item.split(
|
|
1129
|
+
p = item.split(".")
|
|
1276
1130
|
if len(p) > 1:
|
|
1277
1131
|
r.extend(p[:-1])
|
|
1278
1132
|
r.append(p[-1])
|
|
1279
1133
|
else:
|
|
1280
1134
|
r.append(item)
|
|
1281
1135
|
|
|
1282
|
-
r = r[:-2] + [f
|
|
1136
|
+
r = r[:-2] + [f"{r[-2]}.{r[-1]}"]
|
|
1283
1137
|
return tuple(r)
|
|
1284
1138
|
|
|
1285
1139
|
results = {process(k): v for k, v in results.items()}
|
|
1286
1140
|
|
|
1287
|
-
init_result =
|
|
1288
|
-
folders = {t[:-1] if t[-1].endswith(
|
|
1141
|
+
init_result = next(v for k, v in results.items() if k[-1] == "__init__.py")
|
|
1142
|
+
folders = {t[:-1] if t[-1].endswith(".py") else t for t in results}
|
|
1289
1143
|
for folder in folders:
|
|
1290
1144
|
for i in range(len(folder)):
|
|
1291
1145
|
subfolder = folder[: i + 1]
|
|
1292
|
-
init_file = subfolder
|
|
1146
|
+
init_file = (*subfolder, "__init__.py")
|
|
1293
1147
|
results.update({init_file: init_result})
|
|
1294
1148
|
return results
|
|
1295
1149
|
|
|
1296
|
-
def __change_imported_model_name(
|
|
1150
|
+
def __change_imported_model_name( # noqa: PLR6301
|
|
1297
1151
|
self,
|
|
1298
|
-
models:
|
|
1152
|
+
models: list[DataModel],
|
|
1299
1153
|
imports: Imports,
|
|
1300
1154
|
scoped_model_resolver: ModelResolver,
|
|
1301
1155
|
) -> None:
|
|
1302
1156
|
imported_names = {
|
|
1303
|
-
imports.alias[from_][i]
|
|
1304
|
-
if i in imports.alias[from_] and i != imports.alias[from_][i]
|
|
1305
|
-
else i
|
|
1157
|
+
imports.alias[from_][i] if i in imports.alias[from_] and i != imports.alias[from_][i] else i
|
|
1306
1158
|
for from_, import_ in imports.items()
|
|
1307
1159
|
for i in import_
|
|
1308
1160
|
}
|
|
@@ -1311,26 +1163,25 @@ class Parser(ABC):
|
|
|
1311
1163
|
continue
|
|
1312
1164
|
|
|
1313
1165
|
model.reference.name = scoped_model_resolver.add( # pragma: no cover
|
|
1314
|
-
path=get_special_path(
|
|
1166
|
+
path=get_special_path("imported_name", model.path.split("/")),
|
|
1315
1167
|
original_name=model.reference.name,
|
|
1316
1168
|
unique=True,
|
|
1317
1169
|
class_name=True,
|
|
1318
1170
|
).name
|
|
1319
1171
|
|
|
1320
|
-
def parse(
|
|
1172
|
+
def parse( # noqa: PLR0912, PLR0914, PLR0915
|
|
1321
1173
|
self,
|
|
1322
|
-
with_import:
|
|
1323
|
-
format_:
|
|
1324
|
-
settings_path:
|
|
1325
|
-
) ->
|
|
1174
|
+
with_import: bool | None = True, # noqa: FBT001, FBT002
|
|
1175
|
+
format_: bool | None = True, # noqa: FBT001, FBT002
|
|
1176
|
+
settings_path: Path | None = None,
|
|
1177
|
+
) -> str | dict[tuple[str, ...], Result]:
|
|
1326
1178
|
self.parse_raw()
|
|
1327
1179
|
|
|
1328
|
-
if with_import:
|
|
1329
|
-
|
|
1330
|
-
self.imports.append(IMPORT_ANNOTATIONS)
|
|
1180
|
+
if with_import and self.target_python_version != PythonVersion.PY_36:
|
|
1181
|
+
self.imports.append(IMPORT_ANNOTATIONS)
|
|
1331
1182
|
|
|
1332
1183
|
if format_:
|
|
1333
|
-
code_formatter:
|
|
1184
|
+
code_formatter: CodeFormatter | None = CodeFormatter(
|
|
1334
1185
|
self.target_python_version,
|
|
1335
1186
|
settings_path,
|
|
1336
1187
|
self.wrap_string_literal,
|
|
@@ -1342,16 +1193,14 @@ class Parser(ABC):
|
|
|
1342
1193
|
else:
|
|
1343
1194
|
code_formatter = None
|
|
1344
1195
|
|
|
1345
|
-
_, sorted_data_models, require_update_action_models = sort_data_models(
|
|
1346
|
-
self.results
|
|
1347
|
-
)
|
|
1196
|
+
_, sorted_data_models, require_update_action_models = sort_data_models(self.results)
|
|
1348
1197
|
|
|
1349
|
-
results:
|
|
1198
|
+
results: dict[tuple[str, ...], Result] = {}
|
|
1350
1199
|
|
|
1351
|
-
def module_key(data_model: DataModel) ->
|
|
1200
|
+
def module_key(data_model: DataModel) -> tuple[str, ...]:
|
|
1352
1201
|
return tuple(data_model.module_path)
|
|
1353
1202
|
|
|
1354
|
-
def sort_key(data_model: DataModel) ->
|
|
1203
|
+
def sort_key(data_model: DataModel) -> tuple[int, tuple[str, ...]]:
|
|
1355
1204
|
return (len(data_model.module_path), tuple(data_model.module_path))
|
|
1356
1205
|
|
|
1357
1206
|
# process in reverse order to correctly establish module levels
|
|
@@ -1360,59 +1209,54 @@ class Parser(ABC):
|
|
|
1360
1209
|
key=module_key,
|
|
1361
1210
|
)
|
|
1362
1211
|
|
|
1363
|
-
module_models:
|
|
1364
|
-
unused_models:
|
|
1365
|
-
model_to_module_models:
|
|
1366
|
-
|
|
1367
|
-
] = {}
|
|
1368
|
-
module_to_import: Dict[Tuple[str, ...], Imports] = {}
|
|
1212
|
+
module_models: list[tuple[tuple[str, ...], list[DataModel]]] = []
|
|
1213
|
+
unused_models: list[DataModel] = []
|
|
1214
|
+
model_to_module_models: dict[DataModel, tuple[tuple[str, ...], list[DataModel]]] = {}
|
|
1215
|
+
module_to_import: dict[tuple[str, ...], Imports] = {}
|
|
1369
1216
|
|
|
1370
|
-
previous_module
|
|
1217
|
+
previous_module: tuple[str, ...] = ()
|
|
1371
1218
|
for module, models in ((k, [*v]) for k, v in grouped_models):
|
|
1372
1219
|
for model in models:
|
|
1373
1220
|
model_to_module_models[model] = module, models
|
|
1374
1221
|
self.__delete_duplicate_models(models)
|
|
1375
1222
|
self.__replace_duplicate_name_in_module(models)
|
|
1376
1223
|
if len(previous_module) - len(module) > 1:
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
[],
|
|
1382
|
-
)
|
|
1224
|
+
module_models.extend(
|
|
1225
|
+
(
|
|
1226
|
+
previous_module[:parts],
|
|
1227
|
+
[],
|
|
1383
1228
|
)
|
|
1384
|
-
|
|
1385
|
-
(
|
|
1386
|
-
module,
|
|
1387
|
-
models,
|
|
1229
|
+
for parts in range(len(previous_module) - 1, len(module), -1)
|
|
1388
1230
|
)
|
|
1389
|
-
|
|
1231
|
+
module_models.append((
|
|
1232
|
+
module,
|
|
1233
|
+
models,
|
|
1234
|
+
))
|
|
1390
1235
|
previous_module = module
|
|
1391
1236
|
|
|
1392
1237
|
class Processed(NamedTuple):
|
|
1393
|
-
module:
|
|
1394
|
-
models:
|
|
1238
|
+
module: tuple[str, ...]
|
|
1239
|
+
models: list[DataModel]
|
|
1395
1240
|
init: bool
|
|
1396
1241
|
imports: Imports
|
|
1397
1242
|
scoped_model_resolver: ModelResolver
|
|
1398
1243
|
|
|
1399
|
-
processed_models:
|
|
1244
|
+
processed_models: list[Processed] = []
|
|
1400
1245
|
|
|
1401
|
-
for
|
|
1402
|
-
imports = module_to_import[
|
|
1246
|
+
for module_, models in module_models:
|
|
1247
|
+
imports = module_to_import[module_] = Imports(self.use_exact_imports)
|
|
1403
1248
|
init = False
|
|
1404
|
-
if
|
|
1405
|
-
parent = (*
|
|
1249
|
+
if module_:
|
|
1250
|
+
parent = (*module_[:-1], "__init__.py")
|
|
1406
1251
|
if parent not in results:
|
|
1407
|
-
results[parent] = Result(body=
|
|
1408
|
-
if (*
|
|
1409
|
-
module = (*
|
|
1252
|
+
results[parent] = Result(body="")
|
|
1253
|
+
if (*module_, "__init__.py") in results:
|
|
1254
|
+
module = (*module_, "__init__.py")
|
|
1410
1255
|
init = True
|
|
1411
1256
|
else:
|
|
1412
|
-
module = (*
|
|
1413
|
-
module = tuple(part.replace('-', '_') for part in module)
|
|
1257
|
+
module = tuple(part.replace("-", "_") for part in (*module_[:-1], f"{module_[-1]}.py"))
|
|
1414
1258
|
else:
|
|
1415
|
-
module = (
|
|
1259
|
+
module = ("__init__.py",)
|
|
1416
1260
|
|
|
1417
1261
|
scoped_model_resolver = ModelResolver()
|
|
1418
1262
|
|
|
@@ -1422,17 +1266,13 @@ class Parser(ABC):
|
|
|
1422
1266
|
self.__extract_inherited_enum(models)
|
|
1423
1267
|
self.__set_reference_default_value_to_field(models)
|
|
1424
1268
|
self.__reuse_model(models, require_update_action_models)
|
|
1425
|
-
self.__collapse_root_models(
|
|
1426
|
-
models, unused_models, imports, scoped_model_resolver
|
|
1427
|
-
)
|
|
1269
|
+
self.__collapse_root_models(models, unused_models, imports, scoped_model_resolver)
|
|
1428
1270
|
self.__set_default_enum_member(models)
|
|
1429
1271
|
self.__sort_models(models, imports)
|
|
1430
1272
|
self.__apply_discriminator_type(models, imports)
|
|
1431
1273
|
self.__set_one_literal_on_default(models)
|
|
1432
1274
|
|
|
1433
|
-
processed_models.append(
|
|
1434
|
-
Processed(module, models, init, imports, scoped_model_resolver)
|
|
1435
|
-
)
|
|
1275
|
+
processed_models.append(Processed(module, models, init, imports, scoped_model_resolver))
|
|
1436
1276
|
|
|
1437
1277
|
for processed_model in processed_models:
|
|
1438
1278
|
for model in processed_model.models:
|
|
@@ -1447,7 +1287,7 @@ class Parser(ABC):
|
|
|
1447
1287
|
|
|
1448
1288
|
for processed_model in processed_models:
|
|
1449
1289
|
# postprocess imports to remove unused imports.
|
|
1450
|
-
model_code = str(
|
|
1290
|
+
model_code = str("\n".join([str(m) for m in processed_model.models]))
|
|
1451
1291
|
unused_imports = [
|
|
1452
1292
|
(from_, import_)
|
|
1453
1293
|
for from_, imports_ in processed_model.imports.items()
|
|
@@ -1457,56 +1297,44 @@ class Parser(ABC):
|
|
|
1457
1297
|
for from_, import_ in unused_imports:
|
|
1458
1298
|
processed_model.imports.remove(Import(from_=from_, import_=import_))
|
|
1459
1299
|
|
|
1460
|
-
for module, models, init, imports, scoped_model_resolver in processed_models:
|
|
1300
|
+
for module, models, init, imports, scoped_model_resolver in processed_models: # noqa: B007
|
|
1461
1301
|
# process after removing unused models
|
|
1462
1302
|
self.__change_imported_model_name(models, imports, scoped_model_resolver)
|
|
1463
1303
|
|
|
1464
|
-
for module, models, init, imports, scoped_model_resolver in processed_models:
|
|
1465
|
-
result:
|
|
1304
|
+
for module, models, init, imports, scoped_model_resolver in processed_models: # noqa: B007
|
|
1305
|
+
result: list[str] = []
|
|
1466
1306
|
if models:
|
|
1467
1307
|
if with_import:
|
|
1468
|
-
result += [str(self.imports), str(imports),
|
|
1308
|
+
result += [str(self.imports), str(imports), "\n"]
|
|
1469
1309
|
|
|
1470
1310
|
code = dump_templates(models)
|
|
1471
1311
|
result += [code]
|
|
1472
1312
|
|
|
1473
1313
|
if self.dump_resolve_reference_action is not None:
|
|
1474
1314
|
result += [
|
|
1475
|
-
|
|
1315
|
+
"\n",
|
|
1476
1316
|
self.dump_resolve_reference_action(
|
|
1477
|
-
m.reference.short_name
|
|
1478
|
-
for m in models
|
|
1479
|
-
if m.path in require_update_action_models
|
|
1317
|
+
m.reference.short_name for m in models if m.path in require_update_action_models
|
|
1480
1318
|
),
|
|
1481
1319
|
]
|
|
1482
1320
|
if not result and not init:
|
|
1483
1321
|
continue
|
|
1484
|
-
body =
|
|
1322
|
+
body = "\n".join(result)
|
|
1485
1323
|
if code_formatter:
|
|
1486
1324
|
body = code_formatter.format_code(body)
|
|
1487
1325
|
|
|
1488
|
-
results[module] = Result(
|
|
1489
|
-
body=body, source=models[0].file_path if models else None
|
|
1490
|
-
)
|
|
1326
|
+
results[module] = Result(body=body, source=models[0].file_path if models else None)
|
|
1491
1327
|
|
|
1492
1328
|
# retain existing behaviour
|
|
1493
|
-
if [*results] == [(
|
|
1494
|
-
return results[
|
|
1329
|
+
if [*results] == [("__init__.py",)]:
|
|
1330
|
+
return results["__init__.py",].body
|
|
1495
1331
|
|
|
1496
|
-
results = {tuple(i.replace(
|
|
1497
|
-
|
|
1332
|
+
results = {tuple(i.replace("-", "_") for i in k): v for k, v in results.items()}
|
|
1333
|
+
return (
|
|
1498
1334
|
self.__postprocess_result_modules(results)
|
|
1499
1335
|
if self.treat_dots_as_module
|
|
1500
1336
|
else {
|
|
1501
|
-
tuple(
|
|
1502
|
-
(
|
|
1503
|
-
part[: part.rfind('.')].replace('.', '_')
|
|
1504
|
-
+ part[part.rfind('.') :]
|
|
1505
|
-
)
|
|
1506
|
-
for part in k
|
|
1507
|
-
): v
|
|
1337
|
+
tuple((part[: part.rfind(".")].replace(".", "_") + part[part.rfind(".") :]) for part in k): v
|
|
1508
1338
|
for k, v in results.items()
|
|
1509
1339
|
}
|
|
1510
1340
|
)
|
|
1511
|
-
|
|
1512
|
-
return results
|