crackerjack 0.19.7__py3-none-any.whl → 0.20.0__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/.gitignore +2 -0
- crackerjack/.pre-commit-config.yaml +0 -1
- crackerjack/.ruff_cache/0.11.12/1867267426380906393 +0 -0
- crackerjack/.ruff_cache/0.11.12/4240757255861806333 +0 -0
- crackerjack/__init__.py +38 -1
- crackerjack/__main__.py +23 -3
- crackerjack/crackerjack.py +295 -40
- crackerjack/errors.py +176 -0
- crackerjack/interactive.py +487 -0
- crackerjack/py313.py +221 -0
- crackerjack/pyproject.toml +106 -106
- {crackerjack-0.19.7.dist-info → crackerjack-0.20.0.dist-info}/METADATA +95 -3
- {crackerjack-0.19.7.dist-info → crackerjack-0.20.0.dist-info}/RECORD +16 -12
- {crackerjack-0.19.7.dist-info → crackerjack-0.20.0.dist-info}/WHEEL +0 -0
- {crackerjack-0.19.7.dist-info → crackerjack-0.20.0.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.19.7.dist-info → crackerjack-0.20.0.dist-info}/licenses/LICENSE +0 -0
crackerjack/py313.py
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
"""Python 3.13+ specific features and enhancements.
|
2
|
+
|
3
|
+
This module contains implementations that leverage the latest Python 3.13+ features.
|
4
|
+
It serves as a showcase for modern Python capabilities including:
|
5
|
+
- Pattern matching
|
6
|
+
- PEP 695 type parameter syntax
|
7
|
+
- Self type annotations
|
8
|
+
- More precise type hints
|
9
|
+
"""
|
10
|
+
|
11
|
+
import subprocess
|
12
|
+
import typing
|
13
|
+
from enum import Enum, auto
|
14
|
+
from pathlib import Path
|
15
|
+
from typing import Any, Self, TypedDict
|
16
|
+
|
17
|
+
|
18
|
+
class CommandRunner[TReturn]:
|
19
|
+
def run_command(self, cmd: list[str], **kwargs: Any) -> TReturn: ...
|
20
|
+
|
21
|
+
|
22
|
+
class CommandResult(TypedDict):
|
23
|
+
success: bool
|
24
|
+
exit_code: int
|
25
|
+
stdout: str
|
26
|
+
stderr: str
|
27
|
+
command: list[str]
|
28
|
+
duration_ms: float
|
29
|
+
|
30
|
+
|
31
|
+
def process_command_output(result: CommandResult) -> tuple[bool, str]:
|
32
|
+
match result:
|
33
|
+
case {"success": True, "stdout": stdout} if stdout.strip():
|
34
|
+
return True, stdout
|
35
|
+
|
36
|
+
case {"success": True}:
|
37
|
+
return True, "Command completed successfully with no output"
|
38
|
+
|
39
|
+
case {"success": False, "exit_code": code, "stderr": stderr} if code == 127:
|
40
|
+
return False, f"Command not found: {stderr}"
|
41
|
+
|
42
|
+
case {"success": False, "exit_code": code} if code > 0:
|
43
|
+
return False, f"Command failed with exit code {code}: {result['stderr']}"
|
44
|
+
|
45
|
+
case _:
|
46
|
+
pass
|
47
|
+
|
48
|
+
return False, "Unknown command result pattern"
|
49
|
+
|
50
|
+
|
51
|
+
class HookStatus(Enum):
|
52
|
+
SUCCESS = auto()
|
53
|
+
FAILURE = auto()
|
54
|
+
SKIPPED = auto()
|
55
|
+
ERROR = auto()
|
56
|
+
|
57
|
+
|
58
|
+
class HookResult(TypedDict):
|
59
|
+
status: HookStatus
|
60
|
+
hook_id: str
|
61
|
+
output: str
|
62
|
+
files: list[str]
|
63
|
+
|
64
|
+
|
65
|
+
def analyze_hook_result(result: HookResult) -> str:
|
66
|
+
match result:
|
67
|
+
case {"status": HookStatus.SUCCESS, "hook_id": hook_id}:
|
68
|
+
return f"✅ Hook {hook_id} passed successfully"
|
69
|
+
|
70
|
+
case {"status": HookStatus.FAILURE, "hook_id": hook_id, "output": output} if (
|
71
|
+
"fixable" in output
|
72
|
+
):
|
73
|
+
return f"🔧 Hook {hook_id} failed with fixable issues"
|
74
|
+
|
75
|
+
case {"status": HookStatus.FAILURE, "hook_id": hook_id}:
|
76
|
+
return f"❌ Hook {hook_id} failed"
|
77
|
+
|
78
|
+
case {"status": HookStatus.SKIPPED, "hook_id": hook_id}:
|
79
|
+
return f"⏩ Hook {hook_id} was skipped"
|
80
|
+
|
81
|
+
case {"status": HookStatus.ERROR, "hook_id": hook_id, "output": output}:
|
82
|
+
return f"💥 Hook {hook_id} encountered an error: {output}"
|
83
|
+
|
84
|
+
case _:
|
85
|
+
pass
|
86
|
+
|
87
|
+
return "Unknown hook result pattern"
|
88
|
+
|
89
|
+
|
90
|
+
class ModernConfigManager:
|
91
|
+
def __init__(self, config_path: Path) -> None:
|
92
|
+
self.config_path = config_path
|
93
|
+
self.config: dict[str, Any] = {}
|
94
|
+
|
95
|
+
def load(self) -> Self:
|
96
|
+
return self
|
97
|
+
|
98
|
+
def update(self, key: str, value: Any) -> Self:
|
99
|
+
self.config[key] = value
|
100
|
+
return self
|
101
|
+
|
102
|
+
def save(self) -> Self:
|
103
|
+
return self
|
104
|
+
|
105
|
+
|
106
|
+
def categorize_file(file_path: Path) -> str:
|
107
|
+
path_str = str(file_path)
|
108
|
+
name = file_path
|
109
|
+
|
110
|
+
match path_str:
|
111
|
+
case s if name.suffix == ".py" and "/tests/" in s:
|
112
|
+
return "Python Test File"
|
113
|
+
|
114
|
+
case s if name.suffix == ".py" and "__init__.py" in name.name:
|
115
|
+
return "Python Module Init"
|
116
|
+
|
117
|
+
case s if name.suffix == ".py":
|
118
|
+
return "Python Source File"
|
119
|
+
|
120
|
+
case s if name.suffix in {".md", ".rst", ".txt"}:
|
121
|
+
return "Documentation File"
|
122
|
+
|
123
|
+
case s if name.stem.startswith(".") or name.name in {
|
124
|
+
".gitignore",
|
125
|
+
".pre-commit-config.yaml",
|
126
|
+
}:
|
127
|
+
return "Configuration File"
|
128
|
+
|
129
|
+
case _:
|
130
|
+
pass
|
131
|
+
|
132
|
+
return "Unknown File Type"
|
133
|
+
|
134
|
+
|
135
|
+
def process_hook_results[T, R](
|
136
|
+
results: list[T],
|
137
|
+
success_handler: typing.Callable[[T], R],
|
138
|
+
failure_handler: typing.Callable[[T], R],
|
139
|
+
) -> list[R]:
|
140
|
+
processed_results = []
|
141
|
+
|
142
|
+
for result in results:
|
143
|
+
if isinstance(result, dict) and result.get("status") == HookStatus.SUCCESS:
|
144
|
+
processed_results.append(success_handler(result))
|
145
|
+
else:
|
146
|
+
processed_results.append(failure_handler(result))
|
147
|
+
|
148
|
+
return processed_results
|
149
|
+
|
150
|
+
|
151
|
+
class EnhancedCommandRunner:
|
152
|
+
def __init__(self, working_dir: Path | None = None) -> None:
|
153
|
+
self.working_dir = working_dir
|
154
|
+
|
155
|
+
def run(self, cmd: list[str], **kwargs: Any) -> CommandResult:
|
156
|
+
import time
|
157
|
+
|
158
|
+
start_time = time.time()
|
159
|
+
|
160
|
+
try:
|
161
|
+
process = subprocess.run(
|
162
|
+
cmd, capture_output=True, text=True, cwd=self.working_dir, **kwargs
|
163
|
+
)
|
164
|
+
|
165
|
+
duration_ms = (time.time() - start_time) * 1000
|
166
|
+
|
167
|
+
return CommandResult(
|
168
|
+
success=(process.returncode == 0),
|
169
|
+
exit_code=process.returncode,
|
170
|
+
stdout=process.stdout,
|
171
|
+
stderr=process.stderr,
|
172
|
+
command=cmd,
|
173
|
+
duration_ms=duration_ms,
|
174
|
+
)
|
175
|
+
|
176
|
+
except subprocess.SubprocessError as e:
|
177
|
+
duration_ms = (time.time() - start_time) * 1000
|
178
|
+
|
179
|
+
return CommandResult(
|
180
|
+
success=False,
|
181
|
+
exit_code=-1,
|
182
|
+
stdout="",
|
183
|
+
stderr=str(e),
|
184
|
+
command=cmd,
|
185
|
+
duration_ms=duration_ms,
|
186
|
+
)
|
187
|
+
|
188
|
+
def handle_result(self, result: CommandResult) -> tuple[bool, str]:
|
189
|
+
return process_command_output(result)
|
190
|
+
|
191
|
+
|
192
|
+
def clean_python_code(code: str) -> str:
|
193
|
+
lines = code.splitlines()
|
194
|
+
cleaned_lines = []
|
195
|
+
|
196
|
+
for line in lines:
|
197
|
+
match line.strip():
|
198
|
+
case "":
|
199
|
+
if not cleaned_lines or cleaned_lines[-1].strip():
|
200
|
+
cleaned_lines.append("")
|
201
|
+
|
202
|
+
case s if s.startswith(("import ", "from ")):
|
203
|
+
cleaned_lines.append(line)
|
204
|
+
|
205
|
+
case s if s.startswith("#"):
|
206
|
+
continue
|
207
|
+
|
208
|
+
case s if "#" in s and not any(
|
209
|
+
skip in s for skip in ("# noqa", "# type:", "# pragma", "# skip")
|
210
|
+
):
|
211
|
+
code_part = line.split("#", 1)[0].rstrip()
|
212
|
+
if code_part:
|
213
|
+
cleaned_lines.append(code_part)
|
214
|
+
|
215
|
+
case s if s.startswith('"""') or s.startswith("'''"):
|
216
|
+
continue
|
217
|
+
|
218
|
+
case _:
|
219
|
+
cleaned_lines.append(line)
|
220
|
+
|
221
|
+
return "\n".join(cleaned_lines)
|
crackerjack/pyproject.toml
CHANGED
@@ -4,59 +4,59 @@ requires = [ "pdm-backend" ]
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "crackerjack"
|
7
|
-
version = "0.19.
|
7
|
+
version = "0.19.8"
|
8
8
|
description = "Crackerjack: code quality toolkit"
|
9
9
|
readme = "README.md"
|
10
10
|
keywords = [
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
11
|
+
"bandit",
|
12
|
+
"black",
|
13
|
+
"creosote",
|
14
|
+
"mypy",
|
15
|
+
"pyright",
|
16
|
+
"pytest",
|
17
|
+
"refurb",
|
18
|
+
"ruff",
|
19
19
|
]
|
20
20
|
license.text = "BSD-3-CLAUSE"
|
21
21
|
maintainers = [
|
22
|
-
|
22
|
+
{ name = "lesleslie", email = "les@wedgwoodwebworks.com" },
|
23
23
|
]
|
24
24
|
|
25
25
|
authors = [
|
26
|
-
|
26
|
+
{ name = "lesleslie", email = "les@wedgwoodwebworks.com" },
|
27
27
|
]
|
28
28
|
requires-python = ">=3.13"
|
29
29
|
classifiers = [
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
30
|
+
"Development Status :: 4 - Beta",
|
31
|
+
"Environment :: Console",
|
32
|
+
"License :: OSI Approved :: BSD License",
|
33
|
+
"Operating System :: POSIX",
|
34
|
+
"Programming Language :: Python",
|
35
|
+
"Programming Language :: Python :: 3 :: Only",
|
36
|
+
"Programming Language :: Python :: 3.13",
|
37
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
38
|
+
"Topic :: Software Development :: Quality Assurance",
|
39
|
+
"Topic :: Software Development :: Testing",
|
40
|
+
"Topic :: Utilities",
|
41
|
+
"Typing :: Typed",
|
42
42
|
]
|
43
43
|
dependencies = [
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
44
|
+
"autotyping>=24.9",
|
45
|
+
"pdm>=2.24.2",
|
46
|
+
"pdm-bump>=0.9.12",
|
47
|
+
"pre-commit>=4.2",
|
48
|
+
"pydantic>=2.11.5",
|
49
|
+
"pytest>=8.4",
|
50
|
+
"pytest-asyncio>=1",
|
51
|
+
"pytest-benchmark>=5.1",
|
52
|
+
"pytest-cov>=6.1.1",
|
53
|
+
"pytest-mock>=3.14.1",
|
54
|
+
"pytest-timeout>=2.4",
|
55
|
+
"pytest-xdist>=3.7",
|
56
|
+
"rich>=14",
|
57
|
+
"tomli-w>=1.2",
|
58
|
+
"typer>=0.16",
|
59
|
+
"uv>=0.7.10",
|
60
60
|
]
|
61
61
|
urls.documentation = "https://github.com/lesleslie/crackerjack"
|
62
62
|
urls.homepage = "https://github.com/lesleslie/crackerjack"
|
@@ -72,28 +72,28 @@ show-fixes = true
|
|
72
72
|
output-format = "full"
|
73
73
|
format.docstring-code-format = true
|
74
74
|
lint.extend-select = [
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
75
|
+
"C901",
|
76
|
+
"D",
|
77
|
+
"F", # pyflakes
|
78
|
+
"I",
|
79
|
+
"UP", # pyupgrade (includes F-string conversion)
|
80
80
|
]
|
81
81
|
lint.ignore = [
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
82
|
+
"D100",
|
83
|
+
"D101",
|
84
|
+
"D102",
|
85
|
+
"D103",
|
86
|
+
"D104",
|
87
|
+
"D105",
|
88
|
+
"D106",
|
89
|
+
"D107",
|
90
|
+
"F821",
|
91
|
+
"UP040",
|
92
92
|
]
|
93
93
|
lint.fixable = [ "ALL" ]
|
94
94
|
lint.unfixable = [ ]
|
95
95
|
lint.isort.no-lines-before = [
|
96
|
-
|
96
|
+
"first-party",
|
97
97
|
]
|
98
98
|
lint.mccabe.max-complexity = 13
|
99
99
|
lint.pydocstyle.convention = "google"
|
@@ -105,7 +105,7 @@ ignore-words-list = "crate,uptodate,nd"
|
|
105
105
|
|
106
106
|
[tool.pyproject-fmt]
|
107
107
|
column_width = 120
|
108
|
-
indent =
|
108
|
+
indent = 4
|
109
109
|
|
110
110
|
[tool.pytest.ini_options]
|
111
111
|
# Core pytest configuration
|
@@ -118,9 +118,9 @@ python_functions = [ "test_*" ]
|
|
118
118
|
|
119
119
|
# Markers
|
120
120
|
markers = [
|
121
|
-
|
122
|
-
|
123
|
-
|
121
|
+
"unit: marks test as a unit test",
|
122
|
+
"benchmark: mark test as a benchmark (disables parallel execution)",
|
123
|
+
"integration: marks test as an integration test",
|
124
124
|
]
|
125
125
|
|
126
126
|
# Default timeout settings
|
@@ -141,40 +141,40 @@ source = [ "crackerjack" ]
|
|
141
141
|
data_file = ".coverage"
|
142
142
|
parallel = false
|
143
143
|
omit = [
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
144
|
+
"*/tests/*",
|
145
|
+
"*/site-packages/*",
|
146
|
+
"*/__pycache__/*",
|
147
|
+
"*/__init__.py",
|
148
|
+
"*/_version.py",
|
149
|
+
"*/conftest.py",
|
150
|
+
"*/test_*.py",
|
151
|
+
"*/_test.py",
|
152
152
|
]
|
153
153
|
|
154
154
|
[tool.coverage.report]
|
155
155
|
exclude_also = [
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
156
|
+
"pragma: no cover",
|
157
|
+
"def __repr__",
|
158
|
+
"raise NotImplementedError",
|
159
|
+
"if __name__ == .__main__.:",
|
160
|
+
"pass",
|
161
|
+
"raise ImportError",
|
162
|
+
"except ImportError",
|
163
|
+
"def __str__",
|
164
|
+
"@abstractmethod",
|
165
165
|
]
|
166
166
|
ignore_errors = false
|
167
167
|
|
168
168
|
[tool.pyright]
|
169
169
|
verboseOutput = true
|
170
170
|
include = [
|
171
|
-
|
171
|
+
"crackerjack",
|
172
172
|
]
|
173
173
|
exclude = [
|
174
|
-
|
174
|
+
"scratch",
|
175
175
|
]
|
176
176
|
extraPaths = [
|
177
|
-
|
177
|
+
".venv/lib/python3.13/site-packages/",
|
178
178
|
]
|
179
179
|
typeCheckingMode = "strict"
|
180
180
|
reportMissingTypeStubs = false
|
@@ -196,26 +196,26 @@ ignore_names = [ "cls" ]
|
|
196
196
|
|
197
197
|
[tool.creosote]
|
198
198
|
paths = [
|
199
|
-
|
199
|
+
"crackerjack",
|
200
200
|
]
|
201
201
|
deps-file = "pyproject.toml"
|
202
202
|
exclude-deps = [
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
203
|
+
"pdm-bump",
|
204
|
+
"autotyping",
|
205
|
+
"pre-commit",
|
206
|
+
"pytest",
|
207
|
+
"pytest-asyncio",
|
208
|
+
"pytest-cov",
|
209
|
+
"pytest-mock",
|
210
|
+
"pytest-xdist",
|
211
|
+
"pytest-benchmark",
|
212
|
+
"pdm",
|
213
|
+
"pyfiglet",
|
214
|
+
"pyyaml",
|
215
|
+
"uv",
|
216
|
+
"tomli-w",
|
217
|
+
"google-crc32c",
|
218
|
+
"pytest-timeout",
|
219
219
|
]
|
220
220
|
|
221
221
|
[tool.refurb]
|
@@ -224,16 +224,16 @@ quiet = true
|
|
224
224
|
|
225
225
|
[tool.bandit]
|
226
226
|
target = [
|
227
|
-
|
227
|
+
"crackerjack",
|
228
228
|
]
|
229
229
|
skips = [
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
230
|
+
"B101",
|
231
|
+
"B301",
|
232
|
+
"B311",
|
233
|
+
"B403",
|
234
|
+
"B404",
|
235
|
+
"B602",
|
236
|
+
"B603",
|
237
|
+
"B607",
|
238
|
+
"B704",
|
239
239
|
]
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: crackerjack
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.20.0
|
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>
|
@@ -82,7 +82,7 @@ Crackerjack integrates powerful tools like Ruff, PDM, pre-commit, pytest, and mo
|
|
82
82
|
### Quick Start
|
83
83
|
|
84
84
|
If you're new to Crackerjack, follow these steps:
|
85
|
-
1. **Install Python 3.13:** Ensure you have Python 3.13 installed.
|
85
|
+
1. **Install Python 3.13:** Ensure you have Python 3.13 or higher installed.
|
86
86
|
2. **Install PDM:**
|
87
87
|
```
|
88
88
|
pipx install pdm
|
@@ -98,6 +98,11 @@ If you're new to Crackerjack, follow these steps:
|
|
98
98
|
python -m crackerjack
|
99
99
|
```
|
100
100
|
|
101
|
+
Or use the interactive Rich UI:
|
102
|
+
```
|
103
|
+
python -m crackerjack --rich-ui
|
104
|
+
```
|
105
|
+
|
101
106
|
---
|
102
107
|
|
103
108
|
## The Crackerjack Philosophy
|
@@ -137,9 +142,12 @@ Crackerjack is built on the following core principles:
|
|
137
142
|
|
138
143
|
### Developer Experience
|
139
144
|
- **Command-Line Interface:** Simple, intuitive CLI with comprehensive options
|
145
|
+
- **Interactive Rich UI:** Visual workflow with real-time task tracking, progress visualization, and interactive prompts
|
146
|
+
- **Structured Error Handling:** Clear error messages with error codes, detailed explanations, and recovery suggestions
|
140
147
|
- **Programmatic API:** Can be integrated into your own Python scripts and workflows
|
141
148
|
- **AI Agent Integration:** Structured output format for integration with AI assistants, with complete style rules available in [RULES.md](RULES.md) for AI tool customization
|
142
149
|
- **Verbose Mode:** Detailed output for debugging and understanding what's happening
|
150
|
+
- **Python 3.13+ Features:** Leverages the latest Python language features including PEP 695 type parameter syntax, Self type annotations, and structural pattern matching
|
143
151
|
|
144
152
|
## Pre-commit Hooks
|
145
153
|
|
@@ -217,7 +225,7 @@ python -m crackerjack -t --benchmark-regression --benchmark-regression-threshold
|
|
217
225
|
|
218
226
|
## Installation
|
219
227
|
|
220
|
-
1. **Python:** Ensure you have Python 3.13 installed.
|
228
|
+
1. **Python:** Ensure you have Python 3.13 or higher installed.
|
221
229
|
2. **PDM:** Install [PDM](https://pdm.fming.dev/) using `pipx`:
|
222
230
|
|
223
231
|
```
|
@@ -231,6 +239,11 @@ python -m crackerjack -t --benchmark-regression --benchmark-regression-threshold
|
|
231
239
|
python -m crackerjack
|
232
240
|
```
|
233
241
|
|
242
|
+
Or with the interactive Rich UI:
|
243
|
+
```
|
244
|
+
python -m crackerjack --rich-ui
|
245
|
+
```
|
246
|
+
|
234
247
|
## Usage
|
235
248
|
|
236
249
|
### Command Line
|
@@ -312,6 +325,7 @@ runner.process(MyOptions())
|
|
312
325
|
- `--benchmark-regression`: Fail tests if benchmarks regress beyond threshold.
|
313
326
|
- `--benchmark-regression-threshold`: Set threshold percentage for benchmark regression (default 5.0%).
|
314
327
|
- `-a`, `--all`: Run with `-x -t -p <micro|minor|major> -c` development options.
|
328
|
+
- `--rich-ui`: Enable the interactive Rich UI for a more user-friendly experience with visual progress tracking and interactive prompts.
|
315
329
|
- `--ai-agent`: Enable AI agent mode with structured output (see [AI Agent Integration](#ai-agent-integration)).
|
316
330
|
- `--help`: Display help.
|
317
331
|
|
@@ -387,6 +401,11 @@ runner.process(MyOptions())
|
|
387
401
|
python -m crackerjack -i
|
388
402
|
```
|
389
403
|
|
404
|
+
- **Rich Interactive Mode** - Run with the interactive Rich UI:
|
405
|
+
```bash
|
406
|
+
python -m crackerjack --rich-ui
|
407
|
+
```
|
408
|
+
|
390
409
|
- **AI Integration** - Run with structured output for AI tools:
|
391
410
|
```bash
|
392
411
|
python -m crackerjack --ai-agent --test
|
@@ -413,6 +432,79 @@ python -m crackerjack --ai-agent --test
|
|
413
432
|
|
414
433
|
For detailed information about using Crackerjack with AI agents, including the structured output format and programmatic usage, see [README-AI-AGENT.md](README-AI-AGENT.md).
|
415
434
|
|
435
|
+
## Interactive Rich UI
|
436
|
+
|
437
|
+
Crackerjack now offers an enhanced interactive experience through its Rich UI:
|
438
|
+
|
439
|
+
- **Visual Workflow:** See a visual representation of the entire task workflow with dependencies
|
440
|
+
- **Real-time Progress:** Track task progress with interactive progress bars and status indicators
|
441
|
+
- **Task Management:** Confirm tasks before execution and view detailed status information
|
442
|
+
- **Error Visualization:** Errors are presented in a structured, easy-to-understand format with recovery suggestions
|
443
|
+
- **File Selection:** Interactive file browser for operations that require selecting files
|
444
|
+
|
445
|
+
To use the Rich UI, run Crackerjack with the `--rich-ui` flag:
|
446
|
+
|
447
|
+
```bash
|
448
|
+
python -m crackerjack --rich-ui
|
449
|
+
```
|
450
|
+
|
451
|
+
This launches an interactive terminal interface where you can:
|
452
|
+
1. View all available tasks and their dependencies
|
453
|
+
2. Confirm each task before execution
|
454
|
+
3. Get detailed status information for running tasks
|
455
|
+
4. See a summary of completed, failed, and skipped tasks
|
456
|
+
5. Visualize error details with recovery suggestions
|
457
|
+
|
458
|
+
## Structured Error Handling
|
459
|
+
|
460
|
+
Crackerjack implements a comprehensive error handling system that provides:
|
461
|
+
|
462
|
+
- **Error Categories:** Errors are categorized by type (configuration, execution, testing, etc.)
|
463
|
+
- **Error Codes:** Each error has a unique numeric code for easy reference
|
464
|
+
- **Detailed Messages:** Clear, descriptive messages explain what went wrong
|
465
|
+
- **Recovery Suggestions:** Where possible, errors include recovery suggestions to help resolve issues
|
466
|
+
- **Rich Formatting:** Errors are presented with clear visual formatting (when using Rich UI or verbose mode)
|
467
|
+
|
468
|
+
Error types include:
|
469
|
+
- Configuration errors (1000-1999)
|
470
|
+
- Execution errors (2000-2999)
|
471
|
+
- Test errors (3000-3999)
|
472
|
+
- Publishing errors (4000-4999)
|
473
|
+
- Git errors (5000-5999)
|
474
|
+
- File operation errors (6000-6999)
|
475
|
+
- Code cleaning errors (7000-7999)
|
476
|
+
- Generic errors (9000-9999)
|
477
|
+
|
478
|
+
Use the `-v` or `--verbose` flag to see more detailed error information:
|
479
|
+
|
480
|
+
```bash
|
481
|
+
python -m crackerjack -v
|
482
|
+
```
|
483
|
+
|
484
|
+
For the most comprehensive error details with visual formatting, combine verbose mode with the Rich UI:
|
485
|
+
|
486
|
+
```bash
|
487
|
+
python -m crackerjack --rich-ui -v
|
488
|
+
```
|
489
|
+
|
490
|
+
## Python 3.13+ Features
|
491
|
+
|
492
|
+
Crackerjack is designed to leverage the latest Python 3.13+ language features:
|
493
|
+
|
494
|
+
- **Type Parameter Syntax (PEP 695):** Uses the new, more concise syntax for generic type parameters
|
495
|
+
- **Self Type:** Leverages the `Self` type for better method chaining and builder patterns
|
496
|
+
- **Structural Pattern Matching:** Uses pattern matching for cleaner code, especially in configuration and command processing
|
497
|
+
- **Enhanced Type Hints:** More precise type hints with union types using the pipe operator
|
498
|
+
- **Modern Dictionary Patterns:** Leverages structural pattern matching with dictionaries for cleaner data handling
|
499
|
+
|
500
|
+
These modern Python features contribute to:
|
501
|
+
- More readable and maintainable code
|
502
|
+
- Better static type checking with tools like pyright
|
503
|
+
- Cleaner, more concise implementations
|
504
|
+
- Enhanced error handling and pattern recognition
|
505
|
+
|
506
|
+
Crackerjack provides examples of these features in action, serving as a reference for modern Python development practices.
|
507
|
+
|
416
508
|
## Contributing
|
417
509
|
|
418
510
|
Crackerjack is an evolving project. Contributions are welcome! Please open a pull request or issue.
|