wexample-filestate-python 0.0.48__tar.gz → 0.0.56__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.
Files changed (87) hide show
  1. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/PKG-INFO +9 -5
  2. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/README.md +2 -2
  3. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/pyproject.toml +49 -11
  4. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/common/pipy_gateway.py +1 -1
  5. wexample_filestate_python-0.0.56/src/wexample_filestate_python/const/path.py +6 -0
  6. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/const/python_file.py +1 -0
  7. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/file → wexample_filestate_python-0.0.56/src/wexample_filestate_python/event}/__init__.py +0 -0
  8. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/file/__pycache__ → wexample_filestate_python-0.0.56/src/wexample_filestate_python/event/src}/__init__.py +0 -0
  9. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/helpers → wexample_filestate_python-0.0.56/src/wexample_filestate_python/event/src/wexample_event}/__init__.py +0 -0
  10. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/file/__pycache__}/__init__.py +0 -0
  11. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/options_provider/__pycache__ → wexample_filestate_python-0.0.56/src/wexample_filestate_python/helpers/python}/__init__.py +0 -0
  12. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/utils → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option}/__init__.py +0 -0
  13. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/utils/relocate_imports/__pycache__ → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/__init__.py +0 -0
  14. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/abstract_python_file_content_option.py +3 -1
  15. wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python/class_name_matches_file_name_option.py +65 -0
  16. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/relocate_imports_option.py +8 -3
  17. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/remove_unused_option.py +1 -1
  18. wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python_option.py +133 -0
  19. wexample_filestate_python-0.0.56/src/wexample_filestate_python/utils/relocate_imports/__pycache__/__init__.py +0 -0
  20. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/relocate_imports/python_localize_runtime_imports.py +15 -4
  21. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/relocate_imports/python_runtime_symbol_collector.py +22 -0
  22. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/relocate_imports/python_usage_collector.py +7 -1
  23. wexample_filestate_python-0.0.56/tests/tests/__init__.py +0 -0
  24. wexample_filestate_python-0.0.56/tests/wexample_tests/__init__.py +0 -0
  25. wexample_filestate_python-0.0.48/src/wexample_filestate_python/option/python_option.py +0 -164
  26. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/__init__.py +0 -0
  27. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/__pycache__/__init__.py +0 -0
  28. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/common/__init__.py +0 -0
  29. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/common/__pycache__/__init__.py +0 -0
  30. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/config_option/__init__.py +0 -0
  31. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/config_option/__pycache__/__init__.py +0 -0
  32. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/config_option/mixin/__init__.py +0 -0
  33. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/config_option/mixin/__pycache__/__init__.py +0 -0
  34. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/config_option/mixin/with_stdout_wrapping_mixin.py +0 -0
  35. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/config_value/__init__.py +0 -0
  36. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/config_value/__pycache__/__init__.py +0 -0
  37. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/config_value/python_config_value.py +0 -0
  38. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/const/__init__.py +0 -0
  39. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/const/__pycache__/__init__.py +0 -0
  40. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/const/name_pattern.py +0 -0
  41. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/helpers/__pycache__ → wexample_filestate_python-0.0.56/src/wexample_filestate_python/file}/__init__.py +0 -0
  42. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/file/python_file.py +0 -0
  43. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option/__pycache__ → wexample_filestate_python-0.0.56/src/wexample_filestate_python/helpers}/__init__.py +0 -0
  44. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/options_provider → wexample_filestate_python-0.0.56/src/wexample_filestate_python/helpers/__pycache__}/__init__.py +0 -0
  45. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/helpers/package.py +0 -0
  46. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/helpers/toml.py +0 -0
  47. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/utils → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option}/__pycache__/__init__.py +0 -0
  48. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/add_future_annotations_option.py +0 -0
  49. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/add_return_types_option.py +0 -0
  50. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/fix_attrs_option.py +0 -0
  51. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/fix_blank_lines_option.py +0 -0
  52. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/format_option.py +0 -0
  53. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/fstringify_option.py +0 -0
  54. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/modernize_typing_option.py +0 -0
  55. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/order_class_attributes_option.py +0 -0
  56. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/order_class_docstring_option.py +0 -0
  57. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/order_class_methods_option.py +0 -0
  58. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/order_constants_option.py +0 -0
  59. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/order_iterable_items_option.py +0 -0
  60. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/order_main_guard_option.py +0 -0
  61. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/order_module_docstring_option.py +0 -0
  62. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/order_module_functions_option.py +0 -0
  63. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/order_module_metadata_option.py +0 -0
  64. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/order_type_checking_block_option.py +0 -0
  65. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/sort_imports_option.py +0 -0
  66. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/option → wexample_filestate_python-0.0.56/src/wexample_filestate_python/option/python}/unquote_annotations_option.py +0 -0
  67. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/workdir → wexample_filestate_python-0.0.56/src/wexample_filestate_python/options_provider}/__init__.py +0 -0
  68. {wexample_filestate_python-0.0.48/src/wexample_filestate_python/workdir → wexample_filestate_python-0.0.56/src/wexample_filestate_python/options_provider}/__pycache__/__init__.py +0 -0
  69. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/options_provider/python_options_provider.py +0 -0
  70. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/py.typed +0 -0
  71. {wexample_filestate_python-0.0.48/tests/tests → wexample_filestate_python-0.0.56/src/wexample_filestate_python/utils}/__init__.py +0 -0
  72. {wexample_filestate_python-0.0.48/tests/wexample_tests → wexample_filestate_python-0.0.56/src/wexample_filestate_python/utils/__pycache__}/__init__.py +0 -0
  73. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/python_attrs_utils.py +0 -0
  74. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/python_blank_lines_utils.py +0 -0
  75. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/python_class_attributes_utils.py +0 -0
  76. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/python_class_docstring_utils.py +0 -0
  77. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/python_class_methods_utils.py +0 -0
  78. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/python_constants_utils.py +0 -0
  79. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/python_docstring_utils.py +0 -0
  80. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/python_functions_utils.py +0 -0
  81. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/python_iterable_utils.py +0 -0
  82. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/python_main_guard_utils.py +0 -0
  83. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/python_module_metadata_utils.py +0 -0
  84. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/python_type_checking_utils.py +0 -0
  85. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/relocate_imports/__init__.py +0 -0
  86. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/relocate_imports/python_import_rewriter.py +0 -0
  87. {wexample_filestate_python-0.0.48 → wexample_filestate_python-0.0.56}/src/wexample_filestate_python/utils/relocate_imports/python_parser_import_index.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wexample-filestate-python
