crackerjack 0.21.4__tar.gz → 0.21.6__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 (88) hide show
  1. {crackerjack-0.21.4 → crackerjack-0.21.6}/PKG-INFO +1 -1
  2. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.gitignore +4 -1
  3. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/crackerjack.py +46 -3
  4. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/pyproject.toml +1 -1
  5. {crackerjack-0.21.4 → crackerjack-0.21.6}/pyproject.toml +1 -1
  6. {crackerjack-0.21.4 → crackerjack-0.21.6}/tests/test_crackerjack.py +46 -0
  7. {crackerjack-0.21.4 → crackerjack-0.21.6}/LICENSE +0 -0
  8. {crackerjack-0.21.4 → crackerjack-0.21.6}/README.md +0 -0
  9. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.libcst.codemod.yaml +0 -0
  10. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.pdm.toml +0 -0
  11. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.pre-commit-config-ai.yaml +0 -0
  12. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.pre-commit-config.yaml +0 -0
  13. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.pytest_cache/.gitignore +0 -0
  14. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.pytest_cache/CACHEDIR.TAG +0 -0
  15. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.pytest_cache/README.md +0 -0
  16. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.pytest_cache/v/cache/nodeids +0 -0
  17. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.pytest_cache/v/cache/stepwise +0 -0
  18. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/.gitignore +0 -0
  19. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
  20. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
  21. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
  22. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
  23. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
  24. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
  25. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.11.11/18187162184424859798 +0 -0
  26. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.11.12/16869036553936192448 +0 -0
  27. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.11.12/1867267426380906393 +0 -0
  28. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.11.12/4240757255861806333 +0 -0
  29. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.11.12/4441409093023629623 +0 -0
  30. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.11.13/1867267426380906393 +0 -0
  31. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.11.13/4240757255861806333 +0 -0
  32. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
  33. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
  34. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
  35. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
  36. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
  37. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
  38. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
  39. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.12.0/5056746222905752453 +0 -0
  40. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.12.1/5056746222905752453 +0 -0
  41. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
  42. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
  43. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
  44. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
  45. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
  46. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
  47. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
  48. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
  49. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
  50. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
  51. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
  52. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
  53. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
  54. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
  55. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
  56. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
  57. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
  58. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
  59. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
  60. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
  61. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
  62. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
  63. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
  64. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
  65. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
  66. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
  67. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/.ruff_cache/CACHEDIR.TAG +0 -0
  68. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/__init__.py +0 -0
  69. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/__main__.py +0 -0
  70. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/errors.py +0 -0
  71. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/interactive.py +0 -0
  72. {crackerjack-0.21.4 → crackerjack-0.21.6}/crackerjack/py313.py +0 -0
  73. {crackerjack-0.21.4 → crackerjack-0.21.6}/tests/TESTING.md +0 -0
  74. {crackerjack-0.21.4 → crackerjack-0.21.6}/tests/__init__.py +0 -0
  75. {crackerjack-0.21.4 → crackerjack-0.21.6}/tests/conftest.py +0 -0
  76. {crackerjack-0.21.4 → crackerjack-0.21.6}/tests/data/comments_sample.txt +0 -0
  77. {crackerjack-0.21.4 → crackerjack-0.21.6}/tests/data/docstrings_sample.txt +0 -0
  78. {crackerjack-0.21.4 → crackerjack-0.21.6}/tests/data/expected_comments_sample.txt +0 -0
  79. {crackerjack-0.21.4 → crackerjack-0.21.6}/tests/data/init.py +0 -0
  80. {crackerjack-0.21.4 → crackerjack-0.21.6}/tests/test_crackerjack_runner.py +0 -0
  81. {crackerjack-0.21.4 → crackerjack-0.21.6}/tests/test_errors.py +0 -0
  82. {crackerjack-0.21.4 → crackerjack-0.21.6}/tests/test_interactive.py +0 -0
  83. {crackerjack-0.21.4 → crackerjack-0.21.6}/tests/test_interactive_run.py +0 -0
  84. {crackerjack-0.21.4 → crackerjack-0.21.6}/tests/test_main.py +0 -0
  85. {crackerjack-0.21.4 → crackerjack-0.21.6}/tests/test_py313_advanced.py +0 -0
  86. {crackerjack-0.21.4 → crackerjack-0.21.6}/tests/test_py313_features.py +0 -0
  87. {crackerjack-0.21.4 → crackerjack-0.21.6}/tests/test_pytest_features.py +0 -0
  88. {crackerjack-0.21.4 → crackerjack-0.21.6}/tests/test_structured_errors.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: crackerjack
