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.
Files changed (90) hide show
  1. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/PKG-INFO +6 -6
  2. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/README.md +3 -3
  3. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/pyproject.toml +3 -3
  4. {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
  5. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/helpers/package.py +10 -5
  6. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/helpers/toml.py +5 -10
  7. {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
  8. {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
  9. {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
  10. {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
  11. {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
  12. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/relocate_imports_option.py +8 -18
  13. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python_option.py +1 -4
  14. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_attrs_utils.py +18 -19
  15. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_blank_lines_utils.py +5 -4
  16. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_class_attributes_utils.py +3 -1
  17. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_class_docstring_utils.py +2 -3
  18. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_class_methods_utils.py +6 -13
  19. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_constants_utils.py +8 -9
  20. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_docstring_utils.py +1 -1
  21. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_functions_utils.py +1 -1
  22. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_iterable_utils.py +3 -9
  23. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_main_guard_utils.py +1 -5
  24. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_module_metadata_utils.py +4 -4
  25. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/python_type_checking_utils.py +5 -5
  26. {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
  27. {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
  28. {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
  29. {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
  30. {wexample_filestate_python-6.8.7/tests/tests → wexample_filestate_python-7.0.0/tests/helpers}/__init__.py +0 -0
  31. wexample_filestate_python-7.0.0/tests/helpers/test_package.py +94 -0
  32. wexample_filestate_python-7.0.0/tests/helpers/test_toml.py +149 -0
  33. wexample_filestate_python-7.0.0/tests/wexample_tests/__init__.py +0 -0
  34. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/__init__.py +0 -0
  35. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/__pycache__/__init__.py +0 -0
  36. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/common/__init__.py +0 -0
  37. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/common/__pycache__/__init__.py +0 -0
  38. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/common/pipy_gateway.py +0 -0
  39. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/config_option/__init__.py +0 -0
  40. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/config_option/__pycache__/__init__.py +0 -0
  41. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/config_option/mixin/__init__.py +0 -0
  42. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/config_option/mixin/__pycache__/__init__.py +0 -0
  43. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/config_value/__init__.py +0 -0
  44. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/config_value/__pycache__/__init__.py +0 -0
  45. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/config_value/python_config_value.py +0 -0
  46. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/const/__init__.py +0 -0
  47. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/const/__pycache__/__init__.py +0 -0
  48. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/const/name_pattern.py +0 -0
  49. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/const/path.py +0 -0
  50. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/const/python_file.py +0 -0
  51. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/event/__init__.py +0 -0
  52. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/event/src/__init__.py +0 -0
  53. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/event/src/wexample_event/__init__.py +0 -0
  54. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/file/__init__.py +0 -0
  55. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/file/__pycache__/__init__.py +0 -0
  56. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/file/python_file.py +0 -0
  57. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/helpers/__init__.py +0 -0
  58. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/helpers/__pycache__/__init__.py +0 -0
  59. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/helpers/python/__init__.py +0 -0
  60. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/__init__.py +0 -0
  61. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/__pycache__/__init__.py +0 -0
  62. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/__init__.py +0 -0
  63. {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
  64. {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
  65. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/fix_attrs_option.py +0 -0
  66. {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
  67. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/format_option.py +0 -0
  68. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/fstringify_option.py +0 -0
  69. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/modernize_typing_option.py +0 -0
  70. {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
  71. {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
  72. {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
  73. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/order_constants_option.py +0 -0
  74. {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
  75. {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
  76. {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
  77. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/remove_unused_option.py +0 -0
  78. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/sort_imports_option.py +0 -0
  79. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/option/python/unquote_annotations_option.py +0 -0
  80. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/options_provider/__init__.py +0 -0
  81. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/options_provider/__pycache__/__init__.py +0 -0
  82. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/options_provider/python_options_provider.py +0 -0
  83. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/py.typed +0 -0
  84. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/__init__.py +0 -0
  85. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/__pycache__/__init__.py +0 -0
  86. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/cst_cache.py +0 -0
  87. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/relocate_imports/__init__.py +0 -0
  88. {wexample_filestate_python-6.8.7 → wexample_filestate_python-7.0.0}/src/wexample_filestate_python/utils/relocate_imports/__pycache__/__init__.py +0 -0
  89. {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
  90. {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: 6.8.7
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.3.0
23
- Requires-Dist: wexample-filestate>=12.1.0
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: 6.8.7
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.3.0
122
- - wexample-filestate: >=12.1.0
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: 6.8.7
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.3.0
94
- - wexample-filestate: >=12.1.0
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 = "6.8.7"
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.3.0",
32
- "wexample-filestate>=12.1.0",
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 packages_root.iterdir():
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 packages_root.iterdir():
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.Str):
98
- result[kw.arg] = kw.value.s
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.s for elt in kw.value.elts if isinstance(elt, ast.Str)
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 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,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
- 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
- ]
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 = getattr(arr, "multiline", None)
104
+ multiline_flag = arr._multiline
109
105
  # Clear and re-append to preserve tomlkit item identity
110
- while len(arr):
106
+ while arr:
111
107
  arr.pop()
112
108
  for item in sorted_items:
113
109
  arr.append(item)
114
- if multiline_flag is not None:
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.BaseFunctionDef) -> str | None:
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
- # Visit only the immediate body of the function
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
- if isinstance(func_node, cst.FunctionDef):
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 len(docstring_node.body) > 0 and isinstance(
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=lambda n: n.lower())
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 = current_indices == list(
45
- range(current_indices[0], current_indices[0] + len(current_indices))
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
- (set(runtime_local_final) - runtime_used_anywhere)
161
- | set(type_only_names)
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
- n for n in imported_value_names if n not in names_to_remove_from_module
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
- dict_value = {}
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
- 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)
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
- for arg in call.args:
80
- if isinstance(arg.keyword, cst.Name) and arg.keyword.value == "kw_only":
81
- return True
82
- return False
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
- # Check for @attr.s
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
- # Count blank leading_lines
146
- for line in next_stmt.leading_lines:
147
- if isinstance(line, cst.EmptyLine) and line.comment is None:
148
- blank_count += 1
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 _is_special_attribute(node):
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
- body_elems = list(classdef.body.body)
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('"""') or 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: (m["order"][0], m["order"][1]))
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
- order = []
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=lambda n: n.lower()):
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(list(body), i)
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: list[tuple[str, cst.SimpleStatementLine]] = []
191
- for node in nodes:
192
- name = _get_simple_assignment_name(node)
193
- if name is None:
194
- # Shouldn't happen given precondition
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 [n for _, n in sorted_pairs] == [n for _, n in pairs]:
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] + list(docstring_node.body[1:])
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 = sorted(set(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: list[str] = []
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 lines[i].lstrip().startswith("#"):
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
- indices: list[int] = []
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
- res: list[tuple[int, cst.If]] = []
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([i for i, _, _ in found], reverse=True)
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 = -1
96
- for idx, _if in find_type_checking_blocks(module):
97
- last_type_checking = max(last_type_checking, idx)
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
- results: list[tuple[int, cst.If]] = []
9
- for i, stmt in enumerate(module.body):
10
- if isinstance(stmt, cst.If) and _is_type_checking_test(stmt.test):
11
- results.append((i, stmt))
12
- return results
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: