wexample-filestate-python 6.8.7__tar.gz → 8.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.
Files changed (93) hide show
  1. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/PKG-INFO +6 -8
  2. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/README.md +3 -4
  3. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/pyproject.toml +3 -4
  4. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/config_option/mixin/with_stdout_wrapping_mixin.py +7 -11
  5. wexample_filestate_python-8.0.0/src/wexample_filestate_python/config_value/python_config_value.py +210 -0
  6. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/file/python_file.py +1 -1
  7. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/helpers/package.py +19 -26
  8. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/helpers/toml.py +8 -12
  9. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/add_future_annotations_option.py +11 -9
  10. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/add_return_types_option.py +40 -64
  11. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/class_name_matches_file_name_option.py +10 -9
  12. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/fix_attrs_option.py +1 -1
  13. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/fix_blank_lines_option.py +2 -3
  14. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/format_option.py +4 -3
  15. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/fstringify_option.py +1 -6
  16. wexample_filestate_python-8.0.0/src/wexample_filestate_python/option/python/modernize_typing_option.py +77 -0
  17. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/order_class_attributes_option.py +5 -0
  18. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/order_class_docstring_option.py +2 -0
  19. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/order_class_methods_option.py +5 -0
  20. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/order_constants_option.py +5 -0
  21. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/order_iterable_items_option.py +1 -2
  22. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/order_main_guard_option.py +2 -6
  23. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/order_module_docstring_option.py +3 -10
  24. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/order_module_functions_option.py +6 -9
  25. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/order_module_metadata_option.py +8 -12
  26. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/order_type_checking_block_option.py +5 -5
  27. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/relocate_imports_option.py +10 -21
  28. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/remove_unused_option.py +7 -11
  29. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/sort_imports_option.py +10 -4
  30. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/unquote_annotations_option.py +12 -9
  31. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python_option.py +5 -4
  32. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/options_provider/python_options_provider.py +3 -0
  33. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/cst_cache.py +3 -2
  34. wexample_filestate_python-8.0.0/src/wexample_filestate_python/utils/python_attrs_utils.py +95 -0
  35. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/python_blank_lines_utils.py +24 -80
  36. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/python_class_attributes_utils.py +13 -11
  37. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/python_class_docstring_utils.py +14 -17
  38. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/python_class_methods_utils.py +64 -85
  39. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/python_constants_utils.py +15 -19
  40. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/python_docstring_utils.py +3 -8
  41. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/python_functions_utils.py +73 -49
  42. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/python_iterable_utils.py +49 -56
  43. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/python_main_guard_utils.py +7 -13
  44. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/python_module_metadata_utils.py +26 -27
  45. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/python_type_checking_utils.py +7 -11
  46. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/relocate_imports/python_import_rewriter.py +25 -35
  47. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/relocate_imports/python_localize_runtime_imports.py +8 -14
  48. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/relocate_imports/python_parser_import_index.py +6 -6
  49. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/relocate_imports/python_runtime_symbol_collector.py +17 -14
  50. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/relocate_imports/python_usage_collector.py +30 -31
  51. {wexample_filestate_python-6.8.7/tests/tests → wexample_filestate_python-8.0.0/tests/helpers}/__init__.py +0 -0
  52. wexample_filestate_python-8.0.0/tests/helpers/test_package.py +94 -0
  53. wexample_filestate_python-8.0.0/tests/helpers/test_toml.py +149 -0
  54. wexample_filestate_python-8.0.0/tests/wexample_tests/__init__.py +0 -0
  55. wexample_filestate_python-6.8.7/src/wexample_filestate_python/config_value/python_config_value.py +0 -195
  56. wexample_filestate_python-6.8.7/src/wexample_filestate_python/option/python/modernize_typing_option.py +0 -25
  57. wexample_filestate_python-6.8.7/src/wexample_filestate_python/utils/python_attrs_utils.py +0 -112
  58. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/__init__.py +0 -0
  59. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/__pycache__/__init__.py +0 -0
  60. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/common/__init__.py +0 -0
  61. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/common/__pycache__/__init__.py +0 -0
  62. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/common/pipy_gateway.py +0 -0
  63. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/config_option/__init__.py +0 -0
  64. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/config_option/__pycache__/__init__.py +0 -0
  65. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/config_option/mixin/__init__.py +0 -0
  66. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/config_option/mixin/__pycache__/__init__.py +0 -0
  67. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/config_value/__init__.py +0 -0
  68. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/config_value/__pycache__/__init__.py +0 -0
  69. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/const/__init__.py +0 -0
  70. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/const/__pycache__/__init__.py +0 -0
  71. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/const/name_pattern.py +0 -0
  72. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/const/path.py +0 -0
  73. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/const/python_file.py +0 -0
  74. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/event/__init__.py +0 -0
  75. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/event/src/__init__.py +0 -0
  76. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/event/src/wexample_event/__init__.py +0 -0
  77. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/file/__init__.py +0 -0
  78. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/file/__pycache__/__init__.py +0 -0
  79. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/helpers/__init__.py +0 -0
  80. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/helpers/__pycache__/__init__.py +0 -0
  81. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/helpers/python/__init__.py +0 -0
  82. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/__init__.py +0 -0
  83. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/__pycache__/__init__.py +0 -0
  84. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/__init__.py +0 -0
  85. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/option/python/abstract_python_file_content_option.py +0 -0
  86. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/options_provider/__init__.py +0 -0
  87. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/options_provider/__pycache__/__init__.py +0 -0
  88. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/py.typed +0 -0
  89. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/__init__.py +0 -0
  90. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/__pycache__/__init__.py +0 -0
  91. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/relocate_imports/__init__.py +0 -0
  92. {wexample_filestate_python-6.8.7 → wexample_filestate_python-8.0.0}/src/wexample_filestate_python/utils/relocate_imports/__pycache__/__init__.py +0 -0
  93. {wexample_filestate_python-6.8.7/tests/wexample_tests → wexample_filestate_python-8.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: 6.8.7
3
+ Version: 8.0.0
4
4
  Summary: Helpers for Python.
5
5
  Author-Email: weeger <contact@wexample.com>
6
6
  License: MIT
@@ -17,10 +17,9 @@ Requires-Dist: flynt
17
17
  Requires-Dist: isort
18
18
  Requires-Dist: libcst
19
19
  Requires-Dist: packaging
20
- Requires-Dist: pyupgrade
21
20
  Requires-Dist: tomli
22
- Requires-Dist: wexample-api>=6.3.0
23
- Requires-Dist: wexample-filestate>=12.1.0
21
+ Requires-Dist: wexample-api>=6.5.0
22
+ Requires-Dist: wexample-filestate>=13.0.0
24
23
  Provides-Extra: dev
25
24
  Requires-Dist: pytest; extra == "dev"
26
25
  Requires-Dist: pytest-cov; extra == "dev"
@@ -28,7 +27,7 @@ Description-Content-Type: text/markdown
28
27
 
29
28
  # filestate_python
30
29
 
31
- Version: 6.8.7
30
+ Version: 8.0.0
32
31
 
33
32
  Helpers for Python.
34
33
 
@@ -116,10 +115,9 @@ Visit the [Wexample Suite documentation](https://docs.wexample.com) for the comp
116
115
  - isort:
117
116
  - libcst:
118
117
  - packaging:
119
- - pyupgrade:
120
118
  - tomli:
121
- - wexample-api: >=6.3.0
122
- - wexample-filestate: >=12.1.0
119
+ - wexample-api: >=6.5.0
120
+ - wexample-filestate: >=13.0.0
123
121
 
124
122
  ## Versioning & Compatibility Policy
125
123
 
@@ -1,6 +1,6 @@
1
1
  # filestate_python
2
2
 
3
- Version: 6.8.7
3
+ Version: 8.0.0
4
4
 
5
5
  Helpers for Python.
6
6
 
@@ -88,10 +88,9 @@ Visit the [Wexample Suite documentation](https://docs.wexample.com) for the comp
88
88
  - isort:
89
89
  - libcst:
90
90
  - packaging:
91
- - pyupgrade:
92
91
  - tomli:
93
- - wexample-api: >=6.3.0
94
- - wexample-filestate: >=12.1.0
92
+ - wexample-api: >=6.5.0
93
+ - wexample-filestate: >=13.0.0
95
94
 
96
95
  ## Versioning & Compatibility Policy
97
96
 
@@ -6,7 +6,7 @@ build-backend = "pdm.backend"
6
6
 
7
7
  [project]
8
8
  name = "wexample-filestate-python"
9
- version = "6.8.7"
9
+ version = "8.0.0"
10
10
  description = "Helpers for Python."
11
11
  authors = [
12
12
  { name = "weeger", email = "contact@wexample.com" },
@@ -26,10 +26,9 @@ dependencies = [
26
26
  "isort",
27
27
  "libcst",
28
28
  "packaging",
29
- "pyupgrade",
30
29
  "tomli",
31
- "wexample-api>=6.3.0",
32
- "wexample-filestate>=12.1.0",
30
+ "wexample-api>=6.5.0",
31
+ "wexample-filestate>=13.0.0",
33
32
  ]
34
33
 
35
34
  [project.readme]
@@ -1,5 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import io
4
+ import sys
5
+
3
6
 
4
7
  class WithStdoutWrappingMixin:
5
8
  @classmethod
@@ -15,9 +18,6 @@ class WithStdoutWrappingMixin:
15
18
  Returns:
16
19
  The return value of the callback function
17
20
  """
18
- import io
19
- import sys
20
-
21
21
  old_stdout = sys.stdout
22
22
  old_stderr = sys.stderr
23
23
  captured_stdout = io.StringIO()
@@ -34,13 +34,9 @@ class WithStdoutWrappingMixin:
34
34
  stdout_content = captured_stdout.getvalue()
35
35
  stderr_content = captured_stderr.getvalue()
36
36
 
37
- if stdout_content.strip():
38
- sys.stdout.write(stdout_content.rstrip())
39
- sys.stdout.write("\n")
40
- sys.stdout.write("\n")
41
- if stderr_content.strip():
42
- sys.stderr.write(stderr_content.rstrip())
43
- sys.stderr.write("\n")
44
- sys.stderr.write("\n")
37
+ if stdout_content and stdout_content.strip():
38
+ sys.stdout.write(stdout_content.rstrip() + "\n\n")
39
+ if stderr_content and stderr_content.strip():
40
+ sys.stderr.write(stderr_content.rstrip() + "\n\n")
45
41
 
46
42
  return result
@@ -0,0 +1,210 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from wexample_config.config_value.config_value import ConfigValue
6
+ from wexample_helpers.classes.field import public_field
7
+ from wexample_helpers.decorator.base_class import base_class
8
+
9
+ # Cached mapping: option-name → self attribute name.
10
+ # Built once on first call to to_option_raw_value() so the 22 deferred
11
+ # imports and get_name() class-method calls are paid only once per process.
12
+ _OPTION_NAME_TO_ATTR: dict[str, str] | None = None
13
+
14
+
15
+ def _build_option_name_to_attr() -> dict[str, str]:
16
+ from wexample_filestate_python.config_option.add_future_annotations_config_option import (
17
+ AddFutureAnnotationsConfigOption,
18
+ )
19
+ from wexample_filestate_python.config_option.add_return_types_config_option import (
20
+ AddReturnTypesConfigOption,
21
+ )
22
+ from wexample_filestate_python.config_option.fix_attrs_config_option import (
23
+ FixAttrsConfigOption,
24
+ )
25
+ from wexample_filestate_python.config_option.fix_blank_lines_config_option import (
26
+ FixBlankLinesConfigOption,
27
+ )
28
+ from wexample_filestate_python.config_option.format_config_option import (
29
+ FormatConfigOption,
30
+ )
31
+ from wexample_filestate_python.config_option.fstringify_config_option import (
32
+ FstringifyConfigOption,
33
+ )
34
+ from wexample_filestate_python.config_option.modernize_typing_config_option import (
35
+ ModernizeTypingConfigOption,
36
+ )
37
+ from wexample_filestate_python.config_option.order_class_attributes_config_option import (
38
+ OrderClassAttributesConfigOption,
39
+ )
40
+ from wexample_filestate_python.config_option.order_class_docstring_config_option import (
41
+ OrderClassDocstringConfigOption,
42
+ )
43
+ from wexample_filestate_python.config_option.order_class_methods_config_option import (
44
+ OrderClassMethodsConfigOption,
45
+ )
46
+ from wexample_filestate_python.config_option.order_constants_config_option import (
47
+ OrderConstantsConfigOption,
48
+ )
49
+ from wexample_filestate_python.config_option.order_iterable_items_config_option import (
50
+ OrderIterableItemsConfigOption,
51
+ )
52
+ from wexample_filestate_python.config_option.order_main_guard_config_option import (
53
+ OrderMainGuardConfigOption,
54
+ )
55
+ from wexample_filestate_python.config_option.order_module_docstring_config_option import (
56
+ OrderModuleDocstringConfigOption,
57
+ )
58
+ from wexample_filestate_python.config_option.order_module_functions_config_option import (
59
+ OrderModuleFunctionsConfigOption,
60
+ )
61
+ from wexample_filestate_python.config_option.order_module_metadata_config_option import (
62
+ OrderModuleMetadataConfigOption,
63
+ )
64
+ from wexample_filestate_python.config_option.order_spacing_config_option import (
65
+ OrderSpacingConfigOption,
66
+ )
67
+ from wexample_filestate_python.config_option.order_type_checking_block_config_option import (
68
+ OrderTypeCheckingBlockConfigOption,
69
+ )
70
+ from wexample_filestate_python.config_option.relocate_imports_config_option import (
71
+ RelocateImportsConfigOption,
72
+ )
73
+ from wexample_filestate_python.config_option.remove_unused_config_option import (
74
+ RemoveUnusedConfigOption,
75
+ )
76
+ from wexample_filestate_python.config_option.sort_imports_config_option import (
77
+ SortImportsConfigOption,
78
+ )
79
+ from wexample_filestate_python.config_option.unquote_annotations_config_option import (
80
+ UnquoteAnnotationsConfigOption,
81
+ )
82
+
83
+ return {
84
+ AddFutureAnnotationsConfigOption.get_name(): "add_future_annotations",
85
+ AddReturnTypesConfigOption.get_name(): "add_return_types",
86
+ FixAttrsConfigOption.get_name(): "fix_attrs",
87
+ FixBlankLinesConfigOption.get_name(): "fix_blank_lines",
88
+ FormatConfigOption.get_name(): "format",
89
+ FstringifyConfigOption.get_name(): "fstringify",
90
+ ModernizeTypingConfigOption.get_name(): "modernize_typing",
91
+ OrderClassAttributesConfigOption.get_name(): "order_class_attributes",
92
+ OrderClassDocstringConfigOption.get_name(): "order_class_docstring",
93
+ OrderClassMethodsConfigOption.get_name(): "order_class_methods",
94
+ OrderConstantsConfigOption.get_name(): "order_constants",
95
+ OrderIterableItemsConfigOption.get_name(): "order_iterable_items",
96
+ OrderMainGuardConfigOption.get_name(): "order_main_guard",
97
+ OrderModuleDocstringConfigOption.get_name(): "order_module_docstring",
98
+ OrderModuleFunctionsConfigOption.get_name(): "order_module_functions",
99
+ OrderModuleMetadataConfigOption.get_name(): "order_module_metadata",
100
+ OrderSpacingConfigOption.get_name(): "order_spacing",
101
+ OrderTypeCheckingBlockConfigOption.get_name(): "order_type_checking_block",
102
+ RelocateImportsConfigOption.get_name(): "relocate_imports",
103
+ RemoveUnusedConfigOption.get_name(): "remove_unused",
104
+ SortImportsConfigOption.get_name(): "sort_imports",
105
+ UnquoteAnnotationsConfigOption.get_name(): "unquote_annotations",
106
+ }
107
+
108
+
109
+ @base_class
110
+ class PythonConfigValue(ConfigValue):
111
+ add_future_annotations: bool | None = public_field(
112
+ default=None,
113
+ description="Add `from __future__ import annotations`",
114
+ )
115
+ add_return_types: bool | None = public_field(
116
+ default=None,
117
+ description="Add return type annotations",
118
+ )
119
+ fix_attrs: bool | None = public_field(
120
+ default=None,
121
+ description="Fix attrs usage (ensure kw_only=True, etc.)",
122
+ )
123
+ fix_blank_lines: bool | None = public_field(
124
+ default=None,
125
+ description="Fix blank lines in Python files",
126
+ )
127
+ format: bool | None = public_field(
128
+ default=None,
129
+ description="Format Python code",
130
+ )
131
+ fstringify: bool | None = public_field(
132
+ default=None,
133
+ description="Convert string formatting to f-strings",
134
+ )
135
+ modernize_typing: bool | None = public_field(
136
+ default=None,
137
+ description="Modernize typing annotations",
138
+ )
139
+ order_class_attributes: bool | None = public_field(
140
+ default=None,
141
+ description="Sort class attributes: special first, then public A–Z, then private/protected A–Z",
142
+ )
143
+ order_class_docstring: bool | None = public_field(
144
+ default=None,
145
+ description="Ensure class docstring is first statement after header/decorators",
146
+ )
147
+ order_class_methods: bool | None = public_field(
148
+ default=None,
149
+ description="Order class methods (dunders sequence, class/staticmethods, properties, instances)",
150
+ )
151
+ order_constants: bool | None = public_field(
152
+ default=None,
153
+ description="Sort flagged UPPER_CASE constant blocks at module level",
154
+ )
155
+ order_iterable_items: bool | None = public_field(
156
+ default=None,
157
+ description="Sort items inside flagged iterable literals (lists/dicts)",
158
+ )
159
+ order_main_guard: bool | None = public_field(
160
+ default=None,
161
+ description="Ensure if __name__ == '__main__' block is at the very end",
162
+ )
163
+ order_module_docstring: bool | None = public_field(
164
+ default=None,
165
+ description="Order module docstring to be at the top of the file",
166
+ )
167
+ order_module_functions: bool | None = public_field(
168
+ default=None,
169
+ description="Order module-level functions (public A–Z, then private)",
170
+ )
171
+ order_module_metadata: bool | None = public_field(
172
+ default=None,
173
+ description="Group and sort module metadata at module level",
174
+ )
175
+ order_spacing: bool | None = public_field(
176
+ default=None,
177
+ description="Normalize blank lines between program structures (spacing rules)",
178
+ )
179
+ order_type_checking_block: bool | None = public_field(
180
+ default=None,
181
+ description="Move TYPE_CHECKING blocks to after regular imports",
182
+ )
183
+ raw: Any = public_field(
184
+ default=None, description="Disabled raw value for this config."
185
+ )
186
+ relocate_imports: bool | None = public_field(
187
+ default=None,
188
+ description="Relocate imports by usage (runtime-in-method, class property types, type-only)",
189
+ )
190
+ remove_unused: bool | None = public_field(
191
+ default=None,
192
+ description="Remove unused imports",
193
+ )
194
+ sort_imports: bool | None = public_field(
195
+ default=None,
196
+ description="Sort imports",
197
+ )
198
+ unquote_annotations: bool | None = public_field(
199
+ default=None,
200
+ description="Unquote annotations (remove string annotations)",
201
+ )
202
+
203
+ def to_option_raw_value(self) -> Any:
204
+ global _OPTION_NAME_TO_ATTR
205
+ if _OPTION_NAME_TO_ATTR is None:
206
+ _OPTION_NAME_TO_ATTR = _build_option_name_to_attr()
207
+ return {
208
+ name: getattr(self, attr)
209
+ for name, attr in _OPTION_NAME_TO_ATTR.items()
210
+ }
@@ -9,4 +9,4 @@ class PythonFile(ItemTargetFile):
9
9
  EXTENSION_ENV: ClassVar[str] = "py"
10
10
 
11
11
  def _expected_file_name_extension(self) -> str | None:
12
- return PythonFile.EXTENSION_ENV
12
+ return self.EXTENSION_ENV
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import ast
4
+ import re
4
5
  from typing import TYPE_CHECKING
5
6
 
6
7
  import tomli
@@ -19,31 +20,21 @@ def package_get_dependencies(root_dir: str | Path) -> dict[str, set[str]]:
19
20
  if not packages_root.exists() or not packages_root.is_dir():
20
21
  raise ValueError(f"Error: {packages_root} does not exist or is not a directory")
21
22
 
22
- dependencies = {}
23
-
24
- # First pass: collect all local packages
25
- for package_dir in packages_root.iterdir():
26
- if not package_dir.is_dir():
27
- continue
28
-
29
- package_info = package_get_info(package_dir)
30
- if package_info:
31
- name, _ = package_info
32
- dependencies[name] = set()
33
-
34
- # Second pass: analyze dependencies
35
- for package_dir in packages_root.iterdir():
36
- if not package_dir.is_dir():
37
- continue
23
+ # Filter to directories once; avoids repeated is_dir() calls in two passes.
24
+ all_dirs = [e for e in packages_root.iterdir() if e.is_dir()]
38
25
 
26
+ # Single pass: collect all local package infos (name → raw deps).
27
+ pkg_infos: dict[str, set[str]] = {}
28
+ for package_dir in all_dirs:
39
29
  package_info = package_get_info(package_dir)
40
30
  if package_info:
41
31
  name, deps = package_info
42
- if name in dependencies:
43
- # Only keep dependencies that are local packages
44
- dependencies[name] = {dep for dep in deps if dep in dependencies}
32
+ pkg_infos[name] = deps
33
+
34
+ local_names: set[str] = set(pkg_infos) # O(1) membership for filtering
45
35
 
46
- return dependencies
36
+ # Keep only deps that resolve to a local package (set intersection).
37
+ return {name: deps & local_names for name, deps in pkg_infos.items()}
47
38
 
48
39
 
49
40
  def package_get_info(package_dir: Path) -> tuple[str, set[str]] | None:
@@ -71,10 +62,8 @@ def package_get_info(package_dir: Path) -> tuple[str, set[str]] | None:
71
62
 
72
63
 
73
64
  def package_normalize_name(val: str) -> str:
74
- import re as _re
75
-
76
65
  # strip extras, versions, markers
77
- base = _re.split(r"[\s<>=!~;\[]", val, maxsplit=1)[0]
66
+ base = re.split(r"[\s<>=!~;\[]", val, maxsplit=1)[0]
78
67
  return base.strip().lower()
79
68
 
80
69
 
@@ -94,11 +83,15 @@ def package_parse_setup(path: Path) -> dict:
94
83
  ):
95
84
  result = {}
96
85
  for kw in node.keywords:
97
- if isinstance(kw.value, ast.Str):
98
- result[kw.arg] = kw.value.s
86
+ if isinstance(kw.value, ast.Constant) and isinstance(
87
+ kw.value.value, str
88
+ ):
89
+ result[kw.arg] = kw.value.value
99
90
  elif isinstance(kw.value, ast.List):
100
91
  result[kw.arg] = [
101
- elt.s for elt in kw.value.elts if isinstance(elt, ast.Str)
92
+ elt.value
93
+ for elt in kw.value.elts
94
+ if isinstance(elt, ast.Constant) and isinstance(elt.value, str)
102
95
  ]
103
96
  return result
104
97
  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 not tbl or not isinstance(tbl, dict):
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,17 @@ 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
- values = [i.value for i in items]
100
- sorted_items = [
101
- x
102
- for _, x in sorted(zip([v.lower() for v in values], items), key=lambda t: t[0])
103
- ]
104
-
105
- if items == sorted_items:
99
+ keys = [i.value.lower() for i in items]
100
+ sorted_keys = sorted(keys)
101
+ if keys == sorted_keys:
106
102
  return False
107
103
 
108
- multiline_flag = getattr(arr, "multiline", None)
104
+ sorted_items = sorted(items, key=lambda i: i.value.lower())
105
+ multiline_flag = arr._multiline
109
106
  # Clear and re-append to preserve tomlkit item identity
110
- while len(arr):
107
+ while arr:
111
108
  arr.pop()
112
109
  for item in sorted_items:
113
110
  arr.append(item)
114
- if multiline_flag is not None:
115
- arr.multiline(multiline_flag)
111
+ arr.multiline(multiline_flag)
116
112
  return True
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import ast
4
+ import re
3
5
  from typing import TYPE_CHECKING
4
6
 
5
7
  from wexample_helpers.decorator.base_class import base_class
@@ -9,6 +11,10 @@ from .abstract_python_file_content_option import AbstractPythonFileContentOption
9
11
  if TYPE_CHECKING:
10
12
  from wexample_filestate.const.types_state_items import TargetFileOrDirectoryType
11
13
 
14
+ # PEP 263 encoding cookie matcher. Compiled once at import time, reused
15
+ # across every file the rectifier touches.
16
+ _ENCODING_COOKIE_RE = re.compile(r"^#.*coding[:=]\s*([-_.a-zA-Z0-9]+)")
17
+
12
18
 
13
19
  @base_class
14
20
  class AddFutureAnnotationsOption(AbstractPythonFileContentOption):
@@ -23,9 +29,6 @@ class AddFutureAnnotationsOption(AbstractPythonFileContentOption):
23
29
  if "from __future__ import annotations" in src:
24
30
  return src
25
31
 
26
- import ast
27
- import re
28
-
29
32
  lines = src.splitlines(keepends=True)
30
33
 
31
34
  # Detect shebang and encoding cookie positions
@@ -33,9 +36,8 @@ class AddFutureAnnotationsOption(AbstractPythonFileContentOption):
33
36
  if lines and lines[0].startswith("#!"):
34
37
  idx = 1
35
38
  # Encoding cookie can be on first or second line (after shebang)
36
- enc_re = re.compile(r"^#.*coding[:=]\s*([-_.a-zA-Z0-9]+)")
37
39
  for i in range(idx, min(idx + 2, len(lines))):
38
- if enc_re.match(lines[i]):
40
+ if _ENCODING_COOKIE_RE.match(lines[i]):
39
41
  idx = i + 1
40
42
 
41
43
  # Parse to find module docstring span
@@ -45,11 +47,11 @@ class AddFutureAnnotationsOption(AbstractPythonFileContentOption):
45
47
  # If parsing fails, be conservative: insert after header block
46
48
  insert_at = idx
47
49
  else:
48
- body = getattr(tree, "body", [])
50
+ body = tree.body
49
51
  if (
50
52
  body
51
53
  and isinstance(body[0], ast.Expr)
52
- and isinstance(getattr(body[0], "value", None), ast.Constant)
54
+ and isinstance(body[0].value, ast.Constant)
53
55
  and isinstance(body[0].value.value, str)
54
56
  ):
55
57
  # Module docstring present; insert after its end_lineno
@@ -59,7 +61,7 @@ class AddFutureAnnotationsOption(AbstractPythonFileContentOption):
59
61
  insert_at = idx
60
62
 
61
63
  # lines is 0-based; insert_at is 1-based line count from AST
62
- insert_index = max(0, min(len(lines), insert_at))
64
+ insert_index = min(len(lines), insert_at)
63
65
 
64
66
  # Ensure there is a newline after the inserted import if needed
65
67
  future_line = "from __future__ import annotations\n"
@@ -70,7 +72,7 @@ class AddFutureAnnotationsOption(AbstractPythonFileContentOption):
70
72
  # If there isn't a blank line after the future import and next line is not blank, add one for readability
71
73
  j = insert_index + 1
72
74
  if j < len(lines):
73
- if lines[j].strip() != "":
75
+ if lines[j].strip():
74
76
  lines.insert(j, "\n")
75
77
 
76
78
  return "".join(lines)