3
- Version: 0.0.48
3
+ Version: 0.0.56
4
4
  Summary: Helpers for Python.
5
5
  Author-Email: weeger <contact@wexample.com>
6
6
  License: MIT
@@ -11,12 +11,16 @@ Project-URL: homepage, https://github.com/wexample/python-filestate-python
11
11
  Requires-Python: >=3.10
12
12
  Requires-Dist: attrs>=23.1.0
13
13
  Requires-Dist: autoflake
14
+ Requires-Dist: black
14
15
  Requires-Dist: cattrs>=23.1.0
16
+ Requires-Dist: flynt
17
+ Requires-Dist: isort
15
18
  Requires-Dist: libcst
16
19
  Requires-Dist: packaging
20
+ Requires-Dist: pyupgrade
17
21
  Requires-Dist: tomli
18
- Requires-Dist: wexample-filestate==0.0.61
19
- Requires-Dist: wexample-helpers-api==0.0.71
22
+ Requires-Dist: wexample-api==0.0.80
23
+ Requires-Dist: wexample-filestate==0.0.71
20
24
  Provides-Extra: dev
21
25
  Requires-Dist: pytest; extra == "dev"
22
26
  Requires-Dist: pytest-cov; extra == "dev"
@@ -24,7 +28,7 @@ Description-Content-Type: text/markdown
24
28
 
25
29
  # wexample-filestate-python
26
30
 
27
- Version: 0.0.48
31
+ Version: 0.0.56
28
32
 
29
33
  Helpers for Python.
30
34
 
@@ -43,7 +47,7 @@ First, install the required testing dependencies:
43
47
 
44
48
  Run all tests with coverage:
45
49
  ```bash
46
- .venv/bin/python -m pytest --cov
50
+ .venv/bin/python -m pytest --cov --cov-report=html
47
51
  ```
48
52
 
49
53
  ### Common Commands
@@ -1,6 +1,6 @@
1
1
  # wexample-filestate-python
2
2
 
3
- Version: 0.0.48
3
+ Version: 0.0.56
4
4
 
5
5
  Helpers for Python.
6
6
 
@@ -19,7 +19,7 @@ First, install the required testing dependencies:
19
19
 
20
20
  Run all tests with coverage:
21
21
  ```bash
22
- .venv/bin/python -m pytest --cov
22
+ .venv/bin/python -m pytest --cov --cov-report=html
23
23
  ```
24
24
 
25
25
  ### Common Commands
@@ -6,7 +6,7 @@ build-backend = "pdm.backend"
6
6
 
7
7
  [project]
8
8
  name = "wexample-filestate-python"
9
- version = "0.0.48"
9
+ version = "0.0.56"
10
10
  description = "Helpers for Python."
