wexample-filestate-python 0.0.48__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.
- wexample_filestate_python/__init__.py +0 -0
- wexample_filestate_python/__pycache__/__init__.py +0 -0
- wexample_filestate_python/common/__init__.py +0 -0
- wexample_filestate_python/common/__pycache__/__init__.py +0 -0
- wexample_filestate_python/common/pipy_gateway.py +20 -0
- wexample_filestate_python/config_option/__init__.py +0 -0
- wexample_filestate_python/config_option/__pycache__/__init__.py +0 -0
- wexample_filestate_python/config_option/mixin/__init__.py +0 -0
- wexample_filestate_python/config_option/mixin/__pycache__/__init__.py +0 -0
- wexample_filestate_python/config_option/mixin/with_stdout_wrapping_mixin.py +46 -0
- wexample_filestate_python/config_value/__init__.py +0 -0
- wexample_filestate_python/config_value/__pycache__/__init__.py +0 -0
- wexample_filestate_python/config_value/python_config_value.py +195 -0
- wexample_filestate_python/const/__init__.py +0 -0
- wexample_filestate_python/const/__pycache__/__init__.py +0 -0
- wexample_filestate_python/const/name_pattern.py +4 -0
- wexample_filestate_python/const/python_file.py +5 -0
- wexample_filestate_python/file/__init__.py +0 -0
- wexample_filestate_python/file/__pycache__/__init__.py +0 -0
- wexample_filestate_python/file/python_file.py +12 -0
- wexample_filestate_python/helpers/__init__.py +0 -0
- wexample_filestate_python/helpers/__pycache__/__init__.py +0 -0
- wexample_filestate_python/helpers/package.py +122 -0
- wexample_filestate_python/helpers/toml.py +116 -0
- wexample_filestate_python/option/__init__.py +0 -0
- wexample_filestate_python/option/__pycache__/__init__.py +0 -0
- wexample_filestate_python/option/abstract_python_file_content_option.py +45 -0
- wexample_filestate_python/option/add_future_annotations_option.py +79 -0
- wexample_filestate_python/option/add_return_types_option.py +265 -0
- wexample_filestate_python/option/fix_attrs_option.py +37 -0
- wexample_filestate_python/option/fix_blank_lines_option.py +47 -0
- wexample_filestate_python/option/format_option.py +34 -0
- wexample_filestate_python/option/fstringify_option.py +34 -0
- wexample_filestate_python/option/modernize_typing_option.py +25 -0
- wexample_filestate_python/option/order_class_attributes_option.py +34 -0
- wexample_filestate_python/option/order_class_docstring_option.py +36 -0
- wexample_filestate_python/option/order_class_methods_option.py +37 -0
- wexample_filestate_python/option/order_constants_option.py +35 -0
- wexample_filestate_python/option/order_iterable_items_option.py +31 -0
- wexample_filestate_python/option/order_main_guard_option.py +44 -0
- wexample_filestate_python/option/order_module_docstring_option.py +73 -0
- wexample_filestate_python/option/order_module_functions_option.py +42 -0
- wexample_filestate_python/option/order_module_metadata_option.py +62 -0
- wexample_filestate_python/option/order_type_checking_block_option.py +51 -0
- wexample_filestate_python/option/python_option.py +164 -0
- wexample_filestate_python/option/relocate_imports_option.py +189 -0
- wexample_filestate_python/option/remove_unused_option.py +45 -0
- wexample_filestate_python/option/sort_imports_option.py +26 -0
- wexample_filestate_python/option/unquote_annotations_option.py +85 -0
- wexample_filestate_python/options_provider/__init__.py +0 -0
- wexample_filestate_python/options_provider/__pycache__/__init__.py +0 -0
- wexample_filestate_python/options_provider/python_options_provider.py +24 -0
- wexample_filestate_python/py.typed +0 -0
- wexample_filestate_python/utils/__init__.py +0 -0
- wexample_filestate_python/utils/__pycache__/__init__.py +0 -0
- wexample_filestate_python/utils/python_attrs_utils.py +112 -0
- wexample_filestate_python/utils/python_blank_lines_utils.py +568 -0
- wexample_filestate_python/utils/python_class_attributes_utils.py +275 -0
- wexample_filestate_python/utils/python_class_docstring_utils.py +85 -0
- wexample_filestate_python/utils/python_class_methods_utils.py +230 -0
- wexample_filestate_python/utils/python_constants_utils.py +302 -0
- wexample_filestate_python/utils/python_docstring_utils.py +117 -0
- wexample_filestate_python/utils/python_functions_utils.py +212 -0
- wexample_filestate_python/utils/python_iterable_utils.py +131 -0
- wexample_filestate_python/utils/python_main_guard_utils.py +80 -0
- wexample_filestate_python/utils/python_module_metadata_utils.py +147 -0
- wexample_filestate_python/utils/python_type_checking_utils.py +113 -0
- wexample_filestate_python/utils/relocate_imports/__init__.py +7 -0
- wexample_filestate_python/utils/relocate_imports/__pycache__/__init__.py +0 -0
- wexample_filestate_python/utils/relocate_imports/python_import_rewriter.py +413 -0
- wexample_filestate_python/utils/relocate_imports/python_localize_runtime_imports.py +324 -0
- wexample_filestate_python/utils/relocate_imports/python_parser_import_index.py +80 -0
- wexample_filestate_python/utils/relocate_imports/python_runtime_symbol_collector.py +33 -0
- wexample_filestate_python/utils/relocate_imports/python_usage_collector.py +410 -0
- wexample_filestate_python/workdir/__init__.py +0 -0
- wexample_filestate_python/workdir/__pycache__/__init__.py +0 -0
- wexample_filestate_python-0.0.48.dist-info/METADATA +191 -0
- wexample_filestate_python-0.0.48.dist-info/RECORD +80 -0
- wexample_filestate_python-0.0.48.dist-info/WHEEL +4 -0
- wexample_filestate_python-0.0.48.dist-info/entry_points.txt +4 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from wexample_helpers.decorator.base_class import base_class
|
|
6
|
+
|
|
7
|
+
from .abstract_python_file_content_option import AbstractPythonFileContentOption
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from wexample_filestate.const.types_state_items import TargetFileOrDirectoryType
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@base_class
|
|
14
|
+
class RemoveUnusedOption(AbstractPythonFileContentOption):
|
|
15
|
+
def get_description(self) -> str:
|
|
16
|
+
return "Remove unused imports from the Python file using autoflake."
|
|
17
|
+
|
|
18
|
+
def _apply_content_change(self, target: TargetFileOrDirectoryType) -> str:
|
|
19
|
+
"""Remove unused Python imports using autoflake."""
|
|
20
|
+
from wexample_helpers.helpers.shell import shell_run
|
|
21
|
+
from wexample_helpers.helpers.system import system_get_venv_bin_path
|
|
22
|
+
|
|
23
|
+
result = shell_run(
|
|
24
|
+
cmd=[
|
|
25
|
+
f"{system_get_venv_bin_path()}/autoflake",
|
|
26
|
+
"--stdout",
|
|
27
|
+
"--remove-all-unused-imports",
|
|
28
|
+
"--remove-unused-variables",
|
|
29
|
+
"--expand-star-imports",
|
|
30
|
+
"--remove-duplicate-keys",
|
|
31
|
+
target.get_path(),
|
|
32
|
+
],
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
if result.returncode != 0:
|
|
36
|
+
# Double line return is important to keep message visible event last line is erased by parent process.
|
|
37
|
+
target.io.error(f"Autoflake error: {result.stderr}\n\n")
|
|
38
|
+
return target.get_local_file().read() # Return original content on error
|
|
39
|
+
|
|
40
|
+
modified_content = result.stdout
|
|
41
|
+
|
|
42
|
+
if not modified_content.strip():
|
|
43
|
+
return target.get_local_file().read() # Return original content if empty
|
|
44
|
+
|
|
45
|
+
return modified_content
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from wexample_helpers.decorator.base_class import base_class
|
|
6
|
+
|
|
7
|
+
from .abstract_python_file_content_option import AbstractPythonFileContentOption
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from wexample_filestate.const.types_state_items import TargetFileOrDirectoryType
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@base_class
|
|
14
|
+
class SortImportsOption(AbstractPythonFileContentOption):
|
|
15
|
+
def get_description(self) -> str:
|
|
16
|
+
return "Sort and group Python imports using isort."
|
|
17
|
+
|
|
18
|
+
def _apply_content_change(self, target: TargetFileOrDirectoryType) -> str:
|
|
19
|
+
"""Sort Python imports using isort."""
|
|
20
|
+
from isort import code
|
|
21
|
+
from isort.settings import Config
|
|
22
|
+
|
|
23
|
+
src = target.get_local_file().read()
|
|
24
|
+
config = Config(profile="black")
|
|
25
|
+
formatted = code(src, config=config)
|
|
26
|
+
return formatted
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from wexample_helpers.decorator.base_class import base_class
|
|
6
|
+
|
|
7
|
+
from .abstract_python_file_content_option import AbstractPythonFileContentOption
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from wexample_filestate.const.types_state_items import TargetFileOrDirectoryType
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@base_class
|
|
14
|
+
class UnquoteAnnotationsOption(AbstractPythonFileContentOption):
|
|
15
|
+
def get_description(self) -> str:
|
|
16
|
+
return "Unquote type annotations (arguments, returns, variables) using LibCST."
|
|
17
|
+
|
|
18
|
+
def _apply_content_change(self, target: TargetFileOrDirectoryType) -> str:
|
|
19
|
+
"""Remove quotes around type annotations by turning stringized annotations back into expressions."""
|
|
20
|
+
import json
|
|
21
|
+
|
|
22
|
+
import libcst as cst
|
|
23
|
+
|
|
24
|
+
src = target.get_local_file().read()
|
|
25
|
+
|
|
26
|
+
class _Unquoter(cst.CSTTransformer):
|
|
27
|
+
@staticmethod
|
|
28
|
+
def _unquote_expr(s: cst.SimpleString) -> cst.BaseExpression | None:
|
|
29
|
+
try:
|
|
30
|
+
code = json.loads(s.value)
|
|
31
|
+
except Exception:
|
|
32
|
+
return None
|
|
33
|
+
try:
|
|
34
|
+
return cst.parse_expression(code)
|
|
35
|
+
except Exception:
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def _process_annotation(
|
|
40
|
+
ann: cst.Annotation | None,
|
|
41
|
+
) -> cst.Annotation | None:
|
|
42
|
+
if ann is None:
|
|
43
|
+
return None
|
|
44
|
+
node = ann.annotation
|
|
45
|
+
if isinstance(node, cst.SimpleString):
|
|
46
|
+
expr = _Unquoter._unquote_expr(node)
|
|
47
|
+
if expr is not None:
|
|
48
|
+
return cst.Annotation(annotation=expr)
|
|
49
|
+
return ann
|
|
50
|
+
|
|
51
|
+
def leave_Param(
|
|
52
|
+
self, original_node: cst.Param, updated_node: cst.Param
|
|
53
|
+
) -> cst.Param:
|
|
54
|
+
return updated_node.with_changes(
|
|
55
|
+
annotation=self._process_annotation(updated_node.annotation)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def leave_FunctionDef(
|
|
59
|
+
self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef
|
|
60
|
+
) -> cst.FunctionDef:
|
|
61
|
+
return updated_node.with_changes(
|
|
62
|
+
returns=self._process_annotation(updated_node.returns)
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
def leave_AnnAssign(
|
|
66
|
+
self, original_node: cst.AnnAssign, updated_node: cst.AnnAssign
|
|
67
|
+
) -> cst.AnnAssign:
|
|
68
|
+
return updated_node.with_changes(
|
|
69
|
+
annotation=self._process_annotation(updated_node.annotation)
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def leave_TypeAlias(
|
|
73
|
+
self, original_node: cst.TypeAlias, updated_node: cst.TypeAlias
|
|
74
|
+
) -> cst.TypeAlias:
|
|
75
|
+
# Python 3.12 'type X = ...' syntax
|
|
76
|
+
ann = updated_node.annotation
|
|
77
|
+
if isinstance(ann, cst.SimpleString):
|
|
78
|
+
expr = self._unquote_expr(ann)
|
|
79
|
+
if expr is not None:
|
|
80
|
+
return updated_node.with_changes(annotation=expr)
|
|
81
|
+
return updated_node
|
|
82
|
+
|
|
83
|
+
module = cst.parse_module(src)
|
|
84
|
+
new_mod = module.visit(_Unquoter())
|
|
85
|
+
return new_mod.code
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from wexample_config.options_provider.abstract_options_provider import (
|
|
6
|
+
AbstractOptionsProvider,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from wexample_config.config_option.abstract_config_option import (
|
|
11
|
+
AbstractConfigOption,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PythonOptionsProvider(AbstractOptionsProvider):
|
|
16
|
+
@classmethod
|
|
17
|
+
def get_options(cls) -> list[type[AbstractConfigOption]]:
|
|
18
|
+
from wexample_filestate_python.option.python_option import (
|
|
19
|
+
PythonOption,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
return [
|
|
23
|
+
PythonOption,
|
|
24
|
+
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import libcst as cst
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def fix_attrs_kw_only(module: cst.Module) -> cst.Module:
|
|
7
|
+
"""Ensure attrs decorators always use kw_only=True.
|
|
8
|
+
|
|
9
|
+
This applies to:
|
|
10
|
+
- @attrs.define
|
|
11
|
+
- @attr.s
|
|
12
|
+
- @attrs.frozen
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
class AttrsKwOnlyFixer(cst.CSTTransformer):
|
|
16
|
+
def leave_Decorator(
|
|
17
|
+
self, original_node: cst.Decorator, updated_node: cst.Decorator
|
|
18
|
+
) -> cst.Decorator:
|
|
19
|
+
# Check if this is an attrs decorator
|
|
20
|
+
if not _is_attrs_decorator(updated_node):
|
|
21
|
+
return updated_node
|
|
22
|
+
|
|
23
|
+
# Get the decorator call
|
|
24
|
+
if isinstance(updated_node.decorator, cst.Call):
|
|
25
|
+
call = updated_node.decorator
|
|
26
|
+
|
|
27
|
+
# Check if kw_only is already present
|
|
28
|
+
has_kw_only = _has_kw_only_arg(call)
|
|
29
|
+
|
|
30
|
+
if not has_kw_only:
|
|
31
|
+
# Add kw_only=True to the call
|
|
32
|
+
new_args = list(call.args)
|
|
33
|
+
kw_only_arg = cst.Arg(
|
|
34
|
+
keyword=cst.Name("kw_only"), value=cst.Name("True")
|
|
35
|
+
)
|
|
36
|
+
new_args.append(kw_only_arg)
|
|
37
|
+
|
|
38
|
+
new_call = call.with_changes(args=new_args)
|
|
39
|
+
return updated_node.with_changes(decorator=new_call)
|
|
40
|
+
else:
|
|
41
|
+
# Check if kw_only is set to False and change it to True
|
|
42
|
+
new_args = []
|
|
43
|
+
for arg in call.args:
|
|
44
|
+
if (
|
|
45
|
+
isinstance(arg.keyword, cst.Name)
|
|
46
|
+
and arg.keyword.value == "kw_only"
|
|
47
|
+
and isinstance(arg.value, cst.Name)
|
|
48
|
+
and arg.value.value == "False"
|
|
49
|
+
):
|
|
50
|
+
# Change False to True
|
|
51
|
+
new_arg = arg.with_changes(value=cst.Name("True"))
|
|
52
|
+
new_args.append(new_arg)
|
|
53
|
+
else:
|
|
54
|
+
new_args.append(arg)
|
|
55
|
+
|
|
56
|
+
if new_args != list(call.args):
|
|
57
|
+
new_call = call.with_changes(args=new_args)
|
|
58
|
+
return updated_node.with_changes(decorator=new_call)
|
|
59
|
+
|
|
60
|
+
elif isinstance(updated_node.decorator, (cst.Name, cst.Attribute)):
|
|
61
|
+
# Decorator without parentheses, add them with kw_only=True
|
|
62
|
+
kw_only_arg = cst.Arg(
|
|
63
|
+
keyword=cst.Name("kw_only"), value=cst.Name("True")
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
new_call = cst.Call(func=updated_node.decorator, args=[kw_only_arg])
|
|
67
|
+
return updated_node.with_changes(decorator=new_call)
|
|
68
|
+
|
|
69
|
+
return updated_node
|
|
70
|
+
|
|
71
|
+
transformer = AttrsKwOnlyFixer()
|
|
72
|
+
modified_module = module.visit(transformer)
|
|
73
|
+
|
|
74
|
+
return modified_module
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _has_kw_only_arg(call: cst.Call) -> bool:
|
|
78
|
+
"""Check if the call already has a kw_only argument."""
|
|
79
|
+
for arg in call.args:
|
|
80
|
+
if isinstance(arg.keyword, cst.Name) and arg.keyword.value == "kw_only":
|
|
81
|
+
return True
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _is_attrs_decorator(decorator: cst.Decorator) -> bool:
|
|
86
|
+
"""Check if decorator is an attrs decorator (@attrs.define, @attr.s, etc.)."""
|
|
87
|
+
# Get the base decorator (without call)
|
|
88
|
+
base_decorator = decorator.decorator
|
|
89
|
+
if isinstance(base_decorator, cst.Call):
|
|
90
|
+
base_decorator = base_decorator.func
|
|
91
|
+
|
|
92
|
+
# Check for @attrs.define, @attrs.frozen, etc.
|
|
93
|
+
if isinstance(base_decorator, cst.Attribute):
|
|
94
|
+
if (
|
|
95
|
+
isinstance(base_decorator.value, cst.Name)
|
|
96
|
+
and base_decorator.value.value == "attrs"
|
|
97
|
+
and isinstance(base_decorator.attr, cst.Name)
|
|
98
|
+
and base_decorator.attr.value in ("define", "frozen")
|
|
99
|
+
):
|
|
100
|
+
return True
|
|
101
|
+
|
|
102
|
+
# Check for @attr.s
|
|
103
|
+
if isinstance(base_decorator, cst.Attribute):
|
|
104
|
+
if (
|
|
105
|
+
isinstance(base_decorator.value, cst.Name)
|
|
106
|
+
and base_decorator.value.value == "attr"
|
|
107
|
+
and isinstance(base_decorator.attr, cst.Name)
|
|
108
|
+
and base_decorator.attr.value == "s"
|
|
109
|
+
):
|
|
110
|
+
return True
|
|
111
|
+
|
|
112
|
+
return False
|