crackerjack 0.20.13__py3-none-any.whl → 0.20.15__py3-none-any.whl

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.
crackerjack/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import typing as t
2
+
2
3
  from .crackerjack import Crackerjack, create_crackerjack_runner
3
4
  from .errors import (
4
5
  CleaningError,
crackerjack/__main__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from enum import Enum
2
+
2
3
  import typer
3
4
  from pydantic import BaseModel, field_validator
4
5
  from rich.console import Console
@@ -1,4 +1,3 @@
1
- import ast
2
1
  import re
3
2
  import subprocess
4
3
  import typing as t
@@ -7,6 +6,7 @@ from pathlib import Path
7
6
  from subprocess import CompletedProcess
8
7
  from subprocess import run as execute
9
8
  from tomllib import loads
9
+
10
10
  from pydantic import BaseModel
11
11
  from rich.console import Console
12
12
  from tomli_w import dumps
@@ -73,8 +73,8 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
73
73
  def clean_file(self, file_path: Path) -> None:
74
74
  try:
75
75
  code = file_path.read_text()
76
- code = self.remove_docstrings(code)
77
76
  code = self.remove_line_comments(code)
77
+ code = self.remove_docstrings(code)
78
78
  code = self.remove_extra_whitespace(code)
79
79
  code = self.reformat_code(code)
80
80
  file_path.write_text(code)
@@ -83,18 +83,44 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
83
83
  print(f"Error cleaning {file_path}: {e}")
84
84
 
85
85
  def remove_docstrings(self, code: str) -> str:
86
- tree = ast.parse(code)
87
- for node in ast.walk(tree):
88
- if isinstance(
89
- node, ast.FunctionDef | ast.AsyncFunctionDef | ast.ClassDef | ast.Module
90
- ):
91
- if ast.get_docstring(node):
92
- node.body = (
93
- node.body[1:]
94
- if isinstance(node.body[0], ast.Expr)
95
- else node.body
96
- )
97
- return ast.unparse(tree)
86
+ lines = code.split("\n")
87
+ cleaned_lines = []
88
+ in_docstring = False
89
+ docstring_delimiter = None
90
+ waiting_for_docstring = False
91
+ for line in lines:
92
+ stripped = line.strip()
93
+ if stripped.startswith(("def ", "class ", "async def ")):
94
+ waiting_for_docstring = True
95
+ cleaned_lines.append(line)
96
+ continue
97
+ if waiting_for_docstring and stripped:
98
+ if stripped.startswith(('"""', "'''", '"', "'")):
99
+ if stripped.startswith(('"""', "'''")):
100
+ docstring_delimiter = stripped[:3]
101
+ else:
102
+ docstring_delimiter = stripped[0]
103
+ if stripped.endswith(docstring_delimiter) and len(stripped) > len(
104
+ docstring_delimiter
105
+ ):
106
+ waiting_for_docstring = False
107
+ continue
108
+ else:
109
+ in_docstring = True
110
+ waiting_for_docstring = False
111
+ continue
112
+ else:
113
+ waiting_for_docstring = False
114
+ if in_docstring:
115
+ if docstring_delimiter and stripped.endswith(docstring_delimiter):
116
+ in_docstring = False
117
+ docstring_delimiter = None
118
+ continue
119
+ else:
120
+ continue
121
+ cleaned_lines.append(line)
122
+
123
+ return "\n".join(cleaned_lines)
98
124
 
99
125
  def remove_line_comments(self, code: str) -> str:
100
126
  lines = code.split("\n")
@@ -119,7 +145,7 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
119
145
  elif char == "#" and in_string is None:
120
146
  comment = line[i:].strip()
121
147
  if re.match(
122
- "^#\\s*(?:type:\\s*ignore|noqa|nosec|pragma:\\s*no\\s*cover|pylint:\\s*disable|mypy:\\s*ignore)",
148
+ r"^#\s*(?:type:\s*ignore(?:\[.*?\])?|noqa|nosec|pragma:\s*no\s*cover|pylint:\s*disable|mypy:\s*ignore)",
123
149
  comment,
124
150
  ):
125
151
  result.append(line[i:])
@@ -167,16 +193,14 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
167
193
  or (
168
194
  next_line.startswith("#")
169
195
  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
- )
196
+ pattern in next_line
197
+ for pattern in (
198
+ "type:",
199
+ "noqa",
200
+ "nosec",
201
+ "pragma:",
202
+ "pylint:",
203
+ "mypy:",
180
204
  )
181
205
  )