11
11
  authors = [
12
12
  { name = "weeger", email = "contact@wexample.com" },
@@ -20,12 +20,16 @@ classifiers = [
20
20
  dependencies = [
21
21
  "attrs>=23.1.0",
22
22
  "autoflake",
23
+ "black",
23
24
  "cattrs>=23.1.0",
25
+ "flynt",
26
+ "isort",
24
27
  "libcst",
25
28
  "packaging",
29
+ "pyupgrade",
26
30
  "tomli",
27
- "wexample-filestate==0.0.61",
28
- "wexample-helpers-api==0.0.71",
31
+ "wexample-api==0.0.80",
32
+ "wexample-filestate==0.0.71",
29
33
  ]
30
34
 
31
35
  [project.readme]
@@ -44,6 +48,14 @@ dev = [
44
48
  "pytest-cov",
45
49
  ]
46
50
 
51
+ [tool.setuptools.packages.find]
52
+ include = [
53
+ "*",
54
+ ]
55
+ exclude = [
56
+ "wexample_filestate_python.testing*",
57
+ ]
58
+
47
59
  [tool.pdm]
48
60
  distribution = true
49
61
 
@@ -53,16 +65,42 @@ packages = [
53
65
  { include = "wexample_filestate_python", from = "src" },
54
66
  ]
55
67
 
68
+ [tool.pytest.ini_options]
69
+ testpaths = [
70
+ "tests",
71
+ ]
72
+ pythonpath = [
73
+ "src",
74
+ ]
75
+
76
+ [tool.coverage.run]
77
+ source = [
78
+ "wexample_filestate_python",
79
+ ]
80
+ omit = [
81
+ "*/tests/*",
82
+ "*/.venv/*",
83
+ "*/venv/*",
84
+ ]
85
+
86
+ [tool.coverage.report]
87
+ exclude_lines = [
88
+ "pragma: no cover",
89
+ "def __repr__",
90
+ "raise AssertionError",
91
+ "raise NotImplementedError",
92
+ "if __name__ == .__main__.:",
93
+ "if TYPE_CHECKING:",
94
+ "@abstractmethod",
95
+ ]
96
+
56
97
  [tool.filestate]
57
98
  keep = [
99
+ "autoflake",
58
100
  "black",
101
+ "libcst",
102
+ "tomli",
103
+ "pyupgrade",
59
104
  "isort",
60
- ]
61
-
62
- [tool.setuptools.packages.find]
63
- include = [
64
- "*",
65
- ]
66
- exclude = [
67
- "wexample_filestate_python.testing*",
105
+ "flynt",
68
106
  ]
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from wexample_api.common.abstract_gateway import AbstractGateway
3
4
  from wexample_helpers.classes.field import public_field
4
5
  from wexample_helpers.decorator.base_class import base_class
5
- from wexample_helpers_api.common.abstract_gateway import AbstractGateway
6
6
 
7
7
 
8
8
  @base_class
@@ -0,0 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ PATH_DIR_SRC: Path = Path("src")
6
+ PATH_DIR_TESTS: Path = Path("tests")
@@ -3,3 +3,4 @@ from __future__ import annotations
3
3
  from pathlib import Path
4
4
 
5
5
  PYTHON_FILE_PYTEST_COVERAGE_JSON: Path = Path("coverage.json")
6
+ PYTHON_FILE_EXTENSION = "py"
@@ -3,6 +3,8 @@ from __future__ import annotations
3
3
  from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from wexample_config.config_option.abstract_config_option import AbstractConfigOption
6
+ from wexample_filestate.enum.scopes import Scope
7
+ from wexample_filestate.operation.abstract_operation import AbstractOperation
6
8
  from wexample_filestate.option.mixin.option_mixin import OptionMixin
7
9
  from wexample_helpers.classes.abstract_method import abstract_method
8
10
  from wexample_helpers.decorator.base_class import base_class
@@ -18,7 +20,7 @@ class AbstractPythonFileContentOption(OptionMixin, AbstractConfigOption):
18
20
  return bool
19
21
 
20
22
  def create_required_operation(
21
- self, target: TargetFileOrDirectoryType
23
+ self, target: TargetFileOrDirectoryType, scopes: set[Scope]
22
24
  ) -> AbstractOperation | None:
23
25
  from wexample_filestate.operation.file_write_operation import FileWriteOperation
24
26
 
@@ -0,0 +1,65 @@
1
+ from __future__ import annotations
2
+
3
+ import ast
4
+ from pathlib import Path
5
+ from typing import TYPE_CHECKING
6
+
7
+ from wexample_config.config_option.abstract_config_option import AbstractConfigOption
8
+ from wexample_filestate.enum.scopes import Scope
9
+ from wexample_filestate.operation.abstract_operation import AbstractOperation
10
+ from wexample_filestate.option.mixin.option_mixin import OptionMixin
11
+ from wexample_helpers.decorator.base_class import base_class
12
+
13
+ if TYPE_CHECKING:
14
+ from wexample_filestate.const.types_state_items import TargetFileOrDirectoryType
15
+
16
+
17
+ @base_class
18
+ class ClassNameMatchesFileNameOption(OptionMixin, AbstractConfigOption):
19
+ @staticmethod
20
+ def _expected_class_name_from_path(path: Path) -> str | None:
21
+ stem = path.stem
22
+ if not stem or stem == "__init__":
23
+ return None
24
+
25
+ cleaned = stem.replace("-", "_")
26
+ parts = [part for part in cleaned.split("_") if part]
27
+ if not parts:
28
+ return None
29
+
30
+ def normalize(part: str) -> str:
31
+ head, tail = part[:1], part[1:]
32
+ return head.upper() + tail.lower()
33
+
34
+ return "".join(normalize(part) for part in parts)
35
+
36
+ def create_required_operation(
37
+ self, target: TargetFileOrDirectoryType, scopes: set[Scope]
38
+ ) -> AbstractOperation | None:
39
+ del scopes # unused
40
+
41
+ if not self._class_name_matches_file_name(target=target):
42
+ # TODO
43
+ print(f" CORRECT {target.get_path()}")
44
+
45
+ return None
46
+
47
+ def get_description(self) -> str:
48
+ return "File name should be a pascal-case version of the class name"
49
+
50
+ def _class_name_matches_file_name(self, target: TargetFileOrDirectoryType) -> bool:
51
+ expected_name = self._expected_class_name_from_path(path=target.get_path())
52
+
53
+ if expected_name is None:
54
+ return False
55
+
56
+ source = target.get_local_file().read()
57
+ try:
58
+ module = ast.parse(source)
59
+ except SyntaxError:
60
+ return False
61
+
62
+ for node in module.body:
63
+ if isinstance(node, ast.ClassDef) and node.name == expected_name:
64
+ return True
65
+ return False
@@ -152,11 +152,16 @@ class RelocateImportsOption(AbstractPythonFileContentOption):
152
152
 
153
153
  # For names used inside cast() anywhere in the module:
154
154
  # - do NOT auto-add to TYPE_CHECKING (unless also in annotations via type_only_for_block)
155
- # - remove module-level import unless also class_level
155
+ # - remove module-level import unless also class_level or runtime_used_anywhere
156
+ # Exclude names used at runtime at module level (e.g., TerminalColor.RED in dict values)
156
157
  names_to_remove_from_module = (
157
- set(runtime_local_final)
158
+ (set(runtime_local_final) - runtime_used_anywhere)
158
159
  | set(type_only_names)
159
- | (set(cast_type_names_anywhere) - set(class_level_names))
160
+ | (
161
+ set(cast_type_names_anywhere)
162
+ - set(class_level_names)
163
+ - runtime_used_anywhere
164
+ )
160
165
  )
161
166
 
162
167
  # Do not add to TYPE_CHECKING if the name's module-level import is kept
@@ -28,7 +28,7 @@ class RemoveUnusedOption(AbstractPythonFileContentOption):
28
28
  "--remove-unused-variables",
29
29
  "--expand-star-imports",
30
30
  "--remove-duplicate-keys",
31
- target.get_path(),
31
+ str(target.get_path()),
32
32
  ],
33
33
  )
