crackerjack 0.20.12__tar.gz → 0.20.14__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 (86) hide show
  1. {crackerjack-0.20.12 → crackerjack-0.20.14}/PKG-INFO +1 -1
  2. crackerjack-0.20.14/crackerjack/.ruff_cache/0.11.13/1867267426380906393 +0 -0
  3. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/crackerjack.py +50 -3
  4. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/pyproject.toml +1 -1
  5. {crackerjack-0.20.12 → crackerjack-0.20.14}/pyproject.toml +1 -1
  6. {crackerjack-0.20.12 → crackerjack-0.20.14}/tests/data/comments_sample.txt +1 -0
  7. {crackerjack-0.20.12 → crackerjack-0.20.14}/tests/data/expected_comments_sample.txt +1 -0
  8. {crackerjack-0.20.12 → crackerjack-0.20.14}/tests/test_crackerjack.py +37 -2
  9. crackerjack-0.20.12/crackerjack/.ruff_cache/0.11.13/1867267426380906393 +0 -0
  10. {crackerjack-0.20.12 → crackerjack-0.20.14}/LICENSE +0 -0
  11. {crackerjack-0.20.12 → crackerjack-0.20.14}/README.md +0 -0
  12. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.gitignore +0 -0
  13. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.libcst.codemod.yaml +0 -0
  14. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.pdm.toml +0 -0
  15. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.pre-commit-config.yaml +0 -0
  16. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.pytest_cache/.gitignore +0 -0
  17. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.pytest_cache/CACHEDIR.TAG +0 -0
  18. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.pytest_cache/README.md +0 -0
  19. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.pytest_cache/v/cache/nodeids +0 -0
  20. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.pytest_cache/v/cache/stepwise +0 -0
  21. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/.gitignore +0 -0
  22. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
  23. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
  24. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
  25. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
  26. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
  27. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
  28. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.11.11/18187162184424859798 +0 -0
  29. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.11.12/16869036553936192448 +0 -0
  30. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.11.12/1867267426380906393 +0 -0
  31. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.11.12/4240757255861806333 +0 -0
  32. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.11.12/4441409093023629623 +0 -0
  33. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.11.13/4240757255861806333 +0 -0
  34. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
  35. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
  36. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
  37. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
  38. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
  39. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
  40. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
  41. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
  42. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
  43. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
  44. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
  45. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
  46. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
  47. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
  48. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
  49. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
  50. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
  51. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
  52. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
  53. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
  54. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
  55. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
  56. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
  57. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
  58. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
  59. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
  60. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
  61. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
  62. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
  63. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
  64. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
  65. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
  66. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
  67. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/.ruff_cache/CACHEDIR.TAG +0 -0
  68. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/__init__.py +0 -0
  69. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/__main__.py +0 -0
  70. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/errors.py +0 -0
  71. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/interactive.py +0 -0
  72. {crackerjack-0.20.12 → crackerjack-0.20.14}/crackerjack/py313.py +0 -0
  73. {crackerjack-0.20.12 → crackerjack-0.20.14}/tests/TESTING.md +0 -0
  74. {crackerjack-0.20.12 → crackerjack-0.20.14}/tests/__init__.py +0 -0
  75. {crackerjack-0.20.12 → crackerjack-0.20.14}/tests/conftest.py +0 -0
  76. {crackerjack-0.20.12 → crackerjack-0.20.14}/tests/data/docstrings_sample.txt +0 -0
  77. {crackerjack-0.20.12 → crackerjack-0.20.14}/tests/data/init.py +0 -0
  78. {crackerjack-0.20.12 → crackerjack-0.20.14}/tests/test_crackerjack_runner.py +0 -0
  79. {crackerjack-0.20.12 → crackerjack-0.20.14}/tests/test_errors.py +0 -0
  80. {crackerjack-0.20.12 → crackerjack-0.20.14}/tests/test_interactive.py +0 -0
  81. {crackerjack-0.20.12 → crackerjack-0.20.14}/tests/test_interactive_run.py +0 -0
  82. {crackerjack-0.20.12 → crackerjack-0.20.14}/tests/test_main.py +0 -0
  83. {crackerjack-0.20.12 → crackerjack-0.20.14}/tests/test_py313_advanced.py +0 -0
  84. {crackerjack-0.20.12 → crackerjack-0.20.14}/tests/test_py313_features.py +0 -0
  85. {crackerjack-0.20.12 → crackerjack-0.20.14}/tests/test_pytest_features.py +0 -0
  86. {crackerjack-0.20.12 → crackerjack-0.20.14}/tests/test_structured_errors.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: crackerjack
