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,38 +1,40 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
|
-
from typing import TYPE_CHECKING, Callable,
|
|
4
|
+
from typing import TYPE_CHECKING, Callable, NamedTuple
|
|
5
|
+
|
|
6
|
+
from datamodel_code_generator import DatetimeClassType, PythonVersion
|
|
5
7
|
|
|
6
|
-
from .. import DatetimeClassType, PythonVersion
|
|
7
|
-
from ..types import DataTypeManager as DataTypeManagerABC
|
|
8
8
|
from .base import ConstraintsBase, DataModel, DataModelFieldBase
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
|
-
from
|
|
11
|
+
from collections.abc import Iterable
|
|
12
|
+
|
|
13
|
+
from datamodel_code_generator import DataModelType
|
|
14
|
+
from datamodel_code_generator.types import DataTypeManager as DataTypeManagerABC
|
|
12
15
|
|
|
13
16
|
DEFAULT_TARGET_DATETIME_CLASS = DatetimeClassType.Datetime
|
|
14
|
-
DEFAULT_TARGET_PYTHON_VERSION = PythonVersion(
|
|
15
|
-
f'{sys.version_info.major}.{sys.version_info.minor}'
|
|
16
|
-
)
|
|
17
|
+
DEFAULT_TARGET_PYTHON_VERSION = PythonVersion(f"{sys.version_info.major}.{sys.version_info.minor}")
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
class DataModelSet(NamedTuple):
|
|
20
|
-
data_model:
|
|
21
|
-
root_model:
|
|
22
|
-
field_model:
|
|
23
|
-
data_type_manager:
|
|
24
|
-
dump_resolve_reference_action:
|
|
25
|
-
known_third_party:
|
|
21
|
+
data_model: type[DataModel]
|
|
22
|
+
root_model: type[DataModel]
|
|
23
|
+
field_model: type[DataModelFieldBase]
|
|
24
|
+
data_type_manager: type[DataTypeManagerABC]
|
|
25
|
+
dump_resolve_reference_action: Callable[[Iterable[str]], str] | None
|
|
26
|
+
known_third_party: list[str] | None = None
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
def get_data_model_types(
|
|
29
30
|
data_model_type: DataModelType,
|
|
30
31
|
target_python_version: PythonVersion = DEFAULT_TARGET_PYTHON_VERSION,
|
|
31
|
-
target_datetime_class:
|
|
32
|
+
target_datetime_class: DatetimeClassType | None = None,
|
|
32
33
|
) -> DataModelSet:
|
|
33
|
-
from
|
|
34
|
-
|
|
35
|
-
from .
|
|
34
|
+
from datamodel_code_generator import DataModelType # noqa: PLC0415
|
|
35
|
+
|
|
36
|
+
from . import dataclass, msgspec, pydantic, pydantic_v2, rootmodel, typed_dict # noqa: PLC0415
|
|
37
|
+
from .types import DataTypeManager # noqa: PLC0415
|
|
36
38
|
|
|
37
39
|
if target_datetime_class is None:
|
|
38
40
|
target_datetime_class = DEFAULT_TARGET_DATETIME_CLASS
|
|
@@ -44,7 +46,7 @@ def get_data_model_types(
|
|
|
44
46
|
data_type_manager=pydantic.DataTypeManager,
|
|
45
47
|
dump_resolve_reference_action=pydantic.dump_resolve_reference_action,
|
|
46
48
|
)
|
|
47
|
-
|
|
49
|
+
if data_model_type == DataModelType.PydanticV2BaseModel:
|
|
48
50
|
return DataModelSet(
|
|
49
51
|
data_model=pydantic_v2.BaseModel,
|
|
50
52
|
root_model=pydantic_v2.RootModel,
|
|
@@ -52,7 +54,7 @@ def get_data_model_types(
|
|
|
52
54
|
data_type_manager=pydantic_v2.DataTypeManager,
|
|
53
55
|
dump_resolve_reference_action=pydantic_v2.dump_resolve_reference_action,
|
|
54
56
|
)
|
|
55
|
-
|
|
57
|
+
if data_model_type == DataModelType.DataclassesDataclass:
|
|
56
58
|
return DataModelSet(
|
|
57
59
|
data_model=dataclass.DataClass,
|
|
58
60
|
root_model=rootmodel.RootModel,
|
|
@@ -60,13 +62,9 @@ def get_data_model_types(
|
|
|
60
62
|
data_type_manager=dataclass.DataTypeManager,
|
|
61
63
|
dump_resolve_reference_action=None,
|
|
62
64
|
)
|
|
63
|
-
|
|
65
|
+
if data_model_type == DataModelType.TypingTypedDict:
|
|
64
66
|
return DataModelSet(
|
|
65
|
-
data_model=
|
|
66
|
-
typed_dict.TypedDict
|
|
67
|
-
if target_python_version.has_typed_dict
|
|
68
|
-
else typed_dict.TypedDictBackport
|
|
69
|
-
),
|
|
67
|
+
data_model=typed_dict.TypedDict,
|
|
70
68
|
root_model=rootmodel.RootModel,
|
|
71
69
|
field_model=(
|
|
72
70
|
typed_dict.DataModelField
|
|
@@ -76,18 +74,17 @@ def get_data_model_types(
|
|
|
76
74
|
data_type_manager=DataTypeManager,
|
|
77
75
|
dump_resolve_reference_action=None,
|
|
78
76
|
)
|
|
79
|
-
|
|
77
|
+
if data_model_type == DataModelType.MsgspecStruct:
|
|
80
78
|
return DataModelSet(
|
|
81
79
|
data_model=msgspec.Struct,
|
|
82
80
|
root_model=msgspec.RootModel,
|
|
83
81
|
field_model=msgspec.DataModelField,
|
|
84
82
|
data_type_manager=msgspec.DataTypeManager,
|
|
85
83
|
dump_resolve_reference_action=None,
|
|
86
|
-
known_third_party=[
|
|
84
|
+
known_third_party=["msgspec"],
|
|
87
85
|
)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
) # pragma: no cover
|
|
86
|
+
msg = f"{data_model_type} is unsupported data model type"
|
|
87
|
+
raise ValueError(msg) # pragma: no cover
|
|
91
88
|
|
|
92
89
|
|
|
93
|
-
__all__ = [
|
|
90
|
+
__all__ = ["ConstraintsBase", "DataModel", "DataModelFieldBase"]
|
|
@@ -1,23 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from abc import ABC, abstractmethod
|
|
2
4
|
from collections import defaultdict
|
|
3
5
|
from copy import deepcopy
|
|
4
|
-
from functools import lru_cache
|
|
6
|
+
from functools import cached_property, lru_cache
|
|
5
7
|
from pathlib import Path
|
|
6
|
-
from typing import
|
|
7
|
-
TYPE_CHECKING,
|
|
8
|
-
Any,
|
|
9
|
-
ClassVar,
|
|
10
|
-
DefaultDict,
|
|
11
|
-
Dict,
|
|
12
|
-
FrozenSet,
|
|
13
|
-
Iterator,
|
|
14
|
-
List,
|
|
15
|
-
Optional,
|
|
16
|
-
Set,
|
|
17
|
-
Tuple,
|
|
18
|
-
TypeVar,
|
|
19
|
-
Union,
|
|
20
|
-
)
|
|
8
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Optional, TypeVar
|
|
21
9
|
from warnings import warn
|
|
22
10
|
|
|
23
11
|
from jinja2 import Environment, FileSystemLoader, Template
|
|
@@ -25,7 +13,6 @@ from pydantic import Field
|
|
|
25
13
|
|
|
26
14
|
from datamodel_code_generator.imports import (
|
|
27
15
|
IMPORT_ANNOTATED,
|
|
28
|
-
IMPORT_ANNOTATED_BACKPORT,
|
|
29
16
|
IMPORT_OPTIONAL,
|
|
30
17
|
IMPORT_UNION,
|
|
31
18
|
Import,
|
|
@@ -40,20 +27,23 @@ from datamodel_code_generator.types import (
|
|
|
40
27
|
chain_as_tuple,
|
|
41
28
|
get_optional_type,
|
|
42
29
|
)
|
|
43
|
-
from datamodel_code_generator.util import PYDANTIC_V2, ConfigDict
|
|
30
|
+
from datamodel_code_generator.util import PYDANTIC_V2, ConfigDict
|
|
44
31
|
|
|
45
|
-
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from collections.abc import Iterator
|
|
46
34
|
|
|
47
|
-
|
|
35
|
+
TEMPLATE_DIR: Path = Path(__file__).parents[0] / "template"
|
|
48
36
|
|
|
49
|
-
|
|
37
|
+
ALL_MODEL: str = "#all#"
|
|
38
|
+
|
|
39
|
+
ConstraintsBaseT = TypeVar("ConstraintsBaseT", bound="ConstraintsBase")
|
|
50
40
|
|
|
51
41
|
|
|
52
42
|
class ConstraintsBase(_BaseModel):
|
|
53
|
-
unique_items: Optional[bool] = Field(None, alias=
|
|
54
|
-
_exclude_fields: ClassVar[
|
|
43
|
+
unique_items: Optional[bool] = Field(None, alias="uniqueItems") # noqa: UP045
|
|
44
|
+
_exclude_fields: ClassVar[set[str]] = {"has_constraints"}
|
|
55
45
|
if PYDANTIC_V2:
|
|
56
|
-
model_config = ConfigDict( # pyright: ignore
|
|
46
|
+
model_config = ConfigDict( # pyright: ignore[reportAssignmentType]
|
|
57
47
|
arbitrary_types_allowed=True, ignored_types=(cached_property,)
|
|
58
48
|
)
|
|
59
49
|
else:
|
|
@@ -67,61 +57,51 @@ class ConstraintsBase(_BaseModel):
|
|
|
67
57
|
return any(v is not None for v in self.dict().values())
|
|
68
58
|
|
|
69
59
|
@staticmethod
|
|
70
|
-
def merge_constraints(
|
|
71
|
-
a: ConstraintsBaseT, b: ConstraintsBaseT
|
|
72
|
-
) -> Optional[ConstraintsBaseT]:
|
|
60
|
+
def merge_constraints(a: ConstraintsBaseT, b: ConstraintsBaseT) -> ConstraintsBaseT | None:
|
|
73
61
|
constraints_class = None
|
|
74
62
|
if isinstance(a, ConstraintsBase): # pragma: no cover
|
|
75
|
-
root_type_field_constraints = {
|
|
76
|
-
k: v for k, v in a.dict(by_alias=True).items() if v is not None
|
|
77
|
-
}
|
|
63
|
+
root_type_field_constraints = {k: v for k, v in a.dict(by_alias=True).items() if v is not None}
|
|
78
64
|
constraints_class = a.__class__
|
|
79
65
|
else:
|
|
80
66
|
root_type_field_constraints = {} # pragma: no cover
|
|
81
67
|
|
|
82
68
|
if isinstance(b, ConstraintsBase): # pragma: no cover
|
|
83
|
-
model_field_constraints = {
|
|
84
|
-
k: v for k, v in b.dict(by_alias=True).items() if v is not None
|
|
85
|
-
}
|
|
69
|
+
model_field_constraints = {k: v for k, v in b.dict(by_alias=True).items() if v is not None}
|
|
86
70
|
constraints_class = constraints_class or b.__class__
|
|
87
71
|
else:
|
|
88
72
|
model_field_constraints = {}
|
|
89
73
|
|
|
90
|
-
if constraints_class is None or not issubclass(
|
|
91
|
-
constraints_class, ConstraintsBase
|
|
92
|
-
): # pragma: no cover
|
|
74
|
+
if constraints_class is None or not issubclass(constraints_class, ConstraintsBase): # pragma: no cover
|
|
93
75
|
return None
|
|
94
76
|
|
|
95
|
-
return constraints_class.parse_obj(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
)
|
|
77
|
+
return constraints_class.parse_obj({
|
|
78
|
+
**root_type_field_constraints,
|
|
79
|
+
**model_field_constraints,
|
|
80
|
+
})
|
|
101
81
|
|
|
102
82
|
|
|
103
83
|
class DataModelFieldBase(_BaseModel):
|
|
104
|
-
name: Optional[str] = None
|
|
105
|
-
default: Optional[Any] = None
|
|
84
|
+
name: Optional[str] = None # noqa: UP045
|
|
85
|
+
default: Optional[Any] = None # noqa: UP045
|
|
106
86
|
required: bool = False
|
|
107
|
-
alias: Optional[str] = None
|
|
87
|
+
alias: Optional[str] = None # noqa: UP045
|
|
108
88
|
data_type: DataType
|
|
109
89
|
constraints: Any = None
|
|
110
90
|
strip_default_none: bool = False
|
|
111
|
-
nullable: Optional[bool] = None
|
|
112
|
-
parent: Optional[Any] = None
|
|
113
|
-
extras:
|
|
91
|
+
nullable: Optional[bool] = None # noqa: UP045
|
|
92
|
+
parent: Optional[Any] = None # noqa: UP045
|
|
93
|
+
extras: dict[str, Any] = {} # noqa: RUF012
|
|
114
94
|
use_annotated: bool = False
|
|
115
95
|
has_default: bool = False
|
|
116
96
|
use_field_description: bool = False
|
|
117
97
|
const: bool = False
|
|
118
|
-
original_name: Optional[str] = None
|
|
98
|
+
original_name: Optional[str] = None # noqa: UP045
|
|
119
99
|
use_default_kwarg: bool = False
|
|
120
100
|
use_one_literal_as_default: bool = False
|
|
121
|
-
_exclude_fields: ClassVar[
|
|
122
|
-
_pass_fields: ClassVar[
|
|
101
|
+
_exclude_fields: ClassVar[set[str]] = {"parent"}
|
|
102
|
+
_pass_fields: ClassVar[set[str]] = {"parent", "data_type"}
|
|
123
103
|
can_have_extra_keys: ClassVar[bool] = True
|
|
124
|
-
type_has_null: Optional[bool] = None
|
|
104
|
+
type_has_null: Optional[bool] = None # noqa: UP045
|
|
125
105
|
|
|
126
106
|
if not TYPE_CHECKING:
|
|
127
107
|
|
|
@@ -132,46 +112,39 @@ class DataModelFieldBase(_BaseModel):
|
|
|
132
112
|
self.process_const()
|
|
133
113
|
|
|
134
114
|
def process_const(self) -> None:
|
|
135
|
-
if
|
|
136
|
-
return
|
|
137
|
-
self.default = self.extras[
|
|
115
|
+
if "const" not in self.extras:
|
|
116
|
+
return
|
|
117
|
+
self.default = self.extras["const"]
|
|
138
118
|
self.const = True
|
|
139
119
|
self.required = False
|
|
140
120
|
self.nullable = False
|
|
141
121
|
|
|
142
122
|
@property
|
|
143
|
-
def type_hint(self) -> str:
|
|
123
|
+
def type_hint(self) -> str: # noqa: PLR0911
|
|
144
124
|
type_hint = self.data_type.type_hint
|
|
145
125
|
|
|
146
126
|
if not type_hint:
|
|
147
127
|
return NONE
|
|
148
|
-
|
|
149
|
-
return type_hint
|
|
150
|
-
elif self.data_type.is_optional and self.data_type.type != ANY:
|
|
128
|
+
if self.has_default_factory or (self.data_type.is_optional and self.data_type.type != ANY):
|
|
151
129
|
return type_hint
|
|
152
|
-
|
|
130
|
+
if self.nullable is not None:
|
|
153
131
|
if self.nullable:
|
|
154
132
|
return get_optional_type(type_hint, self.data_type.use_union_operator)
|
|
155
133
|
return type_hint
|
|
156
|
-
|
|
134
|
+
if self.required:
|
|
157
135
|
if self.type_has_null:
|
|
158
136
|
return get_optional_type(type_hint, self.data_type.use_union_operator)
|
|
159
137
|
return type_hint
|
|
160
|
-
|
|
138
|
+
if self.fall_back_to_nullable:
|
|
161
139
|
return get_optional_type(type_hint, self.data_type.use_union_operator)
|
|
162
|
-
|
|
163
|
-
return type_hint
|
|
140
|
+
return type_hint
|
|
164
141
|
|
|
165
142
|
@property
|
|
166
|
-
def imports(self) ->
|
|
143
|
+
def imports(self) -> tuple[Import, ...]:
|
|
167
144
|
type_hint = self.type_hint
|
|
168
145
|
has_union = not self.data_type.use_union_operator and UNION_PREFIX in type_hint
|
|
169
|
-
imports:
|
|
170
|
-
iter(
|
|
171
|
-
i
|
|
172
|
-
for i in self.data_type.all_imports
|
|
173
|
-
if not (not has_union and i == IMPORT_UNION)
|
|
174
|
-
)
|
|
146
|
+
imports: list[tuple[Import] | Iterator[Import]] = [
|
|
147
|
+
iter(i for i in self.data_type.all_imports if not (not has_union and i == IMPORT_UNION))
|
|
175
148
|
]
|
|
176
149
|
|
|
177
150
|
if self.fall_back_to_nullable:
|
|
@@ -179,39 +152,31 @@ class DataModelFieldBase(_BaseModel):
|
|
|
179
152
|
self.nullable or (self.nullable is None and not self.required)
|
|
180
153
|
) and not self.data_type.use_union_operator:
|
|
181
154
|
imports.append((IMPORT_OPTIONAL,))
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
self.nullable and not self.data_type.use_union_operator
|
|
185
|
-
): # pragma: no cover
|
|
186
|
-
imports.append((IMPORT_OPTIONAL,))
|
|
155
|
+
elif self.nullable and not self.data_type.use_union_operator: # pragma: no cover
|
|
156
|
+
imports.append((IMPORT_OPTIONAL,))
|
|
187
157
|
if self.use_annotated and self.annotated:
|
|
188
|
-
|
|
189
|
-
IMPORT_ANNOTATED
|
|
190
|
-
if self.data_type.python_version.has_annotated_type
|
|
191
|
-
else IMPORT_ANNOTATED_BACKPORT
|
|
192
|
-
)
|
|
193
|
-
imports.append((import_annotated,))
|
|
158
|
+
imports.append((IMPORT_ANNOTATED,))
|
|
194
159
|
return chain_as_tuple(*imports)
|
|
195
160
|
|
|
196
161
|
@property
|
|
197
|
-
def docstring(self) ->
|
|
162
|
+
def docstring(self) -> str | None:
|
|
198
163
|
if self.use_field_description:
|
|
199
|
-
description = self.extras.get(
|
|
164
|
+
description = self.extras.get("description", None)
|
|
200
165
|
if description is not None:
|
|
201
|
-
return f
|
|
166
|
+
return f"{description}"
|
|
202
167
|
return None
|
|
203
168
|
|
|
204
169
|
@property
|
|
205
|
-
def unresolved_types(self) ->
|
|
170
|
+
def unresolved_types(self) -> frozenset[str]:
|
|
206
171
|
return self.data_type.unresolved_types
|
|
207
172
|
|
|
208
173
|
@property
|
|
209
|
-
def field(self) ->
|
|
174
|
+
def field(self) -> str | None:
|
|
210
175
|
"""for backwards compatibility"""
|
|
211
176
|
return None
|
|
212
177
|
|
|
213
178
|
@property
|
|
214
|
-
def method(self) ->
|
|
179
|
+
def method(self) -> str | None:
|
|
215
180
|
return None
|
|
216
181
|
|
|
217
182
|
@property
|
|
@@ -219,12 +184,12 @@ class DataModelFieldBase(_BaseModel):
|
|
|
219
184
|
return repr(self.default)
|
|
220
185
|
|
|
221
186
|
@property
|
|
222
|
-
def annotated(self) ->
|
|
187
|
+
def annotated(self) -> str | None:
|
|
223
188
|
return None
|
|
224
189
|
|
|
225
190
|
@property
|
|
226
191
|
def has_default_factory(self) -> bool:
|
|
227
|
-
return
|
|
192
|
+
return "default_factory" in self.extras
|
|
228
193
|
|
|
229
194
|
@property
|
|
230
195
|
def fall_back_to_nullable(self) -> bool:
|
|
@@ -234,22 +199,22 @@ class DataModelFieldBase(_BaseModel):
|
|
|
234
199
|
@lru_cache
|
|
235
200
|
def get_template(template_file_path: Path) -> Template:
|
|
236
201
|
loader = FileSystemLoader(str(TEMPLATE_DIR / template_file_path.parent))
|
|
237
|
-
environment: Environment = Environment(loader=loader)
|
|
202
|
+
environment: Environment = Environment(loader=loader) # noqa: S701
|
|
238
203
|
return environment.get_template(template_file_path.name)
|
|
239
204
|
|
|
240
205
|
|
|
241
|
-
def get_module_path(name: str, file_path:
|
|
206
|
+
def get_module_path(name: str, file_path: Path | None) -> list[str]:
|
|
242
207
|
if file_path:
|
|
243
208
|
return [
|
|
244
209
|
*file_path.parts[:-1],
|
|
245
210
|
file_path.stem,
|
|
246
|
-
*name.split(
|
|
211
|
+
*name.split(".")[:-1],
|
|
247
212
|
]
|
|
248
|
-
return name.split(
|
|
213
|
+
return name.split(".")[:-1]
|
|
249
214
|
|
|
250
215
|
|
|
251
|
-
def get_module_name(name: str, file_path:
|
|
252
|
-
return
|
|
216
|
+
def get_module_name(name: str, file_path: Path | None) -> str:
|
|
217
|
+
return ".".join(get_module_path(name, file_path))
|
|
253
218
|
|
|
254
219
|
|
|
255
220
|
class TemplateBase(ABC):
|
|
@@ -280,43 +245,42 @@ UNDEFINED: Any = object()
|
|
|
280
245
|
|
|
281
246
|
|
|
282
247
|
class DataModel(TemplateBase, Nullable, ABC):
|
|
283
|
-
TEMPLATE_FILE_PATH: ClassVar[str] =
|
|
284
|
-
BASE_CLASS: ClassVar[str] =
|
|
285
|
-
DEFAULT_IMPORTS: ClassVar[
|
|
248
|
+
TEMPLATE_FILE_PATH: ClassVar[str] = ""
|
|
249
|
+
BASE_CLASS: ClassVar[str] = ""
|
|
250
|
+
DEFAULT_IMPORTS: ClassVar[tuple[Import, ...]] = ()
|
|
286
251
|
|
|
287
|
-
def __init__(
|
|
252
|
+
def __init__( # noqa: PLR0913
|
|
288
253
|
self,
|
|
289
254
|
*,
|
|
290
255
|
reference: Reference,
|
|
291
|
-
fields:
|
|
292
|
-
decorators:
|
|
293
|
-
base_classes:
|
|
294
|
-
custom_base_class:
|
|
295
|
-
custom_template_dir:
|
|
296
|
-
extra_template_data:
|
|
297
|
-
methods:
|
|
298
|
-
path:
|
|
299
|
-
description:
|
|
256
|
+
fields: list[DataModelFieldBase],
|
|
257
|
+
decorators: list[str] | None = None,
|
|
258
|
+
base_classes: list[Reference] | None = None,
|
|
259
|
+
custom_base_class: str | None = None,
|
|
260
|
+
custom_template_dir: Path | None = None,
|
|
261
|
+
extra_template_data: defaultdict[str, dict[str, Any]] | None = None,
|
|
262
|
+
methods: list[str] | None = None,
|
|
263
|
+
path: Path | None = None,
|
|
264
|
+
description: str | None = None,
|
|
300
265
|
default: Any = UNDEFINED,
|
|
301
266
|
nullable: bool = False,
|
|
302
267
|
keyword_only: bool = False,
|
|
303
268
|
) -> None:
|
|
304
269
|
self.keyword_only = keyword_only
|
|
305
270
|
if not self.TEMPLATE_FILE_PATH:
|
|
306
|
-
|
|
271
|
+
msg = "TEMPLATE_FILE_PATH is undefined"
|
|
272
|
+
raise Exception(msg) # noqa: TRY002
|
|
307
273
|
|
|
308
|
-
self._custom_template_dir:
|
|
309
|
-
self.decorators:
|
|
310
|
-
self._additional_imports:
|
|
274
|
+
self._custom_template_dir: Path | None = custom_template_dir
|
|
275
|
+
self.decorators: list[str] = decorators or []
|
|
276
|
+
self._additional_imports: list[Import] = []
|
|
311
277
|
self.custom_base_class = custom_base_class
|
|
312
278
|
if base_classes:
|
|
313
|
-
self.base_classes:
|
|
314
|
-
BaseClassDataType(reference=b) for b in base_classes
|
|
315
|
-
]
|
|
279
|
+
self.base_classes: list[BaseClassDataType] = [BaseClassDataType(reference=b) for b in base_classes]
|
|
316
280
|
else:
|
|
317
281
|
self.set_base_class()
|
|
318
282
|
|
|
319
|
-
self.file_path:
|
|
283
|
+
self.file_path: Path | None = path
|
|
320
284
|
self.reference: Reference = reference
|
|
321
285
|
|
|
322
286
|
self.reference.source = self
|
|
@@ -324,9 +288,7 @@ class DataModel(TemplateBase, Nullable, ABC):
|
|
|
324
288
|
self.extra_template_data = (
|
|
325
289
|
# The supplied defaultdict will either create a new entry,
|
|
326
290
|
# or already contain a predefined entry for this type
|
|
327
|
-
extra_template_data[self.name]
|
|
328
|
-
if extra_template_data is not None
|
|
329
|
-
else defaultdict(dict)
|
|
291
|
+
extra_template_data[self.name] if extra_template_data is not None else defaultdict(dict)
|
|
330
292
|
)
|
|
331
293
|
|
|
332
294
|
self.fields = self._validate_fields(fields) if fields else []
|
|
@@ -342,7 +304,7 @@ class DataModel(TemplateBase, Nullable, ABC):
|
|
|
342
304
|
# end up inadvertently sharing state (such as "base_class_kwargs")
|
|
343
305
|
self.extra_template_data.update(deepcopy(all_model_extra_template_data))
|
|
344
306
|
|
|
345
|
-
self.methods:
|
|
307
|
+
self.methods: list[str] = methods or []
|
|
346
308
|
|
|
347
309
|
self.description = description
|
|
348
310
|
for field in self.fields:
|
|
@@ -352,18 +314,15 @@ class DataModel(TemplateBase, Nullable, ABC):
|
|
|
352
314
|
self.default: Any = default
|
|
353
315
|
self._nullable: bool = nullable
|
|
354
316
|
|
|
355
|
-
def _validate_fields(
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
names: Set[str] = set()
|
|
359
|
-
unique_fields: List[DataModelFieldBase] = []
|
|
317
|
+
def _validate_fields(self, fields: list[DataModelFieldBase]) -> list[DataModelFieldBase]:
|
|
318
|
+
names: set[str] = set()
|
|
319
|
+
unique_fields: list[DataModelFieldBase] = []
|
|
360
320
|
for field in fields:
|
|
361
321
|
if field.name:
|
|
362
322
|
if field.name in names:
|
|
363
|
-
warn(f
|
|
323
|
+
warn(f"Field name `{field.name}` is duplicated on {self.name}", stacklevel=2)
|
|
364
324
|
continue
|
|
365
|
-
|
|
366
|
-
names.add(field.name)
|
|
325
|
+
names.add(field.name)
|
|
367
326
|
unique_fields.append(field)
|
|
368
327
|
return unique_fields
|
|
369
328
|
|
|
@@ -371,7 +330,7 @@ class DataModel(TemplateBase, Nullable, ABC):
|
|
|
371
330
|
base_class = self.custom_base_class or self.BASE_CLASS
|
|
372
331
|
if not base_class:
|
|
373
332
|
self.base_classes = []
|
|
374
|
-
return
|
|
333
|
+
return
|
|
375
334
|
base_class_import = Import.from_full_path(base_class)
|
|
376
335
|
self._additional_imports.append(base_class_import)
|
|
377
336
|
self.base_classes = [BaseClassDataType.from_import(base_class_import)]
|
|
@@ -386,14 +345,14 @@ class DataModel(TemplateBase, Nullable, ABC):
|
|
|
386
345
|
return template_file_path
|
|
387
346
|
|
|
388
347
|
@property
|
|
389
|
-
def imports(self) ->
|
|
348
|
+
def imports(self) -> tuple[Import, ...]:
|
|
390
349
|
return chain_as_tuple(
|
|
391
350
|
(i for f in self.fields for i in f.imports),
|
|
392
351
|
self._additional_imports,
|
|
393
352
|
)
|
|
394
353
|
|
|
395
354
|
@property
|
|
396
|
-
def reference_classes(self) ->
|
|
355
|
+
def reference_classes(self) -> frozenset[str]:
|
|
397
356
|
return frozenset(
|
|
398
357
|
{r.reference.path for r in self.base_classes if r.reference}
|
|
399
358
|
| {t for f in self.fields for t in f.unresolved_types}
|
|
@@ -405,16 +364,16 @@ class DataModel(TemplateBase, Nullable, ABC):
|
|
|
405
364
|
|
|
406
365
|
@property
|
|
407
366
|
def duplicate_name(self) -> str:
|
|
408
|
-
return self.reference.duplicate_name or
|
|
367
|
+
return self.reference.duplicate_name or ""
|
|
409
368
|
|
|
410
369
|
@property
|
|
411
370
|
def base_class(self) -> str:
|
|
412
|
-
return
|
|
371
|
+
return ", ".join(b.type_hint for b in self.base_classes)
|
|
413
372
|
|
|
414
373
|
@staticmethod
|
|
415
374
|
def _get_class_name(name: str) -> str:
|
|
416
|
-
if
|
|
417
|
-
return name.rsplit(
|
|
375
|
+
if "." in name:
|
|
376
|
+
return name.rsplit(".", 1)[-1]
|
|
418
377
|
return name
|
|
419
378
|
|
|
420
379
|
@property
|
|
@@ -423,10 +382,8 @@ class DataModel(TemplateBase, Nullable, ABC):
|
|
|
423
382
|
|
|
424
383
|
@class_name.setter
|
|
425
384
|
def class_name(self, class_name: str) -> None:
|
|
426
|
-
if
|
|
427
|
-
self.reference.name = (
|
|
428
|
-
f'{self.reference.name.rsplit(".", 1)[0]}.{class_name}'
|
|
429
|
-
)
|
|
385
|
+
if "." in self.reference.name:
|
|
386
|
+
self.reference.name = f"{self.reference.name.rsplit('.', 1)[0]}.{class_name}"
|
|
430
387
|
else:
|
|
431
388
|
self.reference.name = class_name
|
|
432
389
|
|
|
@@ -435,7 +392,7 @@ class DataModel(TemplateBase, Nullable, ABC):
|
|
|
435
392
|
return self._get_class_name(self.duplicate_name)
|
|
436
393
|
|
|
437
394
|
@property
|
|
438
|
-
def module_path(self) ->
|
|
395
|
+
def module_path(self) -> list[str]:
|
|
439
396
|
return get_module_path(self.name, self.file_path)
|
|
440
397
|
|
|
441
398
|
@property
|
|
@@ -456,8 +413,8 @@ class DataModel(TemplateBase, Nullable, ABC):
|
|
|
456
413
|
def path(self) -> str:
|
|
457
414
|
return self.reference.path
|
|
458
415
|
|
|
459
|
-
def render(self, *, class_name:
|
|
460
|
-
|
|
416
|
+
def render(self, *, class_name: str | None = None) -> str:
|
|
417
|
+
return self._render(
|
|
461
418
|
class_name=class_name or self.class_name,
|
|
462
419
|
fields=self.fields,
|
|
463
420
|
decorators=self.decorators,
|
|
@@ -467,4 +424,3 @@ class DataModel(TemplateBase, Nullable, ABC):
|
|
|
467
424
|
keyword_only=self.keyword_only,
|
|
468
425
|
**self.extra_template_data,
|
|
469
426
|
)
|
|
470
|
-
return response
|