34
34
 
@@ -0,0 +1,133 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, Union
4
+
5
+ from wexample_config.config_option.abstract_config_option import AbstractConfigOption
6
+ from wexample_config.config_option.abstract_nested_config_option import (
7
+ AbstractNestedConfigOption,
8
+ )
9
+ from wexample_filestate.enum.scopes import Scope
10
+ from wexample_filestate.operation.abstract_operation import AbstractOperation
11
+ from wexample_filestate.option.mixin.option_mixin import OptionMixin
12
+ from wexample_helpers.decorator.base_class import base_class
13
+
14
+ if TYPE_CHECKING:
15
+ from wexample_filestate.const.types_state_items import TargetFileOrDirectoryType
16
+
17
+
18
+ @base_class
19
+ class PythonOption(OptionMixin, AbstractNestedConfigOption):
20
+ @staticmethod
21
+ def get_raw_value_allowed_type() -> Any:
22
+ from wexample_filestate_python.config_value.python_config_value import (
23
+ PythonConfigValue,
24
+ )
25
+
26
+ return Union[list[str], dict, PythonConfigValue]
27
+
28
+ def create_required_operation(
29
+ self, target: TargetFileOrDirectoryType, scopes: set[Scope]
30
+ ) -> AbstractOperation | None:
31
+ return self._create_child_required_operation(target=target, scopes=scopes)
32
+
33
+ def get_allowed_options(self) -> list[type[AbstractConfigOption]]:
34
+ # Import all the config options for each Python operation
35
+ from wexample_filestate_python.option.python.add_future_annotations_option import (
36
+ AddFutureAnnotationsOption,
37
+ )
38
+ from wexample_filestate_python.option.python.add_return_types_option import (
39
+ AddReturnTypesOption,
40
+ )
41
+ from wexample_filestate_python.option.python.class_name_matches_file_name_option import (
42
+ ClassNameMatchesFileNameOption,
43
+ )
44
+ from wexample_filestate_python.option.python.fix_attrs_option import (
45
+ FixAttrsOption,
46
+ )
47
+ from wexample_filestate_python.option.python.fix_blank_lines_option import (
48
+ FixBlankLinesOption,
49
+ )
50
+ from wexample_filestate_python.option.python.format_option import FormatOption
51
+ from wexample_filestate_python.option.python.fstringify_option import (
52
+ FstringifyOption,
53
+ )
54
+ from wexample_filestate_python.option.python.modernize_typing_option import (
55
+ ModernizeTypingOption,
56
+ )
57
+ from wexample_filestate_python.option.python.order_class_attributes_option import (
58
+ OrderClassAttributesOption,
59
+ )
60
+ from wexample_filestate_python.option.python.order_class_docstring_option import (
61
+ OrderClassDocstringOption,
62
+ )
63
+ from wexample_filestate_python.option.python.order_class_methods_option import (
64
+ OrderClassMethodsOption,
65
+ )
66
+ from wexample_filestate_python.option.python.order_constants_option import (
67
+ OrderConstantsOption,
68
+ )
69
+ from wexample_filestate_python.option.python.order_iterable_items_option import (
70
+ OrderIterableItemsOption,
71
+ )
72
+ from wexample_filestate_python.option.python.order_main_guard_option import (
73
+ OrderMainGuardOption,
74
+ )
75
+ from wexample_filestate_python.option.python.order_module_docstring_option import (
76
+ OrderModuleDocstringOption,
77
+ )
78
+ from wexample_filestate_python.option.python.order_module_functions_option import (
79
+ OrderModuleFunctionsOption,
80
+ )
81
+ from wexample_filestate_python.option.python.order_module_metadata_option import (
82
+ OrderModuleMetadataOption,
83
+ )
84
+ from wexample_filestate_python.option.python.order_type_checking_block_option import (
85
+ OrderTypeCheckingBlockOption,
86
+ )
87
+ from wexample_filestate_python.option.python.relocate_imports_option import (
88
+ RelocateImportsOption,
89
+ )
90
+ from wexample_filestate_python.option.python.remove_unused_option import (
91
+ RemoveUnusedOption,
92
+ )
93
+ from wexample_filestate_python.option.python.sort_imports_option import (
94
+ SortImportsOption,
95
+ )
96
+ from wexample_filestate_python.option.python.unquote_annotations_option import (
97
+ UnquoteAnnotationsOption,
98
+ )
99
+
100
+ return [
101
+ AddFutureAnnotationsOption,
102
+ AddReturnTypesOption,
103
+ ClassNameMatchesFileNameOption,
104
+ FixAttrsOption,
105
+ FixBlankLinesOption,
106
+ FormatOption,
107
+ FstringifyOption,
108
+ ModernizeTypingOption,
109
+ OrderClassAttributesOption,
110
+ OrderClassDocstringOption,
111
+ OrderClassMethodsOption,
112
+ OrderConstantsOption,
113
+ OrderIterableItemsOption,
114
+ OrderMainGuardOption,
115
+ OrderModuleDocstringOption,
116
+ OrderModuleFunctionsOption,
117
+ OrderModuleMetadataOption,
118
+ OrderTypeCheckingBlockOption,
119
+ RelocateImportsOption,
120
+ RemoveUnusedOption,
121
+ SortImportsOption,
122
+ UnquoteAnnotationsOption,
123
+ ]
124
+
125
+ def set_value(self, raw_value: Any) -> None:
126
+ # Convert list form to dict form for consistency
127
+ if isinstance(raw_value, list):
128
+ dict_value = {}
129
+ for option_name in raw_value:
130
+ dict_value[option_name] = True
131
+ raw_value = dict_value
132
+
133
+ super().set_value(raw_value=raw_value)
@@ -27,6 +27,7 @@ class PythonLocalizeRuntimeImports(cst.CSTTransformer):
27
27
  self.functions_needing_local = functions_needing_local