182
206
  )
crackerjack/errors.py CHANGED
@@ -2,6 +2,7 @@ import sys
2
2
  import typing as t
3
3
  from enum import Enum
4
4
  from pathlib import Path
5
+
5
6
  from rich.console import Console
6
7
  from rich.panel import Panel
7
8
 
@@ -2,6 +2,7 @@ import time
2
2
  import typing as t
3
3
  from enum import Enum, auto
4
4
  from pathlib import Path
5
+
5
6
  from rich.box import ROUNDED
6
7
  from rich.console import Console
7
8
  from rich.layout import Layout
@@ -18,6 +19,7 @@ from rich.prompt import Confirm, Prompt
18
19
  from rich.table import Table
19
20
  from rich.text import Text
20
21
  from rich.tree import Tree
22
+
21
23
  from .errors import CrackerjackError, ErrorCode, handle_error
22
24
 
23
25
 
@@ -66,10 +68,8 @@ class Task:
66
68
 
67
69
  def can_run(self) -> bool:
68
70
  return all(
69
- (
70
- dep.status in (TaskStatus.SUCCESS, TaskStatus.SKIPPED)
71
- for dep in self.dependencies
72
- )
71
+ dep.status in (TaskStatus.SUCCESS, TaskStatus.SKIPPED)
72
+ for dep in self.dependencies
73
73
  )
74
74
 
75
75
  def __str__(self) -> str:
@@ -107,11 +107,8 @@ class WorkflowManager:
107
107
 
108
108
  def all_tasks_completed(self) -> bool:
109
109
  return all(
110
- (
111
- task.status
112
- in (TaskStatus.SUCCESS, TaskStatus.FAILED, TaskStatus.SKIPPED)
113
- for task in self.tasks.values()
114
- )
110
+ task.status in (TaskStatus.SUCCESS, TaskStatus.FAILED, TaskStatus.SKIPPED)
111
+ for task in self.tasks.values()
115
112
  )
116
113
 
117
114
  def run_task(self, task: Task, func: t.Callable[[], t.Any]) -> bool:
@@ -257,7 +254,7 @@ class InteractiveCLI:
257
254
  status = "[grey]⏸️ Pending[/grey]"
258
255
  duration = task.duration
259
256
  duration_text = f"{duration:.2f}s" if duration else "-"
260
- deps = ", ".join((dep.name for dep in task.dependencies)) or "-"
257
+ deps = ", ".join(dep.name for dep in task.dependencies) or "-"
261
258
  table.add_row(task.name, status, duration_text, deps)
262
259
  return table
263
260
 
@@ -317,25 +314,19 @@ class InteractiveCLI:
317
314
  layout["details"].update(self.show_task_status(next_task))
318
315
  layout["tasks"].update(self.show_task_table())
319
316
  successful = sum(
320
- (
321
- 1
322
- for task in self.workflow.tasks.values()
323
- if task.status == TaskStatus.SUCCESS
324
- )
317
+ 1
318
+ for task in self.workflow.tasks.values()
319
+ if task.status == TaskStatus.SUCCESS
325
320
  )
326
321
  failed = sum(
327
- (
328
- 1
329
- for task in self.workflow.tasks.values()
330
- if task.status == TaskStatus.FAILED
331
- )
322
+ 1
323
+ for task in self.workflow.tasks.values()
324
+ if task.status == TaskStatus.FAILED
332
325
  )
333
326
  skipped = sum(
334
- (
335
- 1
336
- for task in self.workflow.tasks.values()
337
- if task.status == TaskStatus.SKIPPED
338
- )
327
+ 1
328
+ for task in self.workflow.tasks.values()
329
+ if task.status == TaskStatus.SKIPPED
339
330
  )
