wexample-filestate-python 6.8.7__tar.gz → 7.0.0__tar.gz
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.
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/PKG-INFO +6 -6
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/README.md +3 -3
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/pyproject.toml +3 -3
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/config_option/mixin/with_stdout_wrapping_mixin.py +2 -6
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/helpers/package.py +10 -5
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/helpers/toml.py +5 -10
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/add_future_annotations_option.py +1 -1
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/add_return_types_option.py +4 -30
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/order_module_docstring_option.py +1 -3
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/order_module_metadata_option.py +1 -1
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/order_type_checking_block_option.py +2 -2
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/relocate_imports_option.py +8 -18
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python_option.py +1 -4
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_attrs_utils.py +18 -19
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_blank_lines_utils.py +5 -4
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_class_attributes_utils.py +3 -1
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_class_docstring_utils.py +2 -3
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_class_methods_utils.py +6 -13
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_constants_utils.py +8 -9
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_docstring_utils.py +1 -1
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_functions_utils.py +1 -1
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_iterable_utils.py +3 -9
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_main_guard_utils.py +1 -5
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_module_metadata_utils.py +4 -4
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_type_checking_utils.py +5 -5
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/relocate_imports/python_import_rewriter.py +10 -21
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/relocate_imports/python_localize_runtime_imports.py +3 -2
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/relocate_imports/python_parser_import_index.py +1 -1
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/relocate_imports/python_usage_collector.py +11 -16
- {wexample_filestate_python-6.8.7/tests/tests → wexample_filestate_python-7.0.0/tests/helpers}/__init__.py +0 -0
- wexample_filestate_python-7.0.0/tests/helpers/test_package.py +94 -0
- wexample_filestate_python-7.0.0/tests/helpers/test_toml.py +149 -0
- wexample_filestate_python-7.0.0/tests/wexample_tests/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/__pycache__/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/common/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/common/__pycache__/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/common/pipy_gateway.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/config_option/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/config_option/__pycache__/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/config_option/mixin/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/config_option/mixin/__pycache__/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/config_value/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/config_value/__pycache__/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/config_value/python_config_value.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/const/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/const/__pycache__/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/const/name_pattern.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/const/path.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/const/python_file.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/event/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/event/src/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/event/src/wexample_event/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/file/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/file/__pycache__/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/file/python_file.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/helpers/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/helpers/__pycache__/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/helpers/python/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/__pycache__/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/abstract_python_file_content_option.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/class_name_matches_file_name_option.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/fix_attrs_option.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/fix_blank_lines_option.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/format_option.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/fstringify_option.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/modernize_typing_option.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/order_class_attributes_option.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/order_class_docstring_option.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/order_class_methods_option.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/order_constants_option.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/order_iterable_items_option.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/order_main_guard_option.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/order_module_functions_option.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/remove_unused_option.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/sort_imports_option.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/unquote_annotations_option.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/options_provider/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/options_provider/__pycache__/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/options_provider/python_options_provider.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/py.typed +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/__pycache__/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/cst_cache.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/relocate_imports/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/relocate_imports/__pycache__/__init__.py +0 -0
- {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/relocate_imports/python_runtime_symbol_collector.py +0 -0
- {wexample_filestate_python-6.8.7/tests/wexample_tests → wexample_filestate_python-7.0.0/tests/tests}/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: wexample-filestate-python
|
|
3
|
-
Version:
|
|
3
|
+
Version: 7.0.0
|
|
4
4
|
Summary: Helpers for Python.
|
|
5
5
|
Author-Email: weeger <contact@wexample.com>
|
|
6
6
|
License: MIT
|
|
@@ -19,8 +19,8 @@ Requires-Dist: libcst
|
|
|
19
19
|
Requires-Dist: packaging
|
|
20
20
|
Requires-Dist: pyupgrade
|
|
21
21
|
Requires-Dist: tomli
|
|
22
|
-
Requires-Dist: wexample-api>=6.
|
|
23
|
-
Requires-Dist: wexample-filestate>=12.
|
|
22
|
+
Requires-Dist: wexample-api>=6.4.0
|
|
23
|
+
Requires-Dist: wexample-filestate>=12.2.0
|
|
24
24
|
Provides-Extra: dev
|
|
25
25
|
Requires-Dist: pytest; extra == "dev"
|
|
26
26
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
@@ -28,7 +28,7 @@ Description-Content-Type: text/markdown
|
|
|
28
28
|
|
|
29
29
|
# filestate_python
|
|
30
30
|
|
|
31
|
-
Version:
|
|
31
|
+
Version: 7.0.0
|
|
32
32
|
|
|
33
33
|
Helpers for Python.
|
|
34
34
|
|
|
@@ -118,8 +118,8 @@ Visit the [Wexample Suite documentation](https://docs.wexample.com) for the comp
|
|
|
118
118
|
- packaging:
|
|
119
119
|
- pyupgrade:
|
|
120
120
|
- tomli:
|
|
121
|
-
- wexample-api: >=6.
|
|
122
|
-
- wexample-filestate: >=12.
|
|
121
|
+
- wexample-api: >=6.4.0
|
|
122
|
+
- wexample-filestate: >=12.2.0
|
|
123
123
|
|
|
124
124
|
## Versioning & Compatibility Policy
|
|
125
125
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# filestate_python
|
|
2
2
|
|
|
3
|
-
Version:
|
|
3
|
+
Version: 7.0.0
|
|
4
4
|
|
|
5
5
|
Helpers for Python.
|
|
6
6
|
|
|
@@ -90,8 +90,8 @@ Visit the [Wexample Suite documentation](https://docs.wexample.com) for the comp
|
|
|
90
90
|
- packaging:
|
|
91
91
|
- pyupgrade:
|
|
92
92
|
- tomli:
|
|
93
|
-
- wexample-api: >=6.
|
|
94
|
-
- wexample-filestate: >=12.
|
|
93
|
+
- wexample-api: >=6.4.0
|
|
94
|
+
- wexample-filestate: >=12.2.0
|
|
95
95
|
|
|
96
96
|
## Versioning & Compatibility Policy
|
|
97
97
|
|
|
@@ -6,7 +6,7 @@ build-backend = "pdm.backend"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "wexample-filestate-python"
|
|
9
|
-
version = "
|
|
9
|
+
version = "7.0.0"
|
|
10
10
|
description = "Helpers for Python."
|
|
11
11
|
authors = [
|
|
12
12
|
{ name = "weeger", email = "contact@wexample.com" },
|
|
@@ -28,8 +28,8 @@ dependencies = [
|
|
|
28
28
|
"packaging",
|
|
29
29
|
"pyupgrade",
|
|
30
30
|
"tomli",
|
|
31
|
-
"wexample-api>=6.
|
|
32
|
-
"wexample-filestate>=12.
|
|
31
|
+
"wexample-api>=6.4.0",
|
|
32
|
+
"wexample-filestate>=12.2.0",
|
|
33
33
|
]
|
|
34
34
|
|
|
35
35
|
[project.readme]
|
|
@@ -35,12 +35,8 @@ class WithStdoutWrappingMixin:
|
|
|
35
35
|
stderr_content = captured_stderr.getvalue()
|
|
36
36
|
|
|
37
37
|
if stdout_content.strip():
|
|
38
|
-
sys.stdout.write(stdout_content.rstrip())
|
|
39
|
-
sys.stdout.write("\n")
|
|
40
|
-
sys.stdout.write("\n")
|
|
38
|
+
sys.stdout.write(stdout_content.rstrip() + "\n\n")
|
|
41
39
|
if stderr_content.strip():
|
|
42
|
-
sys.stderr.write(stderr_content.rstrip())
|
|
43
|
-
sys.stderr.write("\n")
|
|
44
|
-
sys.stderr.write("\n")
|
|
40
|
+
sys.stderr.write(stderr_content.rstrip() + "\n\n")
|
|
45
41
|
|
|
46
42
|
return result
|
|
@@ -20,9 +20,10 @@ def package_get_dependencies(root_dir: str | Path) -> dict[str, set[str]]:
|
|
|
20
20
|
raise ValueError(f"Error: {packages_root} does not exist or is not a directory")
|
|
21
21
|
|
|
22
22
|
dependencies = {}
|
|
23
|
+
all_entries = list(packages_root.iterdir())
|
|
23
24
|
|
|
24
25
|
# First pass: collect all local packages
|
|
25
|
-
for package_dir in
|
|
26
|
+
for package_dir in all_entries:
|
|
26
27
|
if not package_dir.is_dir():
|
|
27
28
|
continue
|
|
28
29
|
|
|
@@ -32,7 +33,7 @@ def package_get_dependencies(root_dir: str | Path) -> dict[str, set[str]]:
|
|
|
32
33
|
dependencies[name] = set()
|
|
33
34
|
|
|
34
35
|
# Second pass: analyze dependencies
|
|
35
|
-
for package_dir in
|
|
36
|
+
for package_dir in all_entries:
|
|
36
37
|
if not package_dir.is_dir():
|
|
37
38
|
continue
|
|
38
39
|
|
|
@@ -94,11 +95,15 @@ def package_parse_setup(path: Path) -> dict:
|
|
|
94
95
|
):
|
|
95
96
|
result = {}
|
|
96
97
|
for kw in node.keywords:
|
|
97
|
-
if isinstance(kw.value, ast.
|
|
98
|
-
|
|
98
|
+
if isinstance(kw.value, ast.Constant) and isinstance(
|
|
99
|
+
kw.value.value, str
|
|
100
|
+
):
|
|
101
|
+
result[kw.arg] = kw.value.value
|
|
99
102
|
elif isinstance(kw.value, ast.List):
|
|
100
103
|
result[kw.arg] = [
|
|
101
|
-
elt.
|
|
104
|
+
elt.value
|
|
105
|
+
for elt in kw.value.elts
|
|
106
|
+
if isinstance(elt, ast.Constant) and isinstance(elt.value, str)
|
|
102
107
|
]
|
|
103
108
|
return result
|
|
104
109
|
return {}
|
|
@@ -49,7 +49,7 @@ def toml_ensure_table(doc: Any, path: list[str]) -> tuple[Any, bool]:
|
|
|
49
49
|
current = doc
|
|
50
50
|
for key in path:
|
|
51
51
|
tbl = current.get(key) if isinstance(current, dict) else None
|
|
52
|
-
if
|
|
52
|
+
if tbl is None or not isinstance(tbl, dict):
|
|
53
53
|
tbl = table()
|
|
54
54
|
current[key] = tbl
|
|
55
55
|
changed = True
|
|
@@ -96,21 +96,16 @@ def toml_sort_string_array(arr: Any) -> bool:
|
|
|
96
96
|
if not items or not all(isinstance(i, String) for i in items):
|
|
97
97
|
return False
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
sorted_items = [
|
|
101
|
-
x
|
|
102
|
-
for _, x in sorted(zip([v.lower() for v in values], items), key=lambda t: t[0])
|
|
103
|
-
]
|
|
99
|
+
sorted_items = sorted(items, key=lambda i: i.value.lower())
|
|
104
100
|
|
|
105
101
|
if items == sorted_items:
|
|
106
102
|
return False
|
|
107
103
|
|
|
108
|
-
multiline_flag =
|
|
104
|
+
multiline_flag = arr._multiline
|
|
109
105
|
# Clear and re-append to preserve tomlkit item identity
|
|
110
|
-
while
|
|
106
|
+
while arr:
|
|
111
107
|
arr.pop()
|
|
112
108
|
for item in sorted_items:
|
|
113
109
|
arr.append(item)
|
|
114
|
-
|
|
115
|
-
arr.multiline(multiline_flag)
|
|
110
|
+
arr.multiline(multiline_flag)
|
|
116
111
|
return True
|
|
@@ -70,7 +70,7 @@ class AddFutureAnnotationsOption(AbstractPythonFileContentOption):
|
|
|
70
70
|
# If there isn't a blank line after the future import and next line is not blank, add one for readability
|
|
71
71
|
j = insert_index + 1
|
|
72
72
|
if j < len(lines):
|
|
73
|
-
if lines[j].strip()
|
|
73
|
+
if lines[j].strip():
|
|
74
74
|
lines.insert(j, "\n")
|
|
75
75
|
|
|
76
76
|
return "".join(lines)
|
|
@@ -58,9 +58,6 @@ class AddReturnTypesOption(AbstractPythonFileContentOption):
|
|
|
58
58
|
def visit_FunctionDef(self, node: cst.FunctionDef) -> bool: # type: ignore[override]
|
|
59
59
|
return False
|
|
60
60
|
|
|
61
|
-
def visit_AsyncFunctionDef(self, node: cst.AsyncFunctionDef) -> bool: # type: ignore[override]
|
|
62
|
-
return False
|
|
63
|
-
|
|
64
61
|
def visit_Lambda(self, node: cst.Lambda) -> bool: # type: ignore[override]
|
|
65
62
|
return False
|
|
66
63
|
|
|
@@ -145,9 +142,6 @@ class AddReturnTypesOption(AbstractPythonFileContentOption):
|
|
|
145
142
|
def visit_FunctionDef(self, node: cst.FunctionDef) -> bool: # type: ignore[override]
|
|
146
143
|
return False
|
|
147
144
|
|
|
148
|
-
def visit_AsyncFunctionDef(self, node: cst.AsyncFunctionDef) -> bool: # type: ignore[override]
|
|
149
|
-
return False
|
|
150
|
-
|
|
151
145
|
def visit_ClassDef(self, node: cst.ClassDef) -> bool: # type: ignore[override]
|
|
152
146
|
return False
|
|
153
147
|
|
|
@@ -197,26 +191,19 @@ class AddReturnTypesOption(AbstractPythonFileContentOption):
|
|
|
197
191
|
|
|
198
192
|
return None
|
|
199
193
|
|
|
200
|
-
def _infer_for_function(self, func_node: cst.
|
|
194
|
+
def _infer_for_function(self, func_node: cst.FunctionDef) -> str | None:
|
|
201
195
|
# Collect returns in the function body (non-nested)
|
|
202
196
|
collector = _ReturnCollector()
|
|
203
|
-
|
|
204
|
-
if isinstance(func_node, cst.FunctionDef):
|
|
205
|
-
func_node.body.visit(collector)
|
|
206
|
-
elif isinstance(func_node, cst.AsyncFunctionDef):
|
|
207
|
-
func_node.body.visit(collector)
|
|
208
|
-
else:
|
|
197
|
+
if not isinstance(func_node, cst.FunctionDef):
|
|
209
198
|
return None
|
|
199
|
+
func_node.body.visit(collector)
|
|
210
200
|
# If no return statements -> None
|
|
211
201
|
if not collector.returns:
|
|
212
202
|
return "None"
|
|
213
203
|
|
|
214
204
|
# Build a simple assignment map within the function
|
|
215
205
|
fac = _FunctionAssignCollector(self.known_types)
|
|
216
|
-
|
|
217
|
-
func_node.body.visit(fac)
|
|
218
|
-
elif isinstance(func_node, cst.AsyncFunctionDef):
|
|
219
|
-
func_node.body.visit(fac)
|
|
206
|
+
func_node.body.visit(fac)
|
|
220
207
|
|
|
221
208
|
kinds: set[str] = set()
|
|
222
209
|
for r in collector.returns:
|
|
@@ -245,19 +232,6 @@ class AddReturnTypesOption(AbstractPythonFileContentOption):
|
|
|
245
232
|
)
|
|
246
233
|
return updated_node
|
|
247
234
|
|
|
248
|
-
def leave_AsyncFunctionDef(
|
|
249
|
-
self,
|
|
250
|
-
original_node: cst.AsyncFunctionDef,
|
|
251
|
-
updated_node: cst.AsyncFunctionDef,
|
|
252
|
-
) -> cst.AsyncFunctionDef:
|
|
253
|
-
if updated_node.returns is None:
|
|
254
|
-
rtype = self._infer_for_function(original_node)
|
|
255
|
-
if rtype is not None:
|
|
256
|
-
return updated_node.with_changes(
|
|
257
|
-
returns=cst.Annotation(annotation=cst.Name(rtype))
|
|
258
|
-
)
|
|
259
|
-
return updated_node
|
|
260
|
-
|
|
261
235
|
# Collect known simple type names from the module
|
|
262
236
|
ktc = _KnownTypesCollector()
|
|
263
237
|
module.visit(ktc)
|
|
@@ -43,9 +43,7 @@ class OrderModuleDocstringOption(AbstractPythonFileContentOption):
|
|
|
43
43
|
|
|
44
44
|
if is_module_docstring_at_top(module):
|
|
45
45
|
# Check if quotes need normalization
|
|
46
|
-
if
|
|
47
|
-
docstring_node.body[0], cst.Expr
|
|
48
|
-
):
|
|
46
|
+
if docstring_node.body and isinstance(docstring_node.body[0], cst.Expr):
|
|
49
47
|
expr = docstring_node.body[0]
|
|
50
48
|
if isinstance(expr.value, cst.SimpleString):
|
|
51
49
|
quote = expr.value.quote
|
|
@@ -48,7 +48,7 @@ class OrderModuleMetadataOption(AbstractPythonFileContentOption):
|
|
|
48
48
|
)
|
|
49
49
|
# names sorted?
|
|
50
50
|
names = [name for _, __, name in found]
|
|
51
|
-
names_sorted = sorted(names, key=
|
|
51
|
+
names_sorted = sorted(names, key=str.lower)
|
|
52
52
|
already_sorted = names == names_sorted
|
|
53
53
|
# at correct position?
|
|
54
54
|
desired_index = target_index_for_module_metadata(module)
|
|
@@ -41,8 +41,8 @@ class OrderTypeCheckingBlockOption(AbstractPythonFileContentOption):
|
|
|
41
41
|
current_indices = [i for i, _ in blocks]
|
|
42
42
|
desired_index = target_index_for_type_checking(module)
|
|
43
43
|
# Already correct if first block starts at desired index and blocks are contiguous
|
|
44
|
-
contiguous =
|
|
45
|
-
|
|
44
|
+
contiguous = all(
|
|
45
|
+
b - a == 1 for a, b in zip(current_indices, current_indices[1:])
|
|
46
46
|
)
|
|
47
47
|
if contiguous and current_indices[0] == desired_index:
|
|
48
48
|
return src
|
|
@@ -88,11 +88,7 @@ class RelocateImportsOption(AbstractPythonFileContentOption):
|
|
|
88
88
|
runtime_used_anywhere: set[str] = rsc.runtime_used_anywhere
|
|
89
89
|
|
|
90
90
|
# Resolve categories
|
|
91
|
-
runtime_local_all: set[str] = (
|
|
92
|
-
set().union(*functions_needing_local.values())
|
|
93
|
-
if functions_needing_local
|
|
94
|
-
else set()
|
|
95
|
-
)
|
|
91
|
+
runtime_local_all: set[str] = set().union(*functions_needing_local.values())
|
|
96
92
|
# class_level has priority over runtime_local: if a name is class-level, we do NOT local-import it
|
|
97
93
|
runtime_local_final: set[str] = {
|
|
98
94
|
n for n in runtime_local_all if n not in class_level_names
|
|
@@ -114,9 +110,7 @@ class RelocateImportsOption(AbstractPythonFileContentOption):
|
|
|
114
110
|
# Then subtract names that are already locally imported inside some function where
|
|
115
111
|
# they are used (to avoid redundant TYPE_CHECKING imports when a function-scoped
|
|
116
112
|
# import suffices, e.g., `from multiprocessing import Queue` inside a method).
|
|
117
|
-
annotation_names_candidate: set[str] =
|
|
118
|
-
n for n in type_annotation_names if n not in class_level_names
|
|
119
|
-
}
|
|
113
|
+
annotation_names_candidate: set[str] = type_annotation_names - class_level_names
|
|
120
114
|
|
|
121
115
|
# Collect names imported locally inside functions anywhere in the module
|
|
122
116
|
class _LocalImportNameCollector(cst.CSTVisitor):
|
|
@@ -157,19 +151,15 @@ class RelocateImportsOption(AbstractPythonFileContentOption):
|
|
|
157
151
|
# - remove module-level import unless also class_level or runtime_used_anywhere
|
|
158
152
|
# Exclude names used at runtime at module level (e.g., TerminalColor.RED in dict values)
|
|
159
153
|
names_to_remove_from_module = (
|
|
160
|
-
(
|
|
161
|
-
|
|
|
162
|
-
| (
|
|
163
|
-
set(cast_type_names_anywhere)
|
|
164
|
-
- set(class_level_names)
|
|
165
|
-
- runtime_used_anywhere
|
|
166
|
-
)
|
|
154
|
+
(runtime_local_final - runtime_used_anywhere)
|
|
155
|
+
| type_only_names
|
|
156
|
+
| (cast_type_names_anywhere - class_level_names - runtime_used_anywhere)
|
|
167
157
|
)
|
|
168
158
|
|
|
169
159
|
# Do not add to TYPE_CHECKING if the name's module-level import is kept
|
|
170
|
-
kept_module_imports: set[str] =
|
|
171
|
-
|
|
172
|
-
|
|
160
|
+
kept_module_imports: set[str] = (
|
|
161
|
+
imported_value_names - names_to_remove_from_module
|
|
162
|
+
)
|
|
173
163
|
used_in_C_only_final: set[str] = set(type_only_for_block) - kept_module_imports
|
|
174
164
|
|
|
175
165
|
# Debug summary removed
|
|
@@ -130,9 +130,6 @@ class PythonOption(OptionMixin, AbstractNestedConfigOption):
|
|
|
130
130
|
def set_value(self, raw_value: Any) -> None:
|
|
131
131
|
# Convert list form to dict form for consistency
|
|
132
132
|
if isinstance(raw_value, list):
|
|
133
|
-
|
|
134
|
-
for option_name in raw_value:
|
|
135
|
-
dict_value[option_name] = True
|
|
136
|
-
raw_value = dict_value
|
|
133
|
+
raw_value = {option_name: True for option_name in raw_value}
|
|
137
134
|
|
|
138
135
|
super().set_value(raw_value=raw_value)
|
|
@@ -39,19 +39,19 @@ def fix_attrs_kw_only(module: cst.Module) -> cst.Module:
|
|
|
39
39
|
return updated_node.with_changes(decorator=new_call)
|
|
40
40
|
else:
|
|
41
41
|
# Check if kw_only is set to False and change it to True
|
|
42
|
-
new_args = [
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
42
|
+
new_args = [
|
|
43
|
+
(
|
|
44
|
+
arg.with_changes(value=cst.Name("True"))
|
|
45
|
+
if (
|
|
46
|
+
isinstance(arg.keyword, cst.Name)
|
|
47
|
+
and arg.keyword.value == "kw_only"
|
|
48
|
+
and isinstance(arg.value, cst.Name)
|
|
49
|
+
and arg.value.value == "False"
|
|
50
|
+
)
|
|
51
|
+
else arg
|
|
52
|
+
)
|
|
53
|
+
for arg in call.args
|
|
54
|
+
]
|
|
55
55
|
|
|
56
56
|
if new_args != list(call.args):
|
|
57
57
|
new_call = call.with_changes(args=new_args)
|
|
@@ -76,10 +76,10 @@ def fix_attrs_kw_only(module: cst.Module) -> cst.Module:
|
|
|
76
76
|
|
|
77
77
|
def _has_kw_only_arg(call: cst.Call) -> bool:
|
|
78
78
|
"""Check if the call already has a kw_only argument."""
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
return any(
|
|
80
|
+
isinstance(arg.keyword, cst.Name) and arg.keyword.value == "kw_only"
|
|
81
|
+
for arg in call.args
|
|
82
|
+
)
|
|
83
83
|
|
|
84
84
|
|
|
85
85
|
def _is_attrs_decorator(decorator: cst.Decorator) -> bool:
|
|
@@ -99,8 +99,7 @@ def _is_attrs_decorator(decorator: cst.Decorator) -> bool:
|
|
|
99
99
|
):
|
|
100
100
|
return True
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
if isinstance(base_decorator, cst.Attribute):
|
|
102
|
+
# Check for @attr.s
|
|
104
103
|
if (
|
|
105
104
|
isinstance(base_decorator.value, cst.Name)
|
|
106
105
|
and base_decorator.value.value == "attr"
|
|
@@ -142,10 +142,11 @@ def _fix_module_docstring_spacing(module: cst.Module) -> cst.Module:
|
|
|
142
142
|
# Count existing blank lines after docstring
|
|
143
143
|
blank_count = 0
|
|
144
144
|
if isinstance(next_stmt, cst.SimpleStatementLine):
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
blank_count = sum(
|
|
146
|
+
1
|
|
147
|
+
for line in next_stmt.leading_lines
|
|
148
|
+
if isinstance(line, cst.EmptyLine) and line.comment is None
|
|
149
|
+
)
|
|
149
150
|
|
|
150
151
|
# Ensure exactly 1 blank line
|
|
151
152
|
if blank_count != 1:
|
|
@@ -84,7 +84,9 @@ def reorder_attribute_block(
|
|
|
84
84
|
|
|
85
85
|
def cat(node: cst.CSTNode) -> tuple:
|
|
86
86
|
name = _attr_name(node) or ""
|
|
87
|
-
if
|
|
87
|
+
if name in SPECIAL_ATTR_NAMES or (
|
|
88
|
+
isinstance(node, cst.ClassDef) and name in SPECIAL_INNER_CLASS_NAMES
|
|
89
|
+
):
|
|
88
90
|
# Category 0: special always first
|
|
89
91
|
return (0, _sort_key(name), False)
|
|
90
92
|
if dataclass_mode:
|
|
@@ -28,8 +28,7 @@ def find_class_docstring_nodes(
|
|
|
28
28
|
According to Python conventions, a class docstring is a first statement that's a
|
|
29
29
|
string literal expression. We detect any top-level string literal in the class body.
|
|
30
30
|
"""
|
|
31
|
-
|
|
32
|
-
for i, elem in enumerate(body_elems):
|
|
31
|
+
for i, elem in enumerate(classdef.body.body):
|
|
33
32
|
if isinstance(elem, cst.SimpleStatementLine) and len(elem.body) == 1:
|
|
34
33
|
small = elem.body[0]
|
|
35
34
|
if isinstance(small, cst.Expr) and isinstance(
|
|
@@ -76,7 +75,7 @@ def normalize_docstring_quotes_stmt(
|
|
|
76
75
|
if not (isinstance(small, cst.Expr) and isinstance(small.value, cst.SimpleString)):
|
|
77
76
|
return stmt
|
|
78
77
|
q = small.value.quote
|
|
79
|
-
if q.startswith('"
|
|
78
|
+
if q.startswith('"'):
|
|
80
79
|
return stmt
|
|
81
80
|
# Convert starting quote to double
|
|
82
81
|
new_quote = '"""' if q.startswith("'''") else '"'
|
|
@@ -22,6 +22,8 @@ for gi, group in enumerate(DUnderGroups):
|
|
|
22
22
|
for si, name in enumerate(group):
|
|
23
23
|
_DUNDER_ORDER[name] = (gi, si)
|
|
24
24
|
|
|
25
|
+
_PROP_KINDS: tuple[str, str, str] = ("getter", "setter", "deleter")
|
|
26
|
+
|
|
25
27
|
|
|
26
28
|
def ensure_order_class_methods_in_module(module: cst.Module) -> cst.Module:
|
|
27
29
|
changed = False
|
|
@@ -86,7 +88,7 @@ def reorder_class_methods(classdef: cst.ClassDef) -> cst.ClassDef:
|
|
|
86
88
|
# Order dunders by (group, within) then keep unknown dunders after known, alpha by name
|
|
87
89
|
known = [m for m in dunders if m["order"][0] != 999]
|
|
88
90
|
unknown = [m for m in dunders if m["order"][0] == 999]
|
|
89
|
-
known_sorted = sorted(known, key=lambda m:
|
|
91
|
+
known_sorted = sorted(known, key=lambda m: m["order"])
|
|
90
92
|
unknown_sorted = sorted(unknown, key=lambda m: _sort_key_alpha(m["name"]))
|
|
91
93
|
dunder_ordered = [m["node"] for m in known_sorted + unknown_sorted]
|
|
92
94
|
|
|
@@ -118,17 +120,10 @@ def reorder_class_methods(classdef: cst.ClassDef) -> cst.ClassDef:
|
|
|
118
120
|
g[kind] = node
|
|
119
121
|
|
|
120
122
|
def prop_group_to_nodes(base: str, g: dict[str, cst.FunctionDef | None]):
|
|
121
|
-
|
|
122
|
-
if g.get("getter") is not None:
|
|
123
|
-
order.append(g["getter"]) # type: ignore
|
|
124
|
-
if g.get("setter") is not None:
|
|
125
|
-
order.append(g["setter"]) # type: ignore
|
|
126
|
-
if g.get("deleter") is not None:
|
|
127
|
-
order.append(g["deleter"]) # type: ignore
|
|
128
|
-
return order
|
|
123
|
+
return [g[k] for k in _PROP_KINDS if g[k] is not None] # type: ignore
|
|
129
124
|
|
|
130
125
|
props_ordered: list[cst.FunctionDef] = []
|
|
131
|
-
for base in sorted(prop_groups.keys(), key=
|
|
126
|
+
for base in sorted(prop_groups.keys(), key=str.lower):
|
|
132
127
|
props_ordered.extend(prop_group_to_nodes(base, prop_groups[base]))
|
|
133
128
|
|
|
134
129
|
# Final ordered list: dunder -> classmethods -> staticmethods -> properties -> instances
|
|
@@ -219,9 +214,7 @@ def _property_kind(func: cst.FunctionDef) -> tuple[str | None, str | None]:
|
|
|
219
214
|
# base name is left side of the attribute if it's a Name
|
|
220
215
|
base = expr.value
|
|
221
216
|
if isinstance(base, cst.Name):
|
|
222
|
-
return base.value,
|
|
223
|
-
"setter" if expr.attr.value == "setter" else "deleter"
|
|
224
|
-
)
|
|
217
|
+
return base.value, expr.attr.value
|
|
225
218
|
return None, None
|
|
226
219
|
|
|
227
220
|
|
|
@@ -27,11 +27,12 @@ def find_flagged_constant_blocks(
|
|
|
27
27
|
|
|
28
28
|
i = 0
|
|
29
29
|
body = module.body
|
|
30
|
+
body_list = list(body)
|
|
30
31
|
n = len(body)
|
|
31
32
|
while i < n:
|
|
32
33
|
stmt = body[i]
|
|
33
34
|
if isinstance(stmt, cst.SimpleStatementLine):
|
|
34
|
-
has_flag = _stmt_has_flag(stmt, src) or _prev_line_has_flag(
|
|
35
|
+
has_flag = _stmt_has_flag(stmt, src) or _prev_line_has_flag(body_list, i)
|
|
35
36
|
if has_flag and _get_simple_assignment_name(stmt) is not None:
|
|
36
37
|
# Start a block at i
|
|
37
38
|
j = i
|
|
@@ -187,17 +188,15 @@ def sort_constants_block(
|
|
|
187
188
|
collected_flag_lines = fl
|
|
188
189
|
break
|
|
189
190
|
|
|
190
|
-
pairs
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if name is None
|
|
194
|
-
|
|
195
|
-
continue
|
|
196
|
-
pairs.append((name, node))
|
|
191
|
+
pairs = [
|
|
192
|
+
(name, node)
|
|
193
|
+
for node in nodes
|
|
194
|
+
if (name := _get_simple_assignment_name(node)) is not None
|
|
195
|
+
]
|
|
197
196
|
|
|
198
197
|
# If already sorted, return original (no changes)
|
|
199
198
|
sorted_pairs = sorted(pairs, key=lambda p: p[0].lower())
|
|
200
|
-
if
|
|
199
|
+
if all(a is b for (_, a), (_, b) in zip(sorted_pairs, pairs)):
|
|
201
200
|
return nodes
|
|
202
201
|
|
|
203
202
|
# Build new nodes preserving each node's original leading_lines, but move the
|
|
@@ -112,6 +112,6 @@ def normalize_docstring_quotes(
|
|
|
112
112
|
# Create new string with double quotes
|
|
113
113
|
new_string = string_value.with_changes(quote=new_quote)
|
|
114
114
|
new_expr = expr.with_changes(value=new_string)
|
|
115
|
-
new_body = [new_expr
|
|
115
|
+
new_body = [new_expr, *docstring_node.body[1:]]
|
|
116
116
|
|
|
117
117
|
return docstring_node.with_changes(body=new_body)
|
|
@@ -79,7 +79,7 @@ def reorder_module_functions(module: cst.Module) -> cst.Module:
|
|
|
79
79
|
remove_indices = []
|
|
80
80
|
for idx, g in groups_with_idx:
|
|
81
81
|
remove_indices.extend(range(idx, idx + len(g.nodes)))
|
|
82
|
-
remove_indices =
|
|
82
|
+
remove_indices = set(remove_indices)
|
|
83
83
|
|
|
84
84
|
new_body: list[cst.CSTNode] = []
|
|
85
85
|
for idx, node in enumerate(module.body):
|
|
@@ -71,9 +71,7 @@ def reorder_flagged_iterables(src: str) -> str:
|
|
|
71
71
|
continue
|
|
72
72
|
|
|
73
73
|
# Flatten groups back to lines
|
|
74
|
-
new_block
|
|
75
|
-
for g in sorted_groups:
|
|
76
|
-
new_block.extend(g)
|
|
74
|
+
new_block = [ln for g in sorted_groups for ln in g]
|
|
77
75
|
|
|
78
76
|
lines[start:end] = new_block
|
|
79
77
|
changed = True
|
|
@@ -111,7 +109,7 @@ def _collect_iterable_block(lines: list[str], flag_idx: int) -> tuple[int, int]:
|
|
|
111
109
|
if stripped.startswith("]") and curr_indent <= base_indent:
|
|
112
110
|
break
|
|
113
111
|
# Stop if we encounter a trailing comment-only line
|
|
114
|
-
if
|
|
112
|
+
if stripped.startswith("#"):
|
|
115
113
|
break
|
|
116
114
|
i += 1
|
|
117
115
|
|
|
@@ -124,8 +122,4 @@ def _find_flag_line_indices(src: str) -> list[int]:
|
|
|
124
122
|
from wexample_filestate.helpers.flag import flag_exists
|
|
125
123
|
|
|
126
124
|
lines = src.splitlines()
|
|
127
|
-
|
|
128
|
-
for i, line in enumerate(lines):
|
|
129
|
-
if flag_exists(FLAG_NAME, line):
|
|
130
|
-
indices.append(i)
|
|
131
|
-
return indices
|
|
125
|
+
return [i for i, line in enumerate(lines) if flag_exists(FLAG_NAME, line)]
|
|
@@ -5,11 +5,7 @@ import libcst as cst
|
|
|
5
5
|
|
|
6
6
|
def find_main_guard_blocks(module: cst.Module) -> list[tuple[int, cst.If]]:
|
|
7
7
|
"""Return list of (index, IfNode) for all top-level __main__ guard blocks."""
|
|
8
|
-
|
|
9
|
-
for i, stmt in enumerate(module.body):
|
|
10
|
-
if is_main_guard_if(stmt):
|
|
11
|
-
res.append((i, stmt))
|
|
12
|
-
return res
|
|
8
|
+
return [(i, stmt) for i, stmt in enumerate(module.body) if is_main_guard_if(stmt)]
|
|
13
9
|
|
|
14
10
|
|
|
15
11
|
def is_main_guard_at_end(module: cst.Module) -> bool:
|
|
@@ -48,7 +48,7 @@ def group_and_sort_module_metadata(module: cst.Module) -> cst.Module:
|
|
|
48
48
|
insert_at = target_index_for_module_metadata(module)
|
|
49
49
|
|
|
50
50
|
# Remove from body (reverse order) and collect nodes with cleaned leading lines
|
|
51
|
-
to_remove_indices = sorted(
|
|
51
|
+
to_remove_indices = sorted((i for i, _, _ in found), reverse=True)
|
|
52
52
|
new_body: list[cst.CSTNode] = list(module.body)
|
|
53
53
|
moved: list[tuple[str, cst.SimpleStatementLine]] = []
|
|
54
54
|
|
|
@@ -92,9 +92,9 @@ def target_index_for_module_metadata(module: cst.Module) -> int:
|
|
|
92
92
|
find_type_checking_blocks,
|
|
93
93
|
)
|
|
94
94
|
|
|
95
|
-
last_type_checking =
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
last_type_checking = max(
|
|
96
|
+
(idx for idx, _ in find_type_checking_blocks(module)), default=-1
|
|
97
|
+
)
|
|
98
98
|
|
|
99
99
|
last_regular_import = -1
|
|
100
100
|
last_future_import = -1
|
|
@@ -5,11 +5,11 @@ import libcst as cst
|
|
|
5
5
|
|
|
6
6
|
def find_type_checking_blocks(module: cst.Module) -> list[tuple[int, cst.If]]:
|
|
7
7
|
"""Return list of (index, IfNode) for all top-level `if TYPE_CHECKING:` blocks."""
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
return [
|
|
9
|
+
(i, stmt)
|
|
10
|
+
for i, stmt in enumerate(module.body)
|
|
11
|
+
if isinstance(stmt, cst.If) and _is_type_checking_test(stmt.test)
|
|
12
|
+
]
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def move_type_checking_blocks_after_imports(module: cst.Module) -> cst.Module:
|