crackerjack 0.21.6__py3-none-any.whl → 0.21.8__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/.ruff_cache/0.12.1/5056746222905752453 +0 -0
- crackerjack/crackerjack.py +223 -68
- crackerjack/interactive.py +0 -8
- crackerjack/pyproject.toml +1 -1
- {crackerjack-0.21.6.dist-info → crackerjack-0.21.8.dist-info}/METADATA +8 -8
- {crackerjack-0.21.6.dist-info → crackerjack-0.21.8.dist-info}/RECORD +9 -9
- {crackerjack-0.21.6.dist-info → crackerjack-0.21.8.dist-info}/WHEEL +0 -0
- {crackerjack-0.21.6.dist-info → crackerjack-0.21.8.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.21.6.dist-info → crackerjack-0.21.8.dist-info}/licenses/LICENSE +0 -0
Binary file
|
crackerjack/crackerjack.py
CHANGED
@@ -86,55 +86,96 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
86
86
|
except Exception as e:
|
87
87
|
print(f"Error cleaning {file_path}: {e}")
|
88
88
|
|
89
|
-
def
|
90
|
-
|
91
|
-
cleaned_lines = []
|
92
|
-
docstring_state = {
|
89
|
+
def _initialize_docstring_state(self) -> dict[str, t.Any]:
|
90
|
+
return {
|
93
91
|
"in_docstring": False,
|
94
92
|
"delimiter": None,
|
95
93
|
"waiting": False,
|
96
94
|
"function_indent": 0,
|
97
95
|
"removed_docstring": False,
|
96
|
+
"in_multiline_def": False,
|
98
97
|
}
|
98
|
+
|
99
|
+
def _handle_function_definition(
|
100
|
+
self, line: str, stripped: str, state: dict[str, t.Any]
|
101
|
+
) -> bool:
|
102
|
+
if self._is_function_or_class_definition(stripped):
|
103
|
+
state["waiting"] = True
|
104
|
+
state["function_indent"] = len(line) - len(line.lstrip())
|
105
|
+
state["removed_docstring"] = False
|
106
|
+
state["in_multiline_def"] = not stripped.endswith(":")
|
107
|
+
return True
|
108
|
+
return False
|
109
|
+
|
110
|
+
def _handle_multiline_definition(
|
111
|
+
self, line: str, stripped: str, state: dict[str, t.Any]
|
112
|
+
) -> bool:
|
113
|
+
if state["in_multiline_def"]:
|
114
|
+
if stripped.endswith(":"):
|
115
|
+
state["in_multiline_def"] = False
|
116
|
+
return True
|
117
|
+
return False
|
118
|
+
|
119
|
+
def _handle_waiting_docstring(
|
120
|
+
self, lines: list[str], i: int, stripped: str, state: dict[str, t.Any]
|
121
|
+
) -> tuple[bool, str | None]:
|
122
|
+
if state["waiting"] and stripped:
|
123
|
+
if self._handle_docstring_start(stripped, state):
|
124
|
+
pass_line = None
|
125
|
+
if not state["in_docstring"]:
|
126
|
+
function_indent: int = state["function_indent"]
|
127
|
+
if self._needs_pass_statement(lines, i + 1, function_indent):
|
128
|
+
pass_line = " " * (function_indent + 4) + "pass"
|
129
|
+
state["removed_docstring"] = True
|
130
|
+
return True, pass_line
|
131
|
+
else:
|
132
|
+
state["waiting"] = False
|
133
|
+
return False, None
|
134
|
+
|
135
|
+
def _handle_docstring_content(
|
136
|
+
self, lines: list[str], i: int, stripped: str, state: dict[str, t.Any]
|
137
|
+
) -> tuple[bool, str | None]:
|
138
|
+
if state["in_docstring"]:
|
139
|
+
if self._handle_docstring_end(stripped, state):
|
140
|
+
pass_line = None
|
141
|
+
function_indent: int = state["function_indent"]
|
142
|
+
if self._needs_pass_statement(lines, i + 1, function_indent):
|
143
|
+
pass_line = " " * (function_indent + 4) + "pass"
|
144
|
+
state["removed_docstring"] = False
|
145
|
+
return True, pass_line
|
146
|
+
else:
|
147
|
+
return True, None
|
148
|
+
return False, None
|
149
|
+
|
150
|
+
def _process_line(
|
151
|
+
self, lines: list[str], i: int, line: str, state: dict[str, t.Any]
|
152
|
+
) -> tuple[bool, str | None]:
|
153
|
+
stripped = line.strip()
|
154
|
+
if self._handle_function_definition(line, stripped, state):
|
155
|
+
return True, line
|
156
|
+
if self._handle_multiline_definition(line, stripped, state):
|
157
|
+
return True, line
|
158
|
+
handled, pass_line = self._handle_waiting_docstring(lines, i, stripped, state)
|
159
|
+
if handled:
|
160
|
+
return True, pass_line
|
161
|
+
handled, pass_line = self._handle_docstring_content(lines, i, stripped, state)
|
162
|
+
if handled:
|
163
|
+
return True, pass_line
|
164
|
+
if state["removed_docstring"] and stripped:
|
165
|
+
state["removed_docstring"] = False
|
166
|
+
return False, line
|
167
|
+
|
168
|
+
def remove_docstrings(self, code: str) -> str:
|
169
|
+
lines = code.split("\n")
|
170
|
+
cleaned_lines = []
|
171
|
+
docstring_state = self._initialize_docstring_state()
|
99
172
|
for i, line in enumerate(lines):
|
100
|
-
|
101
|
-
if
|
102
|
-
|
103
|
-
|
104
|
-
|
173
|
+
handled, result_line = self._process_line(lines, i, line, docstring_state)
|
174
|
+
if handled:
|
175
|
+
if result_line is not None:
|
176
|
+
cleaned_lines.append(result_line)
|
177
|
+
else:
|
105
178
|
cleaned_lines.append(line)
|
106
|
-
continue
|
107
|
-
if docstring_state["waiting"] and stripped:
|
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
|
118
|
-
continue
|
119
|
-
else:
|
120
|
-
docstring_state["waiting"] = False
|
121
|
-
if docstring_state["in_docstring"]:
|
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
|
131
|
-
continue
|
132
|
-
else:
|
133
|
-
continue
|
134
|
-
if docstring_state["removed_docstring"] and stripped:
|
135
|
-
docstring_state["removed_docstring"] = False
|
136
|
-
cleaned_lines.append(line)
|
137
|
-
|
138
179
|
return "\n".join(cleaned_lines)
|
139
180
|
|
140
181
|
def _is_function_or_class_definition(self, stripped_line: str) -> bool:
|
@@ -177,9 +218,8 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
177
218
|
line_indent = len(line) - len(line.lstrip())
|
178
219
|
if line_indent <= function_indent:
|
179
220
|
return True
|
180
|
-
if line_indent
|
221
|
+
if line_indent > function_indent:
|
181
222
|
return False
|
182
|
-
|
183
223
|
return True
|
184
224
|
|
185
225
|
def remove_line_comments(self, code: str) -> str:
|
@@ -192,7 +232,6 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
192
232
|
cleaned_line = self._process_line_for_comments(line)
|
193
233
|
if cleaned_line or not line.strip():
|
194
234
|
cleaned_lines.append(cleaned_line or line)
|
195
|
-
|
196
235
|
return "\n".join(cleaned_lines)
|
197
236
|
|
198
237
|
def _process_line_for_comments(self, line: str) -> str:
|
@@ -205,7 +244,6 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
205
244
|
break
|
206
245
|
else:
|
207
246
|
result.append(char)
|
208
|
-
|
209
247
|
return "".join(result).rstrip()
|
210
248
|
|
211
249
|
def _handle_string_character(
|
@@ -216,18 +254,14 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
216
254
|
string_state: dict[str, t.Any],
|
217
255
|
result: list[str],
|
218
256
|
) -> bool:
|
219
|
-
"""Handle string quote characters. Returns True if character was handled."""
|
220
257
|
if char not in ("'", '"'):
|
221
258
|
return False
|
222
|
-
|
223
259
|
if index > 0 and line[index - 1] == "\\":
|
224
260
|
return False
|
225
|
-
|
226
261
|
if string_state["in_string"] is None:
|
227
262
|
string_state["in_string"] = char
|
228
263
|
elif string_state["in_string"] == char:
|
229
264
|
string_state["in_string"] = None
|
230
|
-
|
231
265
|
result.append(char)
|
232
266
|
return True
|
233
267
|
|
@@ -239,14 +273,11 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
239
273
|
string_state: dict[str, t.Any],
|
240
274
|
result: list[str],
|
241
275
|
) -> bool:
|
242
|
-
"""Handle comment character. Returns True if comment was found."""
|
243
276
|
if char != "#" or string_state["in_string"] is not None:
|
244
277
|
return False
|
245
|
-
|
246
278
|
comment = line[index:].strip()
|
247
279
|
if self._is_special_comment_line(comment):
|
248
280
|
result.append(line[index:])
|
249
|
-
|
250
281
|
return True
|
251
282
|
|
252
283
|
def _is_special_comment_line(self, comment: str) -> bool:
|
@@ -260,23 +291,23 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
260
291
|
lines = code.split("\n")
|
261
292
|
cleaned_lines = []
|
262
293
|
function_tracker = {"in_function": False, "function_indent": 0}
|
294
|
+
import_tracker = {"in_imports": False, "last_import_type": None}
|
263
295
|
for i, line in enumerate(lines):
|
264
296
|
line = line.rstrip()
|
265
297
|
stripped_line = line.lstrip()
|
266
298
|
self._update_function_state(line, stripped_line, function_tracker)
|
299
|
+
self._update_import_state(line, stripped_line, import_tracker)
|
267
300
|
if not line:
|
268
301
|
if self._should_skip_empty_line(
|
269
|
-
i, lines, cleaned_lines, function_tracker
|
302
|
+
i, lines, cleaned_lines, function_tracker, import_tracker
|
270
303
|
):
|
271
304
|
continue
|
272
305
|
cleaned_lines.append(line)
|
273
|
-
|
274
306
|
return "\n".join(self._remove_trailing_empty_lines(cleaned_lines))
|
275
307
|
|
276
308
|
def _update_function_state(
|
277
309
|
self, line: str, stripped_line: str, function_tracker: dict[str, t.Any]
|
278
310
|
) -> None:
|
279
|
-
"""Update function tracking state based on current line."""
|
280
311
|
if stripped_line.startswith(("def ", "async def ")):
|
281
312
|
function_tracker["in_function"] = True
|
282
313
|
function_tracker["function_indent"] = len(line) - len(stripped_line)
|
@@ -284,10 +315,116 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
284
315
|
function_tracker["in_function"] = False
|
285
316
|
function_tracker["function_indent"] = 0
|
286
317
|
|
318
|
+
def _update_import_state(
|
319
|
+
self, line: str, stripped_line: str, import_tracker: dict[str, t.Any]
|
320
|
+
) -> None:
|
321
|
+
if stripped_line.startswith(("import ", "from ")):
|
322
|
+
import_tracker["in_imports"] = True
|
323
|
+
if self._is_stdlib_import(stripped_line):
|
324
|
+
current_type = "stdlib"
|
325
|
+
elif self._is_local_import(stripped_line):
|
326
|
+
current_type = "local"
|
327
|
+
else:
|
328
|
+
current_type = "third_party"
|
329
|
+
import_tracker["last_import_type"] = current_type
|
330
|
+
elif stripped_line and not stripped_line.startswith("#"):
|
331
|
+
import_tracker["in_imports"] = False
|
332
|
+
import_tracker["last_import_type"] = None
|
333
|
+
|
334
|
+
def _is_stdlib_import(self, stripped_line: str) -> bool:
|
335
|
+
try:
|
336
|
+
if stripped_line.startswith("from "):
|
337
|
+
module = stripped_line.split()[1].split(".")[0]
|
338
|
+
else:
|
339
|
+
module = stripped_line.split()[1].split(".")[0]
|
340
|
+
except IndexError:
|
341
|
+
return False
|
342
|
+
stdlib_modules = {
|
343
|
+
"os",
|
344
|
+
"sys",
|
345
|
+
"re",
|
346
|
+
"json",
|
347
|
+
"datetime",
|
348
|
+
"time",
|
349
|
+
"pathlib",
|
350
|
+
"typing",
|
351
|
+
"collections",
|
352
|
+
"itertools",
|
353
|
+
"functools",
|
354
|
+
"operator",
|
355
|
+
"math",
|
356
|
+
"random",
|
357
|
+
"uuid",
|
358
|
+
"urllib",
|
359
|
+
"http",
|
360
|
+
"html",
|
361
|
+
"xml",
|
362
|
+
"email",
|
363
|
+
"csv",
|
364
|
+
"sqlite3",
|
365
|
+
"subprocess",
|
366
|
+
"threading",
|
367
|
+
"multiprocessing",
|
368
|
+
"asyncio",
|
369
|
+
"contextlib",
|
370
|
+
"dataclasses",
|
371
|
+
"enum",
|
372
|
+
"abc",
|
373
|
+
"io",
|
374
|
+
"tempfile",
|
375
|
+
"shutil",
|
376
|
+
"glob",
|
377
|
+
"pickle",
|
378
|
+
"copy",
|
379
|
+
"heapq",
|
380
|
+
"bisect",
|
381
|
+
"array",
|
382
|
+
"struct",
|
383
|
+
"zlib",
|
384
|
+
"hashlib",
|
385
|
+
"hmac",
|
386
|
+
"secrets",
|
387
|
+
"base64",
|
388
|
+
"binascii",
|
389
|
+
"codecs",
|
390
|
+
"locale",
|
391
|
+
"platform",
|
392
|
+
"socket",
|
393
|
+
"ssl",
|
394
|
+
"ipaddress",
|
395
|
+
"logging",
|
396
|
+
"warnings",
|
397
|
+
"inspect",
|
398
|
+
"ast",
|
399
|
+
"dis",
|
400
|
+
"tokenize",
|
401
|
+
"keyword",
|
402
|
+
"linecache",
|
403
|
+
"traceback",
|
404
|
+
"weakref",
|
405
|
+
"gc",
|
406
|
+
"ctypes",
|
407
|
+
"unittest",
|
408
|
+
"doctest",
|
409
|
+
"pdb",
|
410
|
+
"profile",
|
411
|
+
"cProfile",
|
412
|
+
"timeit",
|
413
|
+
"trace",
|
414
|
+
"calendar",
|
415
|
+
"decimal",
|
416
|
+
"fractions",
|
417
|
+
"statistics",
|
418
|
+
"tomllib",
|
419
|
+
}
|
420
|
+
return module in stdlib_modules
|
421
|
+
|
422
|
+
def _is_local_import(self, stripped_line: str) -> bool:
|
423
|
+
return stripped_line.startswith("from .") or " . " in stripped_line
|
424
|
+
|
287
425
|
def _is_function_end(
|
288
426
|
self, line: str, stripped_line: str, function_tracker: dict[str, t.Any]
|
289
427
|
) -> bool:
|
290
|
-
"""Check if current line marks the end of a function."""
|
291
428
|
return (
|
292
429
|
function_tracker["in_function"]
|
293
430
|
and bool(line)
|
@@ -301,16 +438,44 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
301
438
|
lines: list[str],
|
302
439
|
cleaned_lines: list[str],
|
303
440
|
function_tracker: dict[str, t.Any],
|
441
|
+
import_tracker: dict[str, t.Any],
|
304
442
|
) -> bool:
|
305
|
-
"""Determine if an empty line should be skipped."""
|
306
443
|
if line_idx > 0 and cleaned_lines and (not cleaned_lines[-1]):
|
307
444
|
return True
|
308
445
|
|
446
|
+
if self._is_import_section_separator(line_idx, lines, import_tracker):
|
447
|
+
return False
|
448
|
+
|
309
449
|
if function_tracker["in_function"]:
|
310
450
|
return self._should_skip_function_empty_line(line_idx, lines)
|
311
|
-
|
312
451
|
return False
|
313
452
|
|
453
|
+
def _is_import_section_separator(
|
454
|
+
self, line_idx: int, lines: list[str], import_tracker: dict[str, t.Any]
|
455
|
+
) -> bool:
|
456
|
+
if not import_tracker["in_imports"]:
|
457
|
+
return False
|
458
|
+
|
459
|
+
next_line_idx = line_idx + 1
|
460
|
+
while next_line_idx < len(lines) and not lines[next_line_idx].strip():
|
461
|
+
next_line_idx += 1
|
462
|
+
|
463
|
+
if next_line_idx >= len(lines):
|
464
|
+
return False
|
465
|
+
|
466
|
+
next_line = lines[next_line_idx].strip()
|
467
|
+
if not next_line.startswith(("import ", "from ")):
|
468
|
+
return False
|
469
|
+
|
470
|
+
if self._is_stdlib_import(next_line):
|
471
|
+
next_type = "stdlib"
|
472
|
+
elif self._is_local_import(next_line):
|
473
|
+
next_type = "local"
|
474
|
+
else:
|
475
|
+
next_type = "third_party"
|
476
|
+
|
477
|
+
return import_tracker["last_import_type"] != next_type
|
478
|
+
|
314
479
|
def _should_skip_function_empty_line(self, line_idx: int, lines: list[str]) -> bool:
|
315
480
|
next_line_idx = line_idx + 1
|
316
481
|
if next_line_idx >= len(lines):
|
@@ -323,7 +488,6 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
323
488
|
return True
|
324
489
|
if next_line in ("pass", "break", "continue", "raise"):
|
325
490
|
return True
|
326
|
-
|
327
491
|
return self._is_special_comment(next_line)
|
328
492
|
|
329
493
|
def _is_special_comment(self, line: str) -> bool:
|
@@ -463,15 +627,12 @@ class ConfigManager(BaseModel, arbitrary_types_allowed=True):
|
|
463
627
|
for tool, settings in our_toml_config.get("tool", {}).items():
|
464
628
|
if tool not in pkg_toml_config["tool"]:
|
465
629
|
pkg_toml_config["tool"][tool] = {}
|
466
|
-
|
467
630
|
pkg_tool_config = pkg_toml_config["tool"][tool]
|
468
|
-
|
469
631
|
self._merge_tool_config(settings, pkg_tool_config, tool)
|
470
632
|
|
471
633
|
def _merge_tool_config(
|
472
634
|
self, our_config: dict[str, t.Any], pkg_config: dict[str, t.Any], tool: str
|
473
635
|
) -> None:
|
474
|
-
"""Recursively merge tool configuration, preserving existing project settings."""
|
475
636
|
for setting, value in our_config.items():
|
476
637
|
if isinstance(value, dict):
|
477
638
|
self._merge_nested_config(setting, value, pkg_config)
|
@@ -481,21 +642,17 @@ class ConfigManager(BaseModel, arbitrary_types_allowed=True):
|
|
481
642
|
def _merge_nested_config(
|
482
643
|
self, setting: str, value: dict[str, t.Any], pkg_config: dict[str, t.Any]
|
483
644
|
) -> None:
|
484
|
-
"""Handle nested configuration merging."""
|
485
645
|
if setting not in pkg_config:
|
486
646
|
pkg_config[setting] = {}
|
487
647
|
elif not isinstance(pkg_config[setting], dict):
|
488
648
|
pkg_config[setting] = {}
|
489
|
-
|
490
649
|
self._merge_tool_config(value, pkg_config[setting], "")
|
491
|
-
|
492
650
|
for k, v in value.items():
|
493
651
|
self._merge_nested_value(k, v, pkg_config[setting])
|
494
652
|
|
495
653
|
def _merge_nested_value(
|
496
654
|
self, key: str, value: t.Any, nested_config: dict[str, t.Any]
|
497
655
|
) -> None:
|
498
|
-
"""Merge individual nested values."""
|
499
656
|
if isinstance(value, str | list) and "crackerjack" in str(value):
|
500
657
|
nested_config[key] = self.swap_package_name(value)
|
501
658
|
elif self._is_mergeable_list(key, value):
|
@@ -510,7 +667,6 @@ class ConfigManager(BaseModel, arbitrary_types_allowed=True):
|
|
510
667
|
def _merge_direct_config(
|
511
668
|
self, setting: str, value: t.Any, pkg_config: dict[str, t.Any]
|
512
669
|
) -> None:
|
513
|
-
"""Handle direct configuration merging."""
|
514
670
|
if isinstance(value, str | list) and "crackerjack" in str(value):
|
515
671
|
pkg_config[setting] = self.swap_package_name(value)
|
516
672
|
elif self._is_mergeable_list(setting, value):
|
@@ -773,7 +929,6 @@ class Crackerjack(BaseModel, arbitrary_types_allowed=True):
|
|
773
929
|
self._add_benchmark_flags(test, options)
|
774
930
|
else:
|
775
931
|
self._add_worker_flags(test, options, project_size)
|
776
|
-
|
777
932
|
return test
|
778
933
|
|
779
934
|
def _detect_project_size(self) -> str:
|
crackerjack/interactive.py
CHANGED
@@ -289,7 +289,6 @@ class InteractiveCLI:
|
|
289
289
|
)
|
290
290
|
total_tasks = len(self.workflow.tasks)
|
291
291
|
progress_task = progress.add_task("Running workflow", total=total_tasks)
|
292
|
-
|
293
292
|
return {
|
294
293
|
"progress": progress,
|
295
294
|
"progress_task": progress_task,
|
@@ -299,14 +298,11 @@ class InteractiveCLI:
|
|
299
298
|
def _execute_workflow_loop(
|
300
299
|
self, layout: Layout, progress_tracker: dict[str, t.Any], live: Live
|
301
300
|
) -> None:
|
302
|
-
"""Execute the main workflow loop."""
|
303
301
|
while not self.workflow.all_tasks_completed():
|
304
302
|
layout["tasks"].update(self.show_task_table())
|
305
303
|
next_task = self.workflow.get_next_task()
|
306
|
-
|
307
304
|
if not next_task:
|
308
305
|
break
|
309
|
-
|
310
306
|
if self._should_execute_task(layout, next_task, live):
|
311
307
|
self._execute_task(layout, next_task, progress_tracker)
|
312
308
|
else:
|
@@ -322,20 +318,16 @@ class InteractiveCLI:
|
|
322
318
|
def _execute_task(
|
323
319
|
self, layout: Layout, task: Task, progress_tracker: dict[str, t.Any]
|
324
320
|
) -> None:
|
325
|
-
"""Execute a single task and update progress."""
|
326
321
|
task.start()
|
327
322
|
layout["details"].update(self.show_task_status(task))
|
328
323
|
time.sleep(1)
|
329
|
-
|
330
324
|
success = self._simulate_task_execution()
|
331
|
-
|
332
325
|
if success:
|
333
326
|
task.complete()
|
334
327
|
progress_tracker["completed_tasks"] += 1
|
335
328
|
else:
|
336
329
|
error = self._create_task_error(task.name)
|
337
330
|
task.fail(error)
|
338
|
-
|
339
331
|
progress_tracker["progress"].update(
|
340
332
|
progress_tracker["progress_task"],
|
341
333
|
completed=progress_tracker["completed_tasks"],
|
crackerjack/pyproject.toml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: crackerjack
|
3
|
-
Version: 0.21.
|
3
|
+
Version: 0.21.8
|
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>
|
@@ -102,7 +102,7 @@ If you're new to Crackerjack, follow these steps:
|
|
102
102
|
|
103
103
|
Or use the interactive Rich UI:
|
104
104
|
```
|
105
|
-
python -m crackerjack --
|
105
|
+
python -m crackerjack --interactive
|
106
106
|
```
|
107
107
|
|
108
108
|
---
|
@@ -269,7 +269,7 @@ python -m crackerjack -t --benchmark-regression --benchmark-regression-threshold
|
|
269
269
|
|
270
270
|
Or with the interactive Rich UI:
|
271
271
|
```
|
272
|
-
python -m crackerjack --
|
272
|
+
python -m crackerjack --interactive
|
273
273
|
```
|
274
274
|
|
275
275
|
## Usage
|
@@ -361,7 +361,7 @@ runner.process(MyOptions())
|
|
361
361
|
- `--benchmark-regression`: Fail tests if benchmarks regress beyond threshold.
|
362
362
|
- `--benchmark-regression-threshold`: Set threshold percentage for benchmark regression (default 5.0%).
|
363
363
|
- `-a`, `--all`: Run with `-x -t -p <micro|minor|major> -c` development options.
|
364
|
-
- `--
|
364
|
+
- `--interactive`: Enable the interactive Rich UI for a more user-friendly experience with visual progress tracking and interactive prompts.
|
365
365
|
- `--ai-agent`: Enable AI agent mode with structured output (see [AI Agent Integration](#ai-agent-integration)).
|
366
366
|
- `--help`: Display help.
|
367
367
|
|
@@ -461,7 +461,7 @@ runner.process(MyOptions())
|
|
461
461
|
|
462
462
|
- **Rich Interactive Mode** - Run with the interactive Rich UI:
|
463
463
|
```bash
|
464
|
-
python -m crackerjack --
|
464
|
+
python -m crackerjack --interactive
|
465
465
|
```
|
466
466
|
|
467
467
|
- **AI Integration** - Run with structured output for AI tools:
|
@@ -500,10 +500,10 @@ Crackerjack now offers an enhanced interactive experience through its Rich UI:
|
|
500
500
|
- **Error Visualization:** Errors are presented in a structured, easy-to-understand format with recovery suggestions
|
501
501
|
- **File Selection:** Interactive file browser for operations that require selecting files
|
502
502
|
|
503
|
-
To use the Rich UI, run Crackerjack with the `--
|
503
|
+
To use the Rich UI, run Crackerjack with the `--interactive` flag:
|
504
504
|
|
505
505
|
```bash
|
506
|
-
python -m crackerjack --
|
506
|
+
python -m crackerjack --interactive
|
507
507
|
```
|
508
508
|
|
509
509
|
This launches an interactive terminal interface where you can:
|
@@ -542,7 +542,7 @@ python -m crackerjack -v
|
|
542
542
|
For the most comprehensive error details with visual formatting, combine verbose mode with the Rich UI:
|
543
543
|
|
544
544
|
```bash
|
545
|
-
python -m crackerjack --
|
545
|
+
python -m crackerjack --interactive -v
|
546
546
|
```
|
547
547
|
|
548
548
|
## Python 3.13+ Features
|
@@ -1,7 +1,7 @@
|
|
1
|
-
crackerjack-0.21.
|
2
|
-
crackerjack-0.21.
|
3
|
-
crackerjack-0.21.
|
4
|
-
crackerjack-0.21.
|
1
|
+
crackerjack-0.21.8.dist-info/METADATA,sha256=IPe32XzA-hFDy247TIpvfAOWuWZwr64gbhZCUbsP4jQ,26442
|
2
|
+
crackerjack-0.21.8.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
|
3
|
+
crackerjack-0.21.8.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
4
|
+
crackerjack-0.21.8.dist-info/licenses/LICENSE,sha256=fDt371P6_6sCu7RyqiZH_AhT1LdN3sN1zjBtqEhDYCk,1531
|
5
5
|
crackerjack/.gitignore,sha256=n8cD6U16L3XZn__PvhYm_F7-YeFHFucHCyxWj2NZCGs,259
|
6
6
|
crackerjack/.libcst.codemod.yaml,sha256=a8DlErRAIPV1nE6QlyXPAzTOgkB24_spl2E9hphuf5s,772
|
7
7
|
crackerjack/.pdm.toml,sha256=dZe44HRcuxxCFESGG8SZIjmc-cGzSoyK3Hs6t4NYA8w,23
|
@@ -34,7 +34,7 @@ crackerjack/.ruff_cache/0.11.7/10386934055395314831,sha256=lBNwN5zAgM4OzbkXIOzCc
|
|
34
34
|
crackerjack/.ruff_cache/0.11.7/3557596832929915217,sha256=fKlwUbsvT3YIKV6UR-aA_i64lLignWeVfVu-MMmVbU0,207
|
35
35
|
crackerjack/.ruff_cache/0.11.8/530407680854991027,sha256=xAMAL3Vu_HR6M-h5ojCTaak0By5ii8u-14pXULLgLqw,224
|
36
36
|
crackerjack/.ruff_cache/0.12.0/5056746222905752453,sha256=MqrIT5qymJcgAOBZyn-TvYoGCFfDFCgN9IwSULq8n14,256
|
37
|
-
crackerjack/.ruff_cache/0.12.1/5056746222905752453,sha256=
|
37
|
+
crackerjack/.ruff_cache/0.12.1/5056746222905752453,sha256=g9fAkLYZwfeJKgDNXhdsOwHnHBgTG5KKHfMoy5Lut1s,256
|
38
38
|
crackerjack/.ruff_cache/0.2.0/10047773857155985907,sha256=j9LNa_RQ4Plor7go1uTYgz17cEENKvZQ-dP6b9MX0ik,248
|
39
39
|
crackerjack/.ruff_cache/0.2.1/8522267973936635051,sha256=u_aPBMibtAp_iYvLwR88GMAECMcIgHezxMyuapmU2P4,248
|
40
40
|
crackerjack/.ruff_cache/0.2.2/18053836298936336950,sha256=Xb_ebP0pVuUfSqPEZKlhQ70so_vqkEfMYpuHQ06iR5U,248
|
@@ -64,9 +64,9 @@ crackerjack/.ruff_cache/0.9.9/8843823720003377982,sha256=e4ymkXfQsUg5e_mtO34xTsa
|
|
64
64
|
crackerjack/.ruff_cache/CACHEDIR.TAG,sha256=WVMVbX4MVkpCclExbq8m-IcOZIOuIZf5FrYw5Pk-Ma4,43
|
65
65
|
crackerjack/__init__.py,sha256=8tBSPAru_YDuPpjz05cL7pNbZjYFoRT_agGd_FWa3gY,839
|
66
66
|
crackerjack/__main__.py,sha256=AknITUlFjq3YUK9s2xeL62dM0GN82JBQyDkPzQ_hCUg,6561
|
67
|
-
crackerjack/crackerjack.py,sha256=
|
67
|
+
crackerjack/crackerjack.py,sha256=Vzt-oT989OJcw0He40BnnTUlmV8-siiZy5-a5fRjL1E,40046
|
68
68
|
crackerjack/errors.py,sha256=QEPtVuMtKtQHuawgr1ToMaN1KbUg5h9-4mS33YB5Znk,4062
|
69
|
-
crackerjack/interactive.py,sha256=
|
69
|
+
crackerjack/interactive.py,sha256=y5QbyR2Wp8WkC_iC89ZqETm-wjAN9X5DK1L3yetpjN4,16153
|
70
70
|
crackerjack/py313.py,sha256=buYE7LO11Q64ffowEhTZRFQoAGj_8sg3DTlZuv8M9eo,5890
|
71
|
-
crackerjack/pyproject.toml,sha256=
|
72
|
-
crackerjack-0.21.
|
71
|
+
crackerjack/pyproject.toml,sha256=CT9hhv56Ax8h1vljzxcEAn4s2wlQh1oZ0Iq3kiA6-m0,4988
|
72
|
+
crackerjack-0.21.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|