340
331
  summary = Panel(
341
332
  f"Workflow completed!\n\n[green]✅ Successful tasks: {successful}[/green]\n[red]❌ Failed tasks: {failed}[/red]\n[blue]⏩ Skipped tasks: {skipped}[/blue]",
crackerjack/py313.py CHANGED
@@ -168,7 +168,7 @@ def clean_python_code(code: str) -> str:
168
168
  continue
169
169
  case s if "#" in s and (
170
170
  not any(
171
- (skip in s for skip in ("# noqa", "# type:", "# pragma", "# skip"))
171
+ skip in s for skip in ("# noqa", "# type:", "# pragma", "# skip")
172
172
  )
173
173
  ):
174
174
  code_part = line.split("#", 1)[0].rstrip()
@@ -4,7 +4,7 @@ requires = [ "pdm-backend" ]
4
4
 
5
5
  [project]
6
6
  name = "crackerjack"
7
- version = "0.20.12"
7
+ version = "0.20.14"
8
8
  description = "Crackerjack: code quality toolkit"
9
9
  readme = "README.md"
10
10
  keywords = [
@@ -54,6 +54,7 @@ dependencies = [
54
54
  "pytest-mock>=3.14.1",
55
55
  "pytest-timeout>=2.4",
56
56
  "pytest-xdist>=3.7",
57
+ "pyyaml>=6.0.2",
57
58
  "rich>=14",
58
59
  "tomli-w>=1.2",
59
60
  "typer>=0.16",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: crackerjack
3
- Version: 0.20.13
3
+ Version: 0.20.15
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>
@@ -35,6 +35,7 @@ Requires-Dist: pytest-cov>=6.1.1
35
35
  Requires-Dist: pytest-mock>=3.14.1
36
36
  Requires-Dist: pytest-timeout>=2.4
37
37
  Requires-Dist: pytest-xdist>=3.7
38
+ Requires-Dist: pyyaml>=6.0.2
38
39
  Requires-Dist: rich>=14
39
40
  Requires-Dist: tomli-w>=1.2
40
41
  Requires-Dist: typer>=0.16
@@ -1,7 +1,7 @@
1
- crackerjack-0.20.13.dist-info/METADATA,sha256=3KcyynnFmWmncj4aGPVu5kFy5wnnPMYVf7aCyTrsOoc,26384
2
- crackerjack-0.20.13.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
- crackerjack-0.20.13.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
- crackerjack-0.20.13.dist-info/licenses/LICENSE,sha256=fDt371P6_6sCu7RyqiZH_AhT1LdN3sN1zjBtqEhDYCk,1531
1
+ crackerjack-0.20.15.dist-info/METADATA,sha256=4D46kwc6c2hBR-8JdW9pxD03lCbbH1chHMYJa3STNOI,26413
2
+ crackerjack-0.20.15.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
+ crackerjack-0.20.15.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
+ crackerjack-0.20.15.dist-info/licenses/LICENSE,sha256=fDt371P6_6sCu7RyqiZH_AhT1LdN3sN1zjBtqEhDYCk,1531
5
5
  crackerjack/.gitignore,sha256=4DYG7ZoVEHR5Tv1gQliRWmsNku5Fw2_k756cG_t12Cg,185
6
6
  crackerjack/.libcst.codemod.yaml,sha256=a8DlErRAIPV1nE6QlyXPAzTOgkB24_spl2E9hphuf5s,772
7
7
  crackerjack/.pdm.toml,sha256=dZe44HRcuxxCFESGG8SZIjmc-cGzSoyK3Hs6t4NYA8w,23
@@ -23,7 +23,7 @@ crackerjack/.ruff_cache/0.11.12/16869036553936192448,sha256=pYYUCDrYh7fPq8xkFLxv
23
23
  crackerjack/.ruff_cache/0.11.12/1867267426380906393,sha256=2w4M0Lrjd9flwuq6uJxehTbm7FVUcK5sL2sz1gS2Yvo,256
24
24
  crackerjack/.ruff_cache/0.11.12/4240757255861806333,sha256=uph5uIRG-XnF7ywAEcCxqqgIkWALPCvJFcwCgnNfTI4,77
25
25
  crackerjack/.ruff_cache/0.11.12/4441409093023629623,sha256=eHrESew3XCFJ2WqmKvtGLO1r4mY5Q_mv7yGlDmM1sSc,153
26
- crackerjack/.ruff_cache/0.11.13/1867267426380906393,sha256=KmwKiZGTVUDcLqqzw2kSoqlT6ABtaGbSBFLbNLO_MHw,256
26
+ crackerjack/.ruff_cache/0.11.13/1867267426380906393,sha256=3VLkfclRTHCDt_uDlX4Dw0IGRPdVZkfTml68T3BrjEI,256
27
27
  crackerjack/.ruff_cache/0.11.13/4240757255861806333,sha256=l35TwAYyTusgJgyePvfP4_CCllPs1sWapEiLFZw8chQ,83
28
28
  crackerjack/.ruff_cache/0.11.2/4070660268492669020,sha256=FTRTUmvj6nZw_QQBp_WHI-h3_iqRejzL39api-9wTvs,224
29
29
  crackerjack/.ruff_cache/0.11.3/9818742842212983150,sha256=U-4mT__a-OljovvAJvv5M6X7TCMa3dReLXx3kTNGgwU,224
@@ -59,11 +59,11 @@ crackerjack/.ruff_cache/0.9.3/13948373885254993391,sha256=kGhtIkzPUtKAgvlKs3D8j4
59
59
  crackerjack/.ruff_cache/0.9.9/12813592349865671909,sha256=tmr8_vhRD2OxsVuMfbJPdT9fDFX-d5tfC5U9jgziyho,224
60
60
  crackerjack/.ruff_cache/0.9.9/8843823720003377982,sha256=e4ymkXfQsUg5e_mtO34xTsaTvs1uA3_fI216Qq9qCAM,136
61
61
  crackerjack/.ruff_cache/CACHEDIR.TAG,sha256=WVMVbX4MVkpCclExbq8m-IcOZIOuIZf5FrYw5Pk-Ma4,43
62
- crackerjack/__init__.py,sha256=LyIPEfjBcqWp7UVuJ-b7cc8oiFxrQqyL8RJ7g6c17Qs,838
63
- crackerjack/__main__.py,sha256=g-vag4kDCPjbY_ik6l6Qqc5ZRVuKGN4ofVA5x3syfFI,6733
64
- crackerjack/crackerjack.py,sha256=nwsAFZwTfc4zmdMUeuhFzF7QXY0U2pC60XC5LaimiXY,25869
65
- crackerjack/errors.py,sha256=xvXO3bk0VrxQz0lysIuVOju6SxxDgf04Wne1BedLFB8,4061
66
- crackerjack/interactive.py,sha256=hNYhsLkdy956AUQ10_21pE_ZBqfV81iKK9i2EKVSY9Q,15332
67
- crackerjack/py313.py,sha256=o6ywqlRANq4I_IESPMuaOU_IEQDOBiHkApFu1klvbZ4,5892
68
- crackerjack/pyproject.toml,sha256=szylSeogkB0mlSeToFr84pk-hY_XdtgOoizGhW672V4,4880
69
- crackerjack-0.20.13.dist-info/RECORD,,
62
+ crackerjack/__init__.py,sha256=8tBSPAru_YDuPpjz05cL7pNbZjYFoRT_agGd_FWa3gY,839
63
+ crackerjack/__main__.py,sha256=A_qOog8kowRG3Z65pG11MK25t3n7eGdEldQNELurwJk,6734
64
+ crackerjack/crackerjack.py,sha256=OOw3wWeLPJ8MuSvcEjFIQNCe3dNQG2EaJInfZpGUr0I,26842
65
+ crackerjack/errors.py,sha256=QEPtVuMtKtQHuawgr1ToMaN1KbUg5h9-4mS33YB5Znk,4062
66
+ crackerjack/interactive.py,sha256=gP2Mb7jjBfR6RIlCUXh77_M-an5OXfTVXfUmnN-9ggA,15076
67
+ crackerjack/py313.py,sha256=buYE7LO11Q64ffowEhTZRFQoAGj_8sg3DTlZuv8M9eo,5890
68
+ crackerjack/pyproject.toml,sha256=7EQyIpWXpXhShafixce7WtKWOk75GdMsTBZwX9vh92Q,4901
69
+ crackerjack-0.20.15.dist-info/RECORD,,