3
- Version: 0.21.4
3
+ Version: 0.21.6
4
4
  Summary: Crackerjack: code quality toolkit
5
5
  Keywords: bandit,black,creosote,mypy,pyright,pytest,refurb,ruff
6
6
  Author-Email: lesleslie <les@wedgwoodwebworks.com>
@@ -13,5 +13,8 @@
13
13
  /scratch/
14
14
  /.zencoder/
15
15
  /.benchmarks/
16
-
17
16
  **/.claude/settings.local.json
17
+ /complexipy.json
18
+ /coverage.json
19
+ /test-results.xml
20
+ /.coverage
@@ -89,23 +89,50 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
89
89
  def remove_docstrings(self, code: str) -> str:
90
90
  lines = code.split("\n")
91
91
  cleaned_lines = []
92
- docstring_state = {"in_docstring": False, "delimiter": None, "waiting": False}
93
- for line in lines:
92
+ docstring_state = {
93
+ "in_docstring": False,
94
+ "delimiter": None,
95
+ "waiting": False,
96
+ "function_indent": 0,
97
+ "removed_docstring": False,
98
+ }
99
+ for i, line in enumerate(lines):
94
100
  stripped = line.strip()
95
101
  if self._is_function_or_class_definition(stripped):
96
102
  docstring_state["waiting"] = True
103
+ docstring_state["function_indent"] = len(line) - len(line.lstrip())
104
+ docstring_state["removed_docstring"] = False
97
105
  cleaned_lines.append(line)
98
106
  continue
99
107
  if docstring_state["waiting"] and stripped:
100
108
  if self._handle_docstring_start(stripped, docstring_state):
109
+ if not docstring_state["in_docstring"]:
110
+ if self._needs_pass_statement(
111
+ lines, i + 1, docstring_state["function_indent"]
112
+ ):
113
+ pass_line = (
114
+ " " * (docstring_state["function_indent"] + 4) + "pass"
115
+ )
116
+ cleaned_lines.append(pass_line)
117
+ docstring_state["removed_docstring"] = True
101
118
  continue
102
119
  else:
103
120
  docstring_state["waiting"] = False
104
121
  if docstring_state["in_docstring"]:
105
122
  if self._handle_docstring_end(stripped, docstring_state):
123
+ if self._needs_pass_statement(
124
+ lines, i + 1, docstring_state["function_indent"]
125
+ ):
126
+ pass_line = (
127
+ " " * (docstring_state["function_indent"] + 4) + "pass"
128
+ )
129
+ cleaned_lines.append(pass_line)
130
+ docstring_state["removed_docstring"] = False
106
131
  continue
107
132
  else:
108
133
  continue
134
+ if docstring_state["removed_docstring"] and stripped:
135
+ docstring_state["removed_docstring"] = False
109
136
  cleaned_lines.append(line)
110
137
 
111
138
  return "\n".join(cleaned_lines)
@@ -137,6 +164,22 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
137
164
  state["in_docstring"] = False
138
165
  state["delimiter"] = None
139
166
  return True
167
+ return False
168
+
169
+ def _needs_pass_statement(
170
+ self, lines: list[str], start_index: int, function_indent: int
171
+ ) -> bool:
172
+ for i in range(start_index, len(lines)):
173
+ line = lines[i]
174
+ stripped = line.strip()
175
+ if not stripped:
176
+ continue
177
+ line_indent = len(line) - len(line.lstrip())
178
+ if line_indent <= function_indent:
179
+ return True
180
+ if line_indent == function_indent + 4:
181
+ return False
182
+
140
183
  return True
