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