3
- Version: 0.20.12
3
+ Version: 0.20.14
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>
@@ -118,7 +118,10 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
118
118
  i += 1
119
119
  elif char == "#" and in_string is None:
120
120
  comment = line[i:].strip()
121
- if re.match("^#\\s*(?:type:\\s*ignore|noqa)\\b", comment):
121
+ if re.match(
122
+ "^#\\s*(?:type:\\s*ignore|noqa|nosec|pragma:\\s*no\\s*cover|pylint:\\s*disable|mypy:\\s*ignore)",
123
+ comment,
124
+ ):
122
125
  result.append(line[i:])
123
126
  break
124
127
  break
@@ -133,11 +136,55 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
133
136
  def remove_extra_whitespace(self, code: str) -> str:
134
137
  lines = code.split("\n")
135
138
  cleaned_lines = []
139
+ in_function = False
140
+ function_indent = 0
136
141
  for i, line in enumerate(lines):
137
142
  line = line.rstrip()
138
- if i > 0 and (not line) and (not cleaned_lines[-1]):
139
- continue
143
+ stripped_line = line.lstrip()
144
+ if stripped_line.startswith(("def ", "async def ")):
145
+ in_function = True
146
+ function_indent = len(line) - len(stripped_line)
147
+ elif (
148
+ in_function
149
+ and line
150
+ and (len(line) - len(stripped_line) <= function_indent)
151
+ and (not stripped_line.startswith(("@", "#")))
152
+ ):
153
+ in_function = False
154
+ function_indent = 0
155
+ if not line:
156
+ if i > 0 and cleaned_lines and (not cleaned_lines[-1]):
157
+ continue
158
+ if in_function:
159
+ next_line_idx = i + 1
160
+ if next_line_idx < len(lines):
161
+ next_line = lines[next_line_idx].strip()
162
+ if not (
163
+ next_line.startswith(
164
+ ("return", "class ", "def ", "async def ", "@")
165
+ )
166
+ or next_line in ("pass", "break", "continue", "raise")
167
+ or (
168
+ next_line.startswith("#")
169
+ and any(
170
+ (
171
+ pattern in next_line
172
+ for pattern in (
173
+ "type:",
174
+ "noqa",
175
+ "nosec",
176
+ "pragma:",
177
+ "pylint:",
178
+ "mypy:",
179
+ )
180
+ )
181
+ )
182
+ )
183
+ ):
184
+ continue
140
185
  cleaned_lines.append(line)
186
+ while cleaned_lines and (not cleaned_lines[-1]):
187
+ cleaned_lines.pop()
141
188
  return "\n".join(cleaned_lines)
142
189
 
143
190
  def reformat_code(self, code: str) -> str:
@@ -4,7 +4,7 @@ requires = [ "pdm-backend" ]
4
4
 
5
5
  [project]
6
6
  name = "crackerjack"
7
- version = "0.20.11"
7
+ version = "0.20.13"
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.20.12"
9
+ version = "0.20.14"
10
10
  description = "Crackerjack: code quality toolkit"
11
11
  readme = "README.md"
12
12
  keywords = [
@@ -4,6 +4,7 @@ def test_func():
4
4
  # Full line comment
5
5
  x = 1 # type: ignore
6
6
  y = 2 # noqa
7
+ z = subprocess.run(["rm", "-rf", user_input]) # nosec
7
8
 
8
9
 
9
10
  class AsyncRedisBytecodeCache(AsyncBytecodeCache):
@@ -3,6 +3,7 @@ def test_func():
3
3
 
4
4
  x = 1 # type: ignore
5
5
  y = 2 # noqa
6
+ z = subprocess.run(["rm", "-rf", user_input]) # nosec
6
7
 
7
8
 
8
9
  class AsyncRedisBytecodeCache(AsyncBytecodeCache):
@@ -1062,18 +1062,53 @@ class TestCrackerjackProcess:
1062
1062
  f"Cleaned code does not match expected.\nExpected:\n{expected_cleaned}\nGot:\n{cleaned_code}"
1063
1063
  )
1064
1064
 