28
28
  self.skip_local_names = skip_local_names or set()
29
29
  self.class_stack: list[str] = []
30
+ self.func_stack: list[str] = []
30
31
 
31
32
  @staticmethod
32
33
  def _build_module_expr(mod: str | None) -> cst.BaseExpression | None:
@@ -63,10 +64,11 @@ class PythonLocalizeRuntimeImports(cst.CSTTransformer):
63
64
  self, original_node: cst.AsyncFunctionDef, updated_node: cst.AsyncFunctionDef
64
65
  ) -> cst.AsyncFunctionDef:
65
66
  func_qname = (
66
- ".".join(self.class_stack + [original_node.name.value])
67
- if self.class_stack
67
+ ".".join(self.class_stack + self.func_stack + [original_node.name.value])
68
+ if (self.class_stack or self.func_stack)
68
69
  else original_node.name.value
69
70
  )
71
+ self.func_stack.pop()
70
72
  to_inject, pairs = self._build_local_imports(func_qname)
71
73
  if not to_inject:
72
74
  return updated_node
@@ -160,10 +162,11 @@ class PythonLocalizeRuntimeImports(cst.CSTTransformer):
160
162
  self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef
161
163
  ) -> cst.FunctionDef:
162
164
  func_qname = (
163
- ".".join(self.class_stack + [original_node.name.value])
164
- if self.class_stack
165
+ ".".join(self.class_stack + self.func_stack + [original_node.name.value])
166
+ if (self.class_stack or self.func_stack)
165
167
  else original_node.name.value
166
168
  )