141
184
 
142
185
  def remove_line_comments(self, code: str) -> str:
@@ -633,7 +676,7 @@ class Crackerjack(BaseModel, arbitrary_types_allowed=True):
633
676
  )
634
677
  if result.returncode == 0:
635
678
  self.console.print("PDM installed: ✅\n")
636
- self.execute_command(["pdm", "sync"])
679
+ self.execute_command(["pdm", "lock"])
637
680
  self.console.print("Lock file updated: ✅\n")
638
681
  else:
639
682
  self.console.print(
@@ -4,7 +4,7 @@ requires = [ "pdm-backend" ]
4
4
 
5
5
  [project]
6
6
  name = "crackerjack"
7
- version = "0.21.3"
7
+ version = "0.21.5"
8
8
  description = "Crackerjack: code quality toolkit"
9
9
  readme = "README.md"
10
10
  keywords = [
@@ -6,7 +6,7 @@ requires = [
6
6
 
7
7
  [project]
8
8
  name = "crackerjack"
9
- version = "0.21.4"
9
+ version = "0.21.6"
10
10
  description = "Crackerjack: code quality toolkit"
11
11
  readme = "README.md"
12
12
  keywords = [
@@ -1091,6 +1091,7 @@ class TestCrackerjackProcess:
1091
1091
  mock_clean_file.assert_called_once_with(py_file)
1092
1092
 
1093
1093
  def test_code_cleaner_remove_docstrings(self) -> None:
1094
+ import ast
1094
1095
  from rich.console import Console
1095
1096
  from crackerjack.crackerjack import CodeCleaner
1096
1097
 
@@ -1108,6 +1109,51 @@ class TestCrackerjackProcess:
1108
1109
  assert "This is a multi-line docstring." not in cleaned_code, (
1109
1110
  f"Got: {cleaned_code!r}"
1110
1111
  )
1112
+ try:
1113
+ ast.parse(cleaned_code)
1114
+ except SyntaxError as e:
1115
+ raise AssertionError(
1116
+ f"Cleaned code is not valid Python syntax: {e}\nCode: {cleaned_code!r}"
1117
+ )
1118
+
1119
+ def test_code_cleaner_remove_docstrings_empty_functions(self) -> None:
1120
+ import ast
1121
+ from rich.console import Console
1122
+ from crackerjack.crackerjack import CodeCleaner
1123
+
1124
+ code_cleaner = CodeCleaner(console=Console())
1125
+ test_code = """
1126
+ def empty_function():
1127
+ pass
1128
+
1129
+ class TestClass:
1130
+
1131
+ def method_with_docstring_only(self):
1132
+ pass
1133
+
1134
+ def method_with_code(self):
1135
+ return True
1136
+ """
1137
+
1138
+ cleaned_code = code_cleaner.remove_docstrings(test_code)
1139
+ print(f"Cleaned code: {cleaned_code!r}")
1140
+
1141
+ assert '"""This function has only a docstring."""' not in cleaned_code
1142
+ assert '"""Class docstring."""' not in cleaned_code
1143
+ assert '"""Method with only docstring."""' not in cleaned_code
1144
+ assert '"""This method has code after docstring."""' not in cleaned_code
1145
+
1146
+ assert "def empty_function():\n pass" in cleaned_code
1147
+ assert "def method_with_docstring_only(self):\n pass" in cleaned_code
1148
+
1149
+ assert "def method_with_code(self):\n return True" in cleaned_code
1150
+
1151
+ try:
1152
+ ast.parse(cleaned_code)
1153
+ except SyntaxError as e:
1154
+ raise AssertionError(
1155
+ f"Cleaned code is not valid Python syntax: {e}\nCode: {cleaned_code!r}"
1156
+ )
1111
1157
 
1112
1158
  def test_code_cleaner_remove_line_comments(self) -> None:
1113
1159
  from pathlib import Path
File without changes
File without changes