1065
+ def test_code_cleaner_preserve_special_comments(self) -> None:
1066
+ from rich.console import Console
1067
+ from crackerjack.crackerjack import CodeCleaner
1068
+
1069
+ code_cleaner = CodeCleaner(console=Console())
1070
+ test_code = "def test_func():\n x = 1 # type: ignore\n y = 2 # noqa\n z = 3 # nosec\n a = 4 # type: ignore[arg-type]\n b = 5 # noqa: E501\n c = 6 # nosec: B101\n d = 7 # pragma: no cover\n e = 8 # pylint: disable=line-too-long\n f = 9 # mypy: ignore\n g = 10 # regular comment that should be removed\n h = 11 #type:ignore\n i = 12#noqa\n j = 13 #nosec\n return x + y + z"
1071
+ expected_code = "def test_func():\n x = 1 # type: ignore\n y = 2 # noqa\n z = 3 # nosec\n a = 4 # type: ignore[arg-type]\n b = 5 # noqa: E501\n c = 6 # nosec: B101\n d = 7 # pragma: no cover\n e = 8 # pylint: disable=line-too-long\n f = 9 # mypy: ignore\n g = 10\n h = 11 #type:ignore\n i = 12#noqa\n j = 13 #nosec\n return x + y + z"
1072
+ cleaned_code = code_cleaner.remove_line_comments(test_code)
1073
+ assert cleaned_code == expected_code, (
1074
+ f"Special comments not preserved correctly.\nExpected:\n{expected_code}\nGot:\n{cleaned_code}"
1075
+ )
1076
+
1077
+ def test_code_cleaner_special_comments_in_strings(self) -> None:
1078
+ from rich.console import Console
1079
+ from crackerjack.crackerjack import CodeCleaner
1080
+
1081
+ code_cleaner = CodeCleaner(console=Console())
1082
+ test_code = 'def test_func():\n s1 = "# type: ignore" # should keep comment outside string but preserve string\n s2 = \'# noqa\' # type: ignore\n s3 = "test" # regular comment should be removed\n return s1 + s2'
1083
+ expected_code = 'def test_func():\n s1 = "# type: ignore"\n s2 = \'# noqa\' # type: ignore\n s3 = "test"\n return s1 + s2'
1084
+ cleaned_code = code_cleaner.remove_line_comments(test_code)
1085
+ assert cleaned_code == expected_code, (
1086
+ f"String handling with special comments failed.\nExpected:\n{expected_code}\nGot:\n{cleaned_code}"
1087
+ )
1088
+
1065
1089
  def test_code_cleaner_remove_extra_whitespace(self) -> None:
1066
1090
  from rich.console import Console
1067
1091
  from crackerjack.crackerjack import CodeCleaner
1068
1092
 
1069
1093
  code_cleaner = CodeCleaner(console=Console())
1070
1094
  code_with_whitespace = (
1071
- "def test_func():\n return True\n \n \n x = 1\n "
1095
+ "def test_func():\n x = 1\n\n\n y = 2\n\n return x + y\n\n\n"
1072
1096
  )
1073
1097
  cleaned_code = code_cleaner.remove_extra_whitespace(code_with_whitespace)
1074
1098
  assert "def test_func():" in cleaned_code, f"Got: {cleaned_code!r}"
1075
- assert "return True" in cleaned_code, f"Got: {cleaned_code!r}"
1076
1099
  assert "x = 1" in cleaned_code, f"Got: {cleaned_code!r}"
1100
+ assert "y = 2" in cleaned_code, f"Got: {cleaned_code!r}"
1101
+ assert "\n\n\n" not in cleaned_code, (
1102
+ f"Triple newlines should be removed: {cleaned_code!r}"
1103
+ )
1104
+ assert "y = 2\n\n return x + y" in cleaned_code, (
1105
+ f"Should keep blank before return: {cleaned_code!r}"
1106
+ )
1107
+ code_with_classes = "class TestClass:\n\n\n def method1(self):\n pass\n\n\n def method2(self):\n return True\n\n"
1108
+ cleaned_class_code = code_cleaner.remove_extra_whitespace(code_with_classes)
1109
+ assert "class TestClass:" in cleaned_class_code
1110
+ assert "def method1(self):" in cleaned_class_code
1111
+ assert "def method2(self):" in cleaned_class_code
1077
1112
 
1078
1113
  def test_code_cleaner_reformat_code_success(
1079
1114
  self, mock_execute: MagicMock, mock_console_print: MagicMock, tmp_path: Path
File without changes
File without changes