169
+ self.func_stack.pop()
167
170
  # Build consolidated imports and list of pairs to hoist
168
171
  to_inject, pairs = self._build_local_imports(func_qname)
169
172
  if not to_inject:
@@ -251,10 +254,18 @@ class PythonLocalizeRuntimeImports(cst.CSTTransformer):
251
254
  body=pruned_node.body.with_changes(body=new_body)
252
255
  )
253
256
 
257
+ def visit_AsyncFunctionDef(self, node: cst.AsyncFunctionDef) -> bool: # type: ignore[override]
258
+ self.func_stack.append(node.name.value)
259
+ return True
260
+
254
261
  def visit_ClassDef(self, node: cst.ClassDef) -> bool: # type: ignore[override]
255
262
  self.class_stack.append(node.name.value)
256
263
  return True
257
264
 
265
+ def visit_FunctionDef(self, node: cst.FunctionDef) -> bool: # type: ignore[override]
266
+ self.func_stack.append(node.name.value)
267
+ return True
268
+
258
269
  def _build_import_statements_from_pairs(
259
270
  self, pairs: set[tuple[str | None, str]]
260
271
  ) -> list[cst.BaseStatement]:
@@ -25,9 +25,31 @@ class PythonRuntimeSymbolCollector(cst.CSTVisitor):
25
25
  self.in_annotation_stack.append(True)
26
26
  return True
27
27
 
28
+ def visit_Attribute(self, node: cst.Attribute) -> bool: # type: ignore[override]
29
+ """Visit Attribute nodes to detect runtime usage of imported names.
30
+
31
+ For example, in TerminalColor.RED, we need to mark TerminalColor as runtime-used.
32
+ """
33
+ if self.in_annotation_stack:
34
+ return True
35
+ # Walk the value (left side) to find the base name
36
+ self._walk_for_runtime_names(node.value)
37
+ return False # Don't visit children since we handled it manually
38
+
28
39
  def visit_Name(self, node: cst.Name) -> None: # type: ignore[override]
29
40
  if self.in_annotation_stack:
30
41
  return
31
42
  val = node.value
32
43
  if val in self.imported_value_names:
33
44
  self.runtime_used_anywhere.add(val)
45
+
46
+ def _walk_for_runtime_names(self, expr: cst.BaseExpression) -> None:
47
+ """Recursively walk an expression to find imported names used at runtime."""
48
+ if isinstance(expr, cst.Name):
49
+ if expr.value in self.imported_value_names:
50
+ self.runtime_used_anywhere.add(expr.value)
51
+ elif isinstance(expr, cst.Attribute):
52
+ # Recurse into the value (left side) only
53
+ self._walk_for_runtime_names(expr.value)
54
+ elif isinstance(expr, cst.Subscript):
55
+ self._walk_for_runtime_names(expr.value)
@@ -341,7 +341,13 @@ class PythonUsageCollector(cst.CSTVisitor):
341
341
 
