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,15 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from enum import Enum
|
|
4
|
+
from functools import cached_property
|
|
4
5
|
from importlib import import_module
|
|
5
6
|
from pathlib import Path
|
|
6
|
-
from typing import TYPE_CHECKING, Any
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
7
8
|
from warnings import warn
|
|
8
9
|
|
|
9
10
|
import black
|
|
10
11
|
import isort
|
|
11
12
|
|
|
12
|
-
from datamodel_code_generator.util import
|
|
13
|
+
from datamodel_code_generator.util import load_toml
|
|
13
14
|
|
|
14
15
|
try:
|
|
15
16
|
import black.mode
|
|
@@ -18,64 +19,30 @@ except ImportError: # pragma: no cover
|
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
class DatetimeClassType(Enum):
|
|
21
|
-
Datetime =
|
|
22
|
-
Awaredatetime =
|
|
23
|
-
Naivedatetime =
|
|
22
|
+
Datetime = "datetime"
|
|
23
|
+
Awaredatetime = "AwareDatetime"
|
|
24
|
+
Naivedatetime = "NaiveDatetime"
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
class PythonVersion(Enum):
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
PY_311 = '3.11'
|
|
33
|
-
PY_312 = '3.12'
|
|
34
|
-
PY_313 = '3.13'
|
|
35
|
-
|
|
36
|
-
@cached_property
|
|
37
|
-
def _is_py_38_or_later(self) -> bool: # pragma: no cover
|
|
38
|
-
return self.value not in {self.PY_36.value, self.PY_37.value} # type: ignore
|
|
39
|
-
|
|
40
|
-
@cached_property
|
|
41
|
-
def _is_py_39_or_later(self) -> bool: # pragma: no cover
|
|
42
|
-
return self.value not in {self.PY_36.value, self.PY_37.value, self.PY_38.value} # type: ignore
|
|
28
|
+
PY_39 = "3.9"
|
|
29
|
+
PY_310 = "3.10"
|
|
30
|
+
PY_311 = "3.11"
|
|
31
|
+
PY_312 = "3.12"
|
|
32
|
+
PY_313 = "3.13"
|
|
43
33
|
|
|
44
34
|
@cached_property
|
|
45
35
|
def _is_py_310_or_later(self) -> bool: # pragma: no cover
|
|
46
|
-
return self.value
|
|
47
|
-
self.PY_36.value,
|
|
48
|
-
self.PY_37.value,
|
|
49
|
-
self.PY_38.value,
|
|
50
|
-
self.PY_39.value,
|
|
51
|
-
} # type: ignore
|
|
36
|
+
return self.value != self.PY_39.value
|
|
52
37
|
|
|
53
38
|
@cached_property
|
|
54
39
|
def _is_py_311_or_later(self) -> bool: # pragma: no cover
|
|
55
|
-
return self.value not in {
|
|
56
|
-
self.PY_36.value,
|
|
57
|
-
self.PY_37.value,
|
|
58
|
-
self.PY_38.value,
|
|
59
|
-
self.PY_39.value,
|
|
60
|
-
self.PY_310.value,
|
|
61
|
-
} # type: ignore
|
|
62
|
-
|
|
63
|
-
@property
|
|
64
|
-
def has_literal_type(self) -> bool:
|
|
65
|
-
return self._is_py_38_or_later
|
|
40
|
+
return self.value not in {self.PY_39.value, self.PY_310.value}
|
|
66
41
|
|
|
67
42
|
@property
|
|
68
43
|
def has_union_operator(self) -> bool: # pragma: no cover
|
|
69
44
|
return self._is_py_310_or_later
|
|
70
45
|
|
|
71
|
-
@property
|
|
72
|
-
def has_annotated_type(self) -> bool:
|
|
73
|
-
return self._is_py_39_or_later
|
|
74
|
-
|
|
75
|
-
@property
|
|
76
|
-
def has_typed_dict(self) -> bool:
|
|
77
|
-
return self._is_py_38_or_later
|
|
78
|
-
|
|
79
46
|
@property
|
|
80
47
|
def has_typed_dict_non_required(self) -> bool:
|
|
81
48
|
return self._is_py_311_or_later
|
|
@@ -85,16 +52,19 @@ class PythonVersion(Enum):
|
|
|
85
52
|
return self._is_py_310_or_later
|
|
86
53
|
|
|
87
54
|
|
|
55
|
+
PythonVersionMin = PythonVersion.PY_39
|
|
56
|
+
|
|
88
57
|
if TYPE_CHECKING:
|
|
58
|
+
from collections.abc import Sequence
|
|
89
59
|
|
|
90
60
|
class _TargetVersion(Enum): ...
|
|
91
61
|
|
|
92
|
-
BLACK_PYTHON_VERSION:
|
|
62
|
+
BLACK_PYTHON_VERSION: dict[PythonVersion, _TargetVersion]
|
|
93
63
|
else:
|
|
94
|
-
BLACK_PYTHON_VERSION:
|
|
95
|
-
v: getattr(black.TargetVersion, f
|
|
64
|
+
BLACK_PYTHON_VERSION: dict[PythonVersion, black.TargetVersion] = {
|
|
65
|
+
v: getattr(black.TargetVersion, f"PY{v.name.split('_')[-1]}")
|
|
96
66
|
for v in PythonVersion
|
|
97
|
-
if hasattr(black.TargetVersion, f
|
|
67
|
+
if hasattr(black.TargetVersion, f"PY{v.name.split('_')[-1]}")
|
|
98
68
|
}
|
|
99
69
|
|
|
100
70
|
|
|
@@ -104,132 +74,111 @@ def is_supported_in_black(python_version: PythonVersion) -> bool: # pragma: no
|
|
|
104
74
|
|
|
105
75
|
def black_find_project_root(sources: Sequence[Path]) -> Path:
|
|
106
76
|
if TYPE_CHECKING:
|
|
107
|
-
from
|
|
77
|
+
from collections.abc import Iterable # noqa: PLC0415
|
|
108
78
|
|
|
109
79
|
def _find_project_root(
|
|
110
|
-
srcs:
|
|
111
|
-
) ->
|
|
80
|
+
srcs: Sequence[str] | Iterable[str],
|
|
81
|
+
) -> tuple[Path, str] | Path: ...
|
|
112
82
|
|
|
113
83
|
else:
|
|
114
|
-
from black import find_project_root as _find_project_root
|
|
84
|
+
from black import find_project_root as _find_project_root # noqa: PLC0415
|
|
115
85
|
project_root = _find_project_root(tuple(str(s) for s in sources))
|
|
116
86
|
if isinstance(project_root, tuple):
|
|
117
87
|
return project_root[0]
|
|
118
|
-
|
|
119
|
-
|
|
88
|
+
# pragma: no cover
|
|
89
|
+
return project_root
|
|
120
90
|
|
|
121
91
|
|
|
122
92
|
class CodeFormatter:
|
|
123
|
-
def __init__(
|
|
93
|
+
def __init__( # noqa: PLR0912, PLR0913, PLR0917
|
|
124
94
|
self,
|
|
125
95
|
python_version: PythonVersion,
|
|
126
|
-
settings_path:
|
|
127
|
-
wrap_string_literal:
|
|
128
|
-
skip_string_normalization: bool = True,
|
|
129
|
-
known_third_party:
|
|
130
|
-
custom_formatters:
|
|
131
|
-
custom_formatters_kwargs:
|
|
96
|
+
settings_path: Path | None = None,
|
|
97
|
+
wrap_string_literal: bool | None = None, # noqa: FBT001
|
|
98
|
+
skip_string_normalization: bool = True, # noqa: FBT001, FBT002
|
|
99
|
+
known_third_party: list[str] | None = None,
|
|
100
|
+
custom_formatters: list[str] | None = None,
|
|
101
|
+
custom_formatters_kwargs: dict[str, Any] | None = None,
|
|
132
102
|
) -> None:
|
|
133
103
|
if not settings_path:
|
|
134
|
-
settings_path = Path
|
|
104
|
+
settings_path = Path.cwd()
|
|
135
105
|
|
|
136
106
|
root = black_find_project_root((settings_path,))
|
|
137
|
-
path = root /
|
|
107
|
+
path = root / "pyproject.toml"
|
|
138
108
|
if path.is_file():
|
|
139
109
|
pyproject_toml = load_toml(path)
|
|
140
|
-
config = pyproject_toml.get(
|
|
110
|
+
config = pyproject_toml.get("tool", {}).get("black", {})
|
|
141
111
|
else:
|
|
142
112
|
config = {}
|
|
143
113
|
|
|
144
|
-
black_kwargs:
|
|
114
|
+
black_kwargs: dict[str, Any] = {}
|
|
145
115
|
if wrap_string_literal is not None:
|
|
146
116
|
experimental_string_processing = wrap_string_literal
|
|
117
|
+
elif black.__version__ < "24.1.0":
|
|
118
|
+
experimental_string_processing = config.get("experimental-string-processing")
|
|
147
119
|
else:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
)
|
|
152
|
-
else:
|
|
153
|
-
experimental_string_processing = config.get(
|
|
154
|
-
'preview', False
|
|
155
|
-
) and ( # pragma: no cover
|
|
156
|
-
config.get('unstable', False)
|
|
157
|
-
or 'string_processing' in config.get('enable-unstable-feature', [])
|
|
158
|
-
)
|
|
120
|
+
experimental_string_processing = config.get("preview", False) and ( # pragma: no cover
|
|
121
|
+
config.get("unstable", False) or "string_processing" in config.get("enable-unstable-feature", [])
|
|
122
|
+
)
|
|
159
123
|
|
|
160
124
|
if experimental_string_processing is not None: # pragma: no cover
|
|
161
|
-
if black.__version__.startswith(
|
|
125
|
+
if black.__version__.startswith("19."):
|
|
162
126
|
warn(
|
|
163
|
-
f"black doesn't support `experimental-string-processing` option"
|
|
164
|
-
f
|
|
165
|
-
|
|
166
|
-
elif black.__version__ < '24.1.0': # type: ignore
|
|
167
|
-
black_kwargs['experimental_string_processing'] = (
|
|
168
|
-
experimental_string_processing
|
|
127
|
+
f"black doesn't support `experimental-string-processing` option"
|
|
128
|
+
f" for wrapping string literal in {black.__version__}",
|
|
129
|
+
stacklevel=2,
|
|
169
130
|
)
|
|
131
|
+
elif black.__version__ < "24.1.0":
|
|
132
|
+
black_kwargs["experimental_string_processing"] = experimental_string_processing
|
|
170
133
|
elif experimental_string_processing:
|
|
171
|
-
black_kwargs[
|
|
172
|
-
black_kwargs[
|
|
173
|
-
black_kwargs[
|
|
174
|
-
black.mode.Preview.string_processing
|
|
175
|
-
}
|
|
134
|
+
black_kwargs["preview"] = True
|
|
135
|
+
black_kwargs["unstable"] = config.get("unstable", False)
|
|
136
|
+
black_kwargs["enabled_features"] = {black.mode.Preview.string_processing}
|
|
176
137
|
|
|
177
138
|
if TYPE_CHECKING:
|
|
178
139
|
self.black_mode: black.FileMode
|
|
179
140
|
else:
|
|
180
141
|
self.black_mode = black.FileMode(
|
|
181
142
|
target_versions={BLACK_PYTHON_VERSION[python_version]},
|
|
182
|
-
line_length=config.get(
|
|
183
|
-
string_normalization=not skip_string_normalization
|
|
184
|
-
or not config.get('skip-string-normalization', True),
|
|
143
|
+
line_length=config.get("line-length", black.DEFAULT_LINE_LENGTH),
|
|
144
|
+
string_normalization=not skip_string_normalization or not config.get("skip-string-normalization", True),
|
|
185
145
|
**black_kwargs,
|
|
186
146
|
)
|
|
187
147
|
|
|
188
148
|
self.settings_path: str = str(settings_path)
|
|
189
149
|
|
|
190
|
-
self.isort_config_kwargs:
|
|
150
|
+
self.isort_config_kwargs: dict[str, Any] = {}
|
|
191
151
|
if known_third_party:
|
|
192
|
-
self.isort_config_kwargs[
|
|
152
|
+
self.isort_config_kwargs["known_third_party"] = known_third_party
|
|
193
153
|
|
|
194
|
-
if isort.__version__.startswith(
|
|
154
|
+
if isort.__version__.startswith("4."):
|
|
195
155
|
self.isort_config = None
|
|
196
156
|
else:
|
|
197
|
-
self.isort_config = isort.Config(
|
|
198
|
-
settings_path=self.settings_path, **self.isort_config_kwargs
|
|
199
|
-
)
|
|
157
|
+
self.isort_config = isort.Config(settings_path=self.settings_path, **self.isort_config_kwargs)
|
|
200
158
|
|
|
201
159
|
self.custom_formatters_kwargs = custom_formatters_kwargs or {}
|
|
202
160
|
self.custom_formatters = self._check_custom_formatters(custom_formatters)
|
|
203
161
|
|
|
204
|
-
def _load_custom_formatter(
|
|
205
|
-
self, custom_formatter_import: str
|
|
206
|
-
) -> CustomCodeFormatter:
|
|
162
|
+
def _load_custom_formatter(self, custom_formatter_import: str) -> CustomCodeFormatter:
|
|
207
163
|
import_ = import_module(custom_formatter_import)
|
|
208
164
|
|
|
209
|
-
if not hasattr(import_,
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
)
|
|
165
|
+
if not hasattr(import_, "CodeFormatter"):
|
|
166
|
+
msg = f"Custom formatter module `{import_.__name__}` must contains object with name Formatter"
|
|
167
|
+
raise NameError(msg)
|
|
213
168
|
|
|
214
|
-
formatter_class = import_.__getattribute__(
|
|
169
|
+
formatter_class = import_.__getattribute__("CodeFormatter") # noqa: PLC2801
|
|
215
170
|
|
|
216
171
|
if not issubclass(formatter_class, CustomCodeFormatter):
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
)
|
|
172
|
+
msg = f"The custom module {custom_formatter_import} must inherit from `datamodel-code-generator`"
|
|
173
|
+
raise TypeError(msg)
|
|
220
174
|
|
|
221
175
|
return formatter_class(formatter_kwargs=self.custom_formatters_kwargs)
|
|
222
176
|
|
|
223
|
-
def _check_custom_formatters(
|
|
224
|
-
self, custom_formatters: Optional[List[str]]
|
|
225
|
-
) -> List[CustomCodeFormatter]:
|
|
177
|
+
def _check_custom_formatters(self, custom_formatters: list[str] | None) -> list[CustomCodeFormatter]:
|
|
226
178
|
if custom_formatters is None:
|
|
227
179
|
return []
|
|
228
180
|
|
|
229
|
-
return [
|
|
230
|
-
self._load_custom_formatter(custom_formatter_import)
|
|
231
|
-
for custom_formatter_import in custom_formatters
|
|
232
|
-
]
|
|
181
|
+
return [self._load_custom_formatter(custom_formatter_import) for custom_formatter_import in custom_formatters]
|
|
233
182
|
|
|
234
183
|
def format_code(
|
|
235
184
|
self,
|
|
@@ -253,24 +202,23 @@ class CodeFormatter:
|
|
|
253
202
|
|
|
254
203
|
def apply_isort(self, code: str) -> str: ...
|
|
255
204
|
|
|
256
|
-
|
|
257
|
-
if isort.__version__.startswith('4.'):
|
|
205
|
+
elif isort.__version__.startswith("4."):
|
|
258
206
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
207
|
+
def apply_isort(self, code: str) -> str:
|
|
208
|
+
return isort.SortImports(
|
|
209
|
+
file_contents=code,
|
|
210
|
+
settings_path=self.settings_path,
|
|
211
|
+
**self.isort_config_kwargs,
|
|
212
|
+
).output
|
|
265
213
|
|
|
266
|
-
|
|
214
|
+
else:
|
|
267
215
|
|
|
268
|
-
|
|
269
|
-
|
|
216
|
+
def apply_isort(self, code: str) -> str:
|
|
217
|
+
return isort.code(code, config=self.isort_config)
|
|
270
218
|
|
|
271
219
|
|
|
272
220
|
class CustomCodeFormatter:
|
|
273
|
-
def __init__(self, formatter_kwargs:
|
|
221
|
+
def __init__(self, formatter_kwargs: dict[str, Any]) -> None:
|
|
274
222
|
self.formatter_kwargs = formatter_kwargs
|
|
275
223
|
|
|
276
224
|
def apply(self, code: str) -> str:
|
datamodel_code_generator/http.py
CHANGED
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from collections.abc import Sequence
|
|
4
7
|
|
|
5
8
|
try:
|
|
6
9
|
import httpx
|
|
7
|
-
except ImportError: # pragma: no cover
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
)
|
|
10
|
+
except ImportError as exc: # pragma: no cover
|
|
11
|
+
msg = "Please run `$pip install 'datamodel-code-generator[http]`' to resolve URL Reference"
|
|
12
|
+
raise Exception(msg) from exc # noqa: TRY002
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
def get_body(
|
|
14
16
|
url: str,
|
|
15
|
-
headers:
|
|
16
|
-
ignore_tls: bool = False,
|
|
17
|
-
query_parameters:
|
|
17
|
+
headers: Sequence[tuple[str, str]] | None = None,
|
|
18
|
+
ignore_tls: bool = False, # noqa: FBT001, FBT002
|
|
19
|
+
query_parameters: Sequence[tuple[str, str]] | None = None,
|
|
18
20
|
) -> str:
|
|
19
21
|
return httpx.get(
|
|
20
22
|
url,
|
|
21
23
|
headers=headers,
|
|
22
24
|
verify=not ignore_tls,
|
|
23
25
|
follow_redirects=True,
|
|
24
|
-
params=query_parameters, # pyright: ignore
|
|
26
|
+
params=query_parameters, # pyright: ignore[reportArgumentType]
|
|
25
27
|
# TODO: Improve params type
|
|
26
28
|
).text
|
|
27
29
|
|
|
28
30
|
|
|
29
|
-
def join_url(url: str, ref: str =
|
|
31
|
+
def join_url(url: str, ref: str = ".") -> str:
|
|
30
32
|
return str(httpx.URL(url).join(ref))
|
|
@@ -2,86 +2,82 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from collections import defaultdict
|
|
4
4
|
from functools import lru_cache
|
|
5
|
-
from
|
|
5
|
+
from itertools import starmap
|
|
6
|
+
from typing import TYPE_CHECKING, Optional
|
|
6
7
|
|
|
7
8
|
from datamodel_code_generator.util import BaseModel
|
|
8
9
|
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from collections.abc import Iterable
|
|
12
|
+
|
|
9
13
|
|
|
10
14
|
class Import(BaseModel):
|
|
11
|
-
from_: Optional[str] = None
|
|
15
|
+
from_: Optional[str] = None # noqa: UP045
|
|
12
16
|
import_: str
|
|
13
|
-
alias: Optional[str] = None
|
|
14
|
-
reference_path: Optional[str] = None
|
|
17
|
+
alias: Optional[str] = None # noqa: UP045
|
|
18
|
+
reference_path: Optional[str] = None # noqa: UP045
|
|
15
19
|
|
|
16
20
|
@classmethod
|
|
17
21
|
@lru_cache
|
|
18
22
|
def from_full_path(cls, class_path: str) -> Import:
|
|
19
|
-
split_class_path:
|
|
20
|
-
return Import(
|
|
21
|
-
from_='.'.join(split_class_path[:-1]) or None, import_=split_class_path[-1]
|
|
22
|
-
)
|
|
23
|
+
split_class_path: list[str] = class_path.split(".")
|
|
24
|
+
return Import(from_=".".join(split_class_path[:-1]) or None, import_=split_class_path[-1])
|
|
23
25
|
|
|
24
26
|
|
|
25
|
-
class Imports(
|
|
27
|
+
class Imports(defaultdict[Optional[str], set[str]]):
|
|
26
28
|
def __str__(self) -> str:
|
|
27
29
|
return self.dump()
|
|
28
30
|
|
|
29
|
-
def __init__(self, use_exact: bool = False) -> None:
|
|
31
|
+
def __init__(self, use_exact: bool = False) -> None: # noqa: FBT001, FBT002
|
|
30
32
|
super().__init__(set)
|
|
31
|
-
self.alias:
|
|
32
|
-
self.counter:
|
|
33
|
-
self.reference_paths:
|
|
33
|
+
self.alias: defaultdict[str | None, dict[str, str]] = defaultdict(dict)
|
|
34
|
+
self.counter: dict[tuple[str | None, str], int] = defaultdict(int)
|
|
35
|
+
self.reference_paths: dict[str, Import] = {}
|
|
34
36
|
self.use_exact: bool = use_exact
|
|
35
37
|
|
|
36
|
-
def _set_alias(self, from_:
|
|
38
|
+
def _set_alias(self, from_: str | None, imports: set[str]) -> list[str]:
|
|
37
39
|
return [
|
|
38
|
-
f
|
|
39
|
-
if i in self.alias[from_] and i != self.alias[from_][i]
|
|
40
|
-
else i
|
|
40
|
+
f"{i} as {self.alias[from_][i]}" if i in self.alias[from_] and i != self.alias[from_][i] else i
|
|
41
41
|
for i in sorted(imports)
|
|
42
42
|
]
|
|
43
43
|
|
|
44
|
-
def create_line(self, from_:
|
|
44
|
+
def create_line(self, from_: str | None, imports: set[str]) -> str:
|
|
45
45
|
if from_:
|
|
46
|
-
return f
|
|
47
|
-
return
|
|
46
|
+
return f"from {from_} import {', '.join(self._set_alias(from_, imports))}"
|
|
47
|
+
return "\n".join(f"import {i}" for i in self._set_alias(from_, imports))
|
|
48
48
|
|
|
49
49
|
def dump(self) -> str:
|
|
50
|
-
return
|
|
51
|
-
self.create_line(from_, imports) for from_, imports in self.items()
|
|
52
|
-
)
|
|
50
|
+
return "\n".join(starmap(self.create_line, self.items()))
|
|
53
51
|
|
|
54
|
-
def append(self, imports:
|
|
52
|
+
def append(self, imports: Import | Iterable[Import] | None) -> None:
|
|
55
53
|
if imports:
|
|
56
54
|
if isinstance(imports, Import):
|
|
57
55
|
imports = [imports]
|
|
58
56
|
for import_ in imports:
|
|
59
57
|
if import_.reference_path:
|
|
60
58
|
self.reference_paths[import_.reference_path] = import_
|
|
61
|
-
if
|
|
59
|
+
if "." in import_.import_:
|
|
62
60
|
self[None].add(import_.import_)
|
|
63
|
-
self.counter[
|
|
61
|
+
self.counter[None, import_.import_] += 1
|
|
64
62
|
else:
|
|
65
63
|
self[import_.from_].add(import_.import_)
|
|
66
|
-
self.counter[
|
|
64
|
+
self.counter[import_.from_, import_.import_] += 1
|
|
67
65
|
if import_.alias:
|
|
68
66
|
self.alias[import_.from_][import_.import_] = import_.alias
|
|
69
67
|
|
|
70
|
-
def remove(self, imports:
|
|
68
|
+
def remove(self, imports: Import | Iterable[Import]) -> None:
|
|
71
69
|
if isinstance(imports, Import): # pragma: no cover
|
|
72
70
|
imports = [imports]
|
|
73
71
|
for import_ in imports:
|
|
74
|
-
if
|
|
75
|
-
self.counter[
|
|
76
|
-
if self.counter[
|
|
72
|
+
if "." in import_.import_: # pragma: no cover
|
|
73
|
+
self.counter[None, import_.import_] -= 1
|
|
74
|
+
if self.counter[None, import_.import_] == 0: # pragma: no cover
|
|
77
75
|
self[None].remove(import_.import_)
|
|
78
76
|
if not self[None]:
|
|
79
77
|
del self[None]
|
|
80
78
|
else:
|
|
81
|
-
self.counter[
|
|
82
|
-
if
|
|
83
|
-
self.counter[(import_.from_, import_.import_)] == 0
|
|
84
|
-
): # pragma: no cover
|
|
79
|
+
self.counter[import_.from_, import_.import_] -= 1 # pragma: no cover
|
|
80
|
+
if self.counter[import_.from_, import_.import_] == 0: # pragma: no cover
|
|
85
81
|
self[import_.from_].remove(import_.import_)
|
|
86
82
|
if not self[import_.from_]:
|
|
87
83
|
del self[import_.from_]
|
|
@@ -95,33 +91,31 @@ class Imports(DefaultDict[Optional[str], Set[str]]):
|
|
|
95
91
|
self.remove(self.reference_paths[reference_path])
|
|
96
92
|
|
|
97
93
|
|
|
98
|
-
IMPORT_ANNOTATED = Import.from_full_path(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
IMPORT_PENDULUM_DURATION = Import.from_full_path('pendulum.Duration')
|
|
127
|
-
IMPORT_PENDULUM_TIME = Import.from_full_path('pendulum.Time')
|
|
94
|
+
IMPORT_ANNOTATED = Import.from_full_path("typing.Annotated")
|
|
95
|
+
IMPORT_ANY = Import.from_full_path("typing.Any")
|
|
96
|
+
IMPORT_LIST = Import.from_full_path("typing.List")
|
|
97
|
+
IMPORT_SET = Import.from_full_path("typing.Set")
|
|
98
|
+
IMPORT_UNION = Import.from_full_path("typing.Union")
|
|
99
|
+
IMPORT_OPTIONAL = Import.from_full_path("typing.Optional")
|
|
100
|
+
IMPORT_LITERAL = Import.from_full_path("typing.Literal")
|
|
101
|
+
IMPORT_TYPE_ALIAS = Import.from_full_path("typing.TypeAlias")
|
|
102
|
+
IMPORT_SEQUENCE = Import.from_full_path("typing.Sequence")
|
|
103
|
+
IMPORT_FROZEN_SET = Import.from_full_path("typing.FrozenSet")
|
|
104
|
+
IMPORT_MAPPING = Import.from_full_path("typing.Mapping")
|
|
105
|
+
IMPORT_ABC_SEQUENCE = Import.from_full_path("collections.abc.Sequence")
|
|
106
|
+
IMPORT_ABC_SET = Import.from_full_path("collections.abc.Set")
|
|
107
|
+
IMPORT_ABC_MAPPING = Import.from_full_path("collections.abc.Mapping")
|
|
108
|
+
IMPORT_ENUM = Import.from_full_path("enum.Enum")
|
|
109
|
+
IMPORT_ANNOTATIONS = Import.from_full_path("__future__.annotations")
|
|
110
|
+
IMPORT_DICT = Import.from_full_path("typing.Dict")
|
|
111
|
+
IMPORT_DECIMAL = Import.from_full_path("decimal.Decimal")
|
|
112
|
+
IMPORT_DATE = Import.from_full_path("datetime.date")
|
|
113
|
+
IMPORT_DATETIME = Import.from_full_path("datetime.datetime")
|
|
114
|
+
IMPORT_TIMEDELTA = Import.from_full_path("datetime.timedelta")
|
|
115
|
+
IMPORT_PATH = Import.from_full_path("pathlib.Path")
|
|
116
|
+
IMPORT_TIME = Import.from_full_path("datetime.time")
|
|
117
|
+
IMPORT_UUID = Import.from_full_path("uuid.UUID")
|
|
118
|
+
IMPORT_PENDULUM_DATE = Import.from_full_path("pendulum.Date")
|
|
119
|
+
IMPORT_PENDULUM_DATETIME = Import.from_full_path("pendulum.DateTime")
|
|
120
|
+
IMPORT_PENDULUM_DURATION = Import.from_full_path("pendulum.Duration")
|
|
121
|
+
IMPORT_PENDULUM_TIME = Import.from_full_path("pendulum.Time")
|