342
342
  # ----- internals -----
343
343
  def _qualified_func_name(self, base: str) -> str:
344
- return ".".join(self.class_stack + [base]) if self.class_stack else base
344
+ # Include func_stack to distinguish nested functions with the same name
345
+ # e.g., ScreenExample.demo_with_progress_and_confirm._callback vs ScreenExample.example_extended._callback
346
+ return (
347
+ ".".join(self.class_stack + self.func_stack + [base])
348
+ if (self.class_stack or self.func_stack)
349
+ else base
350
+ )
345
351
 
346
352
  def _record_type_names(self, ann: cst.BaseExpression, bucket: set[str]) -> None:
347
353
  if isinstance(ann, cst.Name):
@@ -1,164 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import TYPE_CHECKING, Any, ClassVar, Union
4
-
5
- from wexample_config.config_option.abstract_config_option import AbstractConfigOption
6
- from wexample_config.config_option.abstract_nested_config_option import (
7
- AbstractNestedConfigOption,
8
- )
9
- from wexample_filestate.operation.abstract_operation import AbstractOperation
10
- from wexample_filestate.option.mixin.option_mixin import OptionMixin
11
- from wexample_helpers.decorator.base_class import base_class
12
-
13
- if TYPE_CHECKING:
14
- from wexample_filestate.const.types_state_items import TargetFileOrDirectoryType
15
-
16
-
17
- @base_class
18
- class PythonOption(OptionMixin, AbstractNestedConfigOption):
19
- # filestate: python-constant-sort
20
- # New preferred option name to add `from __future__ import annotations`
21
- OPTION_NAME_ADD_FUTURE_ANNOTATIONS: ClassVar[str] = "add_future_annotations"
22
- OPTION_NAME_ADD_RETURN_TYPES: ClassVar[str] = "add_return_types"
23
- # Fix attrs usage (ensure kw_only=True, etc.)
24
- OPTION_NAME_FIX_ATTRS: ClassVar[str] = "fix_attrs"
25
- # Fix blank lines in Python files (after signatures, docstrings, etc.)
26
- OPTION_NAME_FIX_BLANK_LINES: ClassVar[str] = "fix_blank_lines"
27
- OPTION_NAME_FORMAT: ClassVar[str] = "format"
28
- OPTION_NAME_FSTRINGIFY: ClassVar[str] = "fstringify"
29
- OPTION_NAME_MODERNIZE_TYPING: ClassVar[str] = "modernize_typing"
30
- # Sort class attributes: special first, then public A–Z, then private/protected A–Z
31
- OPTION_NAME_ORDER_CLASS_ATTRIBUTES: ClassVar[str] = "order_class_attributes"
32
- # Ensure class docstring is first statement after header/decorators
33
- OPTION_NAME_ORDER_CLASS_DOCSTRING: ClassVar[str] = "order_class_docstring"
34
- # Order class methods (dunders sequence, class/staticmethods, properties, instances)
35
- OPTION_NAME_ORDER_CLASS_METHODS: ClassVar[str] = "order_class_methods"
36
- # Sort flagged UPPER_CASE constant blocks at module level
37
- OPTION_NAME_ORDER_CONSTANTS: ClassVar[str] = "order_constants"
38
- # Sort items inside flagged iterable literals (lists/dicts)
39
- OPTION_NAME_ORDER_ITERABLE_ITEMS: ClassVar[str] = "order_iterable_items"
40
- # Ensure if __name__ == "__main__" block is at the very end
41
- OPTION_NAME_ORDER_MAIN_GUARD: ClassVar[str] = "order_main_guard"
42
- # Order module docstring to be at the top of the file
43
- OPTION_NAME_ORDER_MODULE_DOCSTRING: ClassVar[str] = "order_module_docstring"
44
- # Order module-level functions (public A–Z, then private)
45
- OPTION_NAME_ORDER_MODULE_FUNCTIONS: ClassVar[str] = "order_module_functions"
46
- # Group and sort module metadata at module level
47
- OPTION_NAME_ORDER_MODULE_METADATA: ClassVar[str] = "order_module_metadata"
48
- # Normalize blank lines between program structures (spacing rules)
49
- OPTION_NAME_ORDER_SPACING: ClassVar[str] = "order_spacing"
50
- # Move TYPE_CHECKING blocks to after regular imports
51
- OPTION_NAME_ORDER_TYPE_CHECKING_BLOCK: ClassVar[str] = "order_type_checking_block"
52
- # Relocate imports by usage (runtime-in-method, class property types, type-only)
53
- OPTION_NAME_RELOCATE_IMPORTS: ClassVar[str] = "relocate_imports"
54
- OPTION_NAME_REMOVE_UNUSED: ClassVar[str] = "remove_unused"
55
- OPTION_NAME_SORT_IMPORTS: ClassVar[str] = "sort_imports"
56
- # New policy: unquote annotations (remove string annotations)
57
- OPTION_NAME_UNQUOTE_ANNOTATIONS: ClassVar[str] = "unquote_annotations"
58
-
59
- @staticmethod
60
- def get_raw_value_allowed_type() -> Any:
61
- from wexample_filestate_python.config_value.python_config_value import (
62
- PythonConfigValue,
63
- )
64
-
65
- return Union[list[str], dict, PythonConfigValue]
66
-
67
- def create_required_operation(
68
- self, target: TargetFileOrDirectoryType
69
- ) -> AbstractOperation | None:
70
- return self._create_child_required_operation(target=target)
71
-
72
- def get_allowed_options(self) -> list[type[AbstractConfigOption]]:
73
- # Import all the config options for each Python operation
74
- from wexample_filestate_python.option.add_future_annotations_option import (
75
- AddFutureAnnotationsOption,
76
- )
77
- from wexample_filestate_python.option.add_return_types_option import (
78
- AddReturnTypesOption,
79
- )
80
- from wexample_filestate_python.option.fix_attrs_option import FixAttrsOption
81
- from wexample_filestate_python.option.fix_blank_lines_option import (
82
- FixBlankLinesOption,
83
- )
84
- from wexample_filestate_python.option.format_option import FormatOption
85
- from wexample_filestate_python.option.fstringify_option import FstringifyOption
86
- from wexample_filestate_python.option.modernize_typing_option import (
87
- ModernizeTypingOption,
88
- )
89
- from wexample_filestate_python.option.order_class_attributes_option import (
90
- OrderClassAttributesOption,
91
- )
92
- from wexample_filestate_python.option.order_class_docstring_option import (
93
- OrderClassDocstringOption,
94
- )
95
- from wexample_filestate_python.option.order_class_methods_option import (
96
- OrderClassMethodsOption,
97
- )
98
- from wexample_filestate_python.option.order_constants_option import (
99
- OrderConstantsOption,
100
- )
101
- from wexample_filestate_python.option.order_iterable_items_option import (
102
- OrderIterableItemsOption,
103
- )
104
- from wexample_filestate_python.option.order_main_guard_option import (
105
- OrderMainGuardOption,
106
- )
107
- from wexample_filestate_python.option.order_module_docstring_option import (
108
- OrderModuleDocstringOption,
109
- )
110
- from wexample_filestate_python.option.order_module_functions_option import (
111
- OrderModuleFunctionsOption,
112
- )
113
- from wexample_filestate_python.option.order_module_metadata_option import (
114
- OrderModuleMetadataOption,
115
- )
116
- from wexample_filestate_python.option.order_type_checking_block_option import (
117
- OrderTypeCheckingBlockOption,
118
- )
119
- from wexample_filestate_python.option.relocate_imports_option import (
120
- RelocateImportsOption,
121
- )
122
- from wexample_filestate_python.option.remove_unused_option import (
123
- RemoveUnusedOption,
124
- )
125
- from wexample_filestate_python.option.sort_imports_option import (
126
- SortImportsOption,
127
- )
128
- from wexample_filestate_python.option.unquote_annotations_option import (
129
- UnquoteAnnotationsOption,
130
- )
131
-
132
- return [
133
- AddFutureAnnotationsOption,
134
- AddReturnTypesOption,
135
- FixAttrsOption,
136
- FixBlankLinesOption,
137
- FormatOption,
138
- FstringifyOption,
139
- ModernizeTypingOption,
140
- OrderClassAttributesOption,
141
- OrderClassDocstringOption,
142
- OrderClassMethodsOption,
143
- OrderConstantsOption,
144
- OrderIterableItemsOption,
145
- OrderMainGuardOption,
146
- OrderModuleDocstringOption,
147
- OrderModuleFunctionsOption,
148
- OrderModuleMetadataOption,
149
- OrderTypeCheckingBlockOption,
150
- RelocateImportsOption,
151
- RemoveUnusedOption,
152
- SortImportsOption,
153
- UnquoteAnnotationsOption,
154
- ]
155
-
156
- def set_value(self, raw_value: Any) -> None:
157
- # Convert list form to dict form for consistency
158
- if isinstance(raw_value, list):
159
- dict_value = {}
160
- for option_name in raw_value:
161
- dict_value[option_name] = True
162
- raw_value = dict_value
163
-
164
- super().set_value(raw_value=raw_value)