crackerjack 0.21.6__tar.gz → 0.21.7__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.
- {crackerjack-0.21.6 → crackerjack-0.21.7}/PKG-INFO +8 -8
- {crackerjack-0.21.6 → crackerjack-0.21.7}/README.md +7 -7
- crackerjack-0.21.7/crackerjack/.ruff_cache/0.12.1/5056746222905752453 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/__init__.py +0 -1
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/__main__.py +0 -1
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/crackerjack.py +81 -70
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/errors.py +0 -1
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/interactive.py +0 -10
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/pyproject.toml +1 -1
- {crackerjack-0.21.6 → crackerjack-0.21.7}/pyproject.toml +1 -1
- {crackerjack-0.21.6 → crackerjack-0.21.7}/tests/conftest.py +0 -1
- {crackerjack-0.21.6 → crackerjack-0.21.7}/tests/test_crackerjack.py +0 -33
- {crackerjack-0.21.6 → crackerjack-0.21.7}/tests/test_crackerjack_runner.py +0 -1
- {crackerjack-0.21.6 → crackerjack-0.21.7}/tests/test_errors.py +0 -1
- {crackerjack-0.21.6 → crackerjack-0.21.7}/tests/test_interactive.py +0 -1
- {crackerjack-0.21.6 → crackerjack-0.21.7}/tests/test_interactive_run.py +0 -1
- {crackerjack-0.21.6 → crackerjack-0.21.7}/tests/test_main.py +0 -3
- crackerjack-0.21.7/tests/test_multiline_functions.py +163 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/tests/test_py313_advanced.py +0 -1
- {crackerjack-0.21.6 → crackerjack-0.21.7}/tests/test_py313_features.py +0 -1
- {crackerjack-0.21.6 → crackerjack-0.21.7}/tests/test_pytest_features.py +0 -1
- {crackerjack-0.21.6 → crackerjack-0.21.7}/tests/test_structured_errors.py +0 -1
- crackerjack-0.21.6/crackerjack/.ruff_cache/0.12.1/5056746222905752453 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/LICENSE +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.gitignore +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.libcst.codemod.yaml +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.pdm.toml +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.pre-commit-config-ai.yaml +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.pre-commit-config.yaml +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.pytest_cache/.gitignore +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.pytest_cache/CACHEDIR.TAG +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.pytest_cache/README.md +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.pytest_cache/v/cache/nodeids +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.pytest_cache/v/cache/stepwise +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/.gitignore +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.11/18187162184424859798 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.12/16869036553936192448 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.12/1867267426380906393 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.12/4240757255861806333 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.12/4441409093023629623 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.13/1867267426380906393 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.13/4240757255861806333 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.12.0/5056746222905752453 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/CACHEDIR.TAG +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/py313.py +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/tests/TESTING.md +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/tests/__init__.py +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/tests/data/comments_sample.txt +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/tests/data/docstrings_sample.txt +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/tests/data/expected_comments_sample.txt +0 -0
- {crackerjack-0.21.6 → crackerjack-0.21.7}/tests/data/init.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: crackerjack
|
3
|
-
Version: 0.21.
|
3
|
+
Version: 0.21.7
|
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
|
@@ -58,7 +58,7 @@ If you're new to Crackerjack, follow these steps:
|
|
58
58
|
|
59
59
|
Or use the interactive Rich UI:
|
60
60
|
```
|
61
|
-
python -m crackerjack --
|
61
|
+
python -m crackerjack --interactive
|
62
62
|
```
|
63
63
|
|
64
64
|
---
|
@@ -225,7 +225,7 @@ python -m crackerjack -t --benchmark-regression --benchmark-regression-threshold
|
|
225
225
|
|
226
226
|
Or with the interactive Rich UI:
|
227
227
|
```
|
228
|
-
python -m crackerjack --
|
228
|
+
python -m crackerjack --interactive
|
229
229
|
```
|
230
230
|
|
231
231
|
## Usage
|
@@ -317,7 +317,7 @@ runner.process(MyOptions())
|
|
317
317
|
- `--benchmark-regression`: Fail tests if benchmarks regress beyond threshold.
|
318
318
|
- `--benchmark-regression-threshold`: Set threshold percentage for benchmark regression (default 5.0%).
|
319
319
|
- `-a`, `--all`: Run with `-x -t -p <micro|minor|major> -c` development options.
|
320
|
-
- `--
|
320
|
+
- `--interactive`: Enable the interactive Rich UI for a more user-friendly experience with visual progress tracking and interactive prompts.
|
321
321
|
- `--ai-agent`: Enable AI agent mode with structured output (see [AI Agent Integration](#ai-agent-integration)).
|
322
322
|
- `--help`: Display help.
|
323
323
|
|
@@ -417,7 +417,7 @@ runner.process(MyOptions())
|
|
417
417
|
|
418
418
|
- **Rich Interactive Mode** - Run with the interactive Rich UI:
|
419
419
|
```bash
|
420
|
-
python -m crackerjack --
|
420
|
+
python -m crackerjack --interactive
|
421
421
|
```
|
422
422
|
|
423
423
|
- **AI Integration** - Run with structured output for AI tools:
|
@@ -456,10 +456,10 @@ Crackerjack now offers an enhanced interactive experience through its Rich UI:
|
|
456
456
|
- **Error Visualization:** Errors are presented in a structured, easy-to-understand format with recovery suggestions
|
457
457
|
- **File Selection:** Interactive file browser for operations that require selecting files
|
458
458
|
|
459
|
-
To use the Rich UI, run Crackerjack with the `--
|
459
|
+
To use the Rich UI, run Crackerjack with the `--interactive` flag:
|
460
460
|
|
461
461
|
```bash
|
462
|
-
python -m crackerjack --
|
462
|
+
python -m crackerjack --interactive
|
463
463
|
```
|
464
464
|
|
465
465
|
This launches an interactive terminal interface where you can:
|
@@ -498,7 +498,7 @@ python -m crackerjack -v
|
|
498
498
|
For the most comprehensive error details with visual formatting, combine verbose mode with the Rich UI:
|
499
499
|
|
500
500
|
```bash
|
501
|
-
python -m crackerjack --
|
501
|
+
python -m crackerjack --interactive -v
|
502
502
|
```
|
503
503
|
|
504
504
|
## Python 3.13+ Features
|
Binary file
|
@@ -6,7 +6,6 @@ from pathlib import Path
|
|
6
6
|
from subprocess import CompletedProcess
|
7
7
|
from subprocess import run as execute
|
8
8
|
from tomllib import loads
|
9
|
-
|
10
9
|
from pydantic import BaseModel
|
11
10
|
from rich.console import Console
|
12
11
|
from tomli_w import dumps
|
@@ -86,55 +85,93 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
86
85
|
except Exception as e:
|
87
86
|
print(f"Error cleaning {file_path}: {e}")
|
88
87
|
|
89
|
-
def
|
90
|
-
|
91
|
-
cleaned_lines = []
|
92
|
-
docstring_state = {
|
88
|
+
def _initialize_docstring_state(self) -> dict[str, t.Any]:
|
89
|
+
return {
|
93
90
|
"in_docstring": False,
|
94
91
|
"delimiter": None,
|
95
92
|
"waiting": False,
|
96
93
|
"function_indent": 0,
|
97
94
|
"removed_docstring": False,
|
95
|
+
"in_multiline_def": False,
|
98
96
|
}
|
99
|
-
for i, line in enumerate(lines):
|
100
|
-
stripped = line.strip()
|
101
|
-
if self._is_function_or_class_definition(stripped):
|
102
|
-
docstring_state["waiting"] = True
|
103
|
-
docstring_state["function_indent"] = len(line) - len(line.lstrip())
|
104
|
-
docstring_state["removed_docstring"] = False
|
105
|
-
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
97
|
|
98
|
+
def _handle_function_definition(
|
99
|
+
self, line: str, stripped: str, state: dict[str, t.Any]
|
100
|
+
) -> bool:
|
101
|
+
if self._is_function_or_class_definition(stripped):
|
102
|
+
state["waiting"] = True
|
103
|
+
state["function_indent"] = len(line) - len(line.lstrip())
|
104
|
+
state["removed_docstring"] = False
|
105
|
+
state["in_multiline_def"] = not stripped.endswith(":")
|
106
|
+
return True
|
107
|
+
return False
|
108
|
+
|
109
|
+
def _handle_multiline_definition(
|
110
|
+
self, line: str, stripped: str, state: dict[str, t.Any]
|
111
|
+
) -> bool:
|
112
|
+
if state["in_multiline_def"]:
|
113
|
+
if stripped.endswith(":"):
|
114
|
+
state["in_multiline_def"] = False
|
115
|
+
return True
|
116
|
+
return False
|
117
|
+
|
118
|
+
def _handle_waiting_docstring(
|
119
|
+
self, lines: list[str], i: int, stripped: str, state: dict[str, t.Any]
|
120
|
+
) -> tuple[bool, str | None]:
|
121
|
+
if state["waiting"] and stripped:
|
122
|
+
if self._handle_docstring_start(stripped, state):
|
123
|
+
pass_line = None
|
124
|
+
if not state["in_docstring"]:
|
125
|
+
function_indent: int = state["function_indent"]
|
126
|
+
if self._needs_pass_statement(lines, i + 1, function_indent):
|
127
|
+
pass_line = " " * (function_indent + 4) + "pass"
|
128
|
+
state["removed_docstring"] = True
|
129
|
+
return True, pass_line
|
130
|
+
else:
|
131
|
+
state["waiting"] = False
|
132
|
+
return False, None
|
133
|
+
|
134
|
+
def _handle_docstring_content(
|
135
|
+
self, lines: list[str], i: int, stripped: str, state: dict[str, t.Any]
|
136
|
+
) -> tuple[bool, str | None]:
|
137
|
+
if state["in_docstring"]:
|
138
|
+
if self._handle_docstring_end(stripped, state):
|
139
|
+
pass_line = None
|
140
|
+
function_indent: int = state["function_indent"]
|
141
|
+
if self._needs_pass_statement(lines, i + 1, function_indent):
|
142
|
+
pass_line = " " * (function_indent + 4) + "pass"
|
143
|
+
state["removed_docstring"] = False
|
144
|
+
return True, pass_line
|
145
|
+
else:
|
146
|
+
return True, None
|
147
|
+
return False, None
|
148
|
+
|
149
|
+
def _process_line(
|
150
|
+
self, lines: list[str], i: int, line: str, state: dict[str, t.Any]
|
151
|
+
) -> tuple[bool, str | None]:
|
152
|
+
stripped = line.strip()
|
153
|
+
if self._handle_function_definition(line, stripped, state):
|
154
|
+
return True, line
|
155
|
+
if self._handle_multiline_definition(line, stripped, state):
|
156
|
+
return True, line
|
157
|
+
handled, pass_line = self._handle_waiting_docstring(lines, i, stripped, state)
|
158
|
+
if handled:
|
159
|
+
return True, pass_line
|
160
|
+
handled, pass_line = self._handle_docstring_content(lines, i, stripped, state)
|
161
|
+
if handled:
|
162
|
+
return True, pass_line
|
163
|
+
if state["removed_docstring"] and stripped:
|
164
|
+
state["removed_docstring"] = False
|
165
|
+
return False, line
|
166
|
+
|
167
|
+
def remove_docstrings(self, code: str) -> str:
|
168
|
+
lines = code.split("\n")
|
169
|
+
cleaned_lines = []
|
170
|
+
docstring_state = self._initialize_docstring_state()
|
171
|
+
for i, line in enumerate(lines):
|
172
|
+
_, result_line = self._process_line(lines, i, line, docstring_state)
|
173
|
+
if result_line:
|
174
|
+
cleaned_lines.append(result_line)
|
138
175
|
return "\n".join(cleaned_lines)
|
139
176
|
|
140
177
|
def _is_function_or_class_definition(self, stripped_line: str) -> bool:
|
@@ -177,9 +214,8 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
177
214
|
line_indent = len(line) - len(line.lstrip())
|
178
215
|
if line_indent <= function_indent:
|
179
216
|
return True
|
180
|
-
if line_indent
|
217
|
+
if line_indent > function_indent:
|
181
218
|
return False
|
182
|
-
|
183
219
|
return True
|
184
220
|
|
185
221
|
def remove_line_comments(self, code: str) -> str:
|
@@ -192,7 +228,6 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
192
228
|
cleaned_line = self._process_line_for_comments(line)
|
193
229
|
if cleaned_line or not line.strip():
|
194
230
|
cleaned_lines.append(cleaned_line or line)
|
195
|
-
|
196
231
|
return "\n".join(cleaned_lines)
|
197
232
|
|
198
233
|
def _process_line_for_comments(self, line: str) -> str:
|
@@ -205,7 +240,6 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
205
240
|
break
|
206
241
|
else:
|
207
242
|
result.append(char)
|
208
|
-
|
209
243
|
return "".join(result).rstrip()
|
210
244
|
|
211
245
|
def _handle_string_character(
|
@@ -216,18 +250,14 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
216
250
|
string_state: dict[str, t.Any],
|
217
251
|
result: list[str],
|
218
252
|
) -> bool:
|
219
|
-
"""Handle string quote characters. Returns True if character was handled."""
|
220
253
|
if char not in ("'", '"'):
|
221
254
|
return False
|
222
|
-
|
223
255
|
if index > 0 and line[index - 1] == "\\":
|
224
256
|
return False
|
225
|
-
|
226
257
|
if string_state["in_string"] is None:
|
227
258
|
string_state["in_string"] = char
|
228
259
|
elif string_state["in_string"] == char:
|
229
260
|
string_state["in_string"] = None
|
230
|
-
|
231
261
|
result.append(char)
|
232
262
|
return True
|
233
263
|
|
@@ -239,14 +269,11 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
239
269
|
string_state: dict[str, t.Any],
|
240
270
|
result: list[str],
|
241
271
|
) -> bool:
|
242
|
-
"""Handle comment character. Returns True if comment was found."""
|
243
272
|
if char != "#" or string_state["in_string"] is not None:
|
244
273
|
return False
|
245
|
-
|
246
274
|
comment = line[index:].strip()
|
247
275
|
if self._is_special_comment_line(comment):
|
248
276
|
result.append(line[index:])
|
249
|
-
|
250
277
|
return True
|
251
278
|
|
252
279
|
def _is_special_comment_line(self, comment: str) -> bool:
|
@@ -270,13 +297,11 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
270
297
|
):
|
271
298
|
continue
|
272
299
|
cleaned_lines.append(line)
|
273
|
-
|
274
300
|
return "\n".join(self._remove_trailing_empty_lines(cleaned_lines))
|
275
301
|
|
276
302
|
def _update_function_state(
|
277
303
|
self, line: str, stripped_line: str, function_tracker: dict[str, t.Any]
|
278
304
|
) -> None:
|
279
|
-
"""Update function tracking state based on current line."""
|
280
305
|
if stripped_line.startswith(("def ", "async def ")):
|
281
306
|
function_tracker["in_function"] = True
|
282
307
|
function_tracker["function_indent"] = len(line) - len(stripped_line)
|
@@ -287,7 +312,6 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
287
312
|
def _is_function_end(
|
288
313
|
self, line: str, stripped_line: str, function_tracker: dict[str, t.Any]
|
289
314
|
) -> bool:
|
290
|
-
"""Check if current line marks the end of a function."""
|
291
315
|
return (
|
292
316
|
function_tracker["in_function"]
|
293
317
|
and bool(line)
|
@@ -302,13 +326,10 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
302
326
|
cleaned_lines: list[str],
|
303
327
|
function_tracker: dict[str, t.Any],
|
304
328
|
) -> bool:
|
305
|
-
"""Determine if an empty line should be skipped."""
|
306
329
|
if line_idx > 0 and cleaned_lines and (not cleaned_lines[-1]):
|
307
330
|
return True
|
308
|
-
|
309
331
|
if function_tracker["in_function"]:
|
310
332
|
return self._should_skip_function_empty_line(line_idx, lines)
|
311
|
-
|
312
333
|
return False
|
313
334
|
|
314
335
|
def _should_skip_function_empty_line(self, line_idx: int, lines: list[str]) -> bool:
|
@@ -323,7 +344,6 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
|
323
344
|
return True
|
324
345
|
if next_line in ("pass", "break", "continue", "raise"):
|
325
346
|
return True
|
326
|
-
|
327
347
|
return self._is_special_comment(next_line)
|
328
348
|
|
329
349
|
def _is_special_comment(self, line: str) -> bool:
|
@@ -463,15 +483,12 @@ class ConfigManager(BaseModel, arbitrary_types_allowed=True):
|
|
463
483
|
for tool, settings in our_toml_config.get("tool", {}).items():
|
464
484
|
if tool not in pkg_toml_config["tool"]:
|
465
485
|
pkg_toml_config["tool"][tool] = {}
|
466
|
-
|
467
486
|
pkg_tool_config = pkg_toml_config["tool"][tool]
|
468
|
-
|
469
487
|
self._merge_tool_config(settings, pkg_tool_config, tool)
|
470
488
|
|
471
489
|
def _merge_tool_config(
|
472
490
|
self, our_config: dict[str, t.Any], pkg_config: dict[str, t.Any], tool: str
|
473
491
|
) -> None:
|
474
|
-
"""Recursively merge tool configuration, preserving existing project settings."""
|
475
492
|
for setting, value in our_config.items():
|
476
493
|
if isinstance(value, dict):
|
477
494
|
self._merge_nested_config(setting, value, pkg_config)
|
@@ -481,21 +498,17 @@ class ConfigManager(BaseModel, arbitrary_types_allowed=True):
|
|
481
498
|
def _merge_nested_config(
|
482
499
|
self, setting: str, value: dict[str, t.Any], pkg_config: dict[str, t.Any]
|
483
500
|
) -> None:
|
484
|
-
"""Handle nested configuration merging."""
|
485
501
|
if setting not in pkg_config:
|
486
502
|
pkg_config[setting] = {}
|
487
503
|
elif not isinstance(pkg_config[setting], dict):
|
488
504
|
pkg_config[setting] = {}
|
489
|
-
|
490
505
|
self._merge_tool_config(value, pkg_config[setting], "")
|
491
|
-
|
492
506
|
for k, v in value.items():
|
493
507
|
self._merge_nested_value(k, v, pkg_config[setting])
|
494
508
|
|
495
509
|
def _merge_nested_value(
|
496
510
|
self, key: str, value: t.Any, nested_config: dict[str, t.Any]
|
497
511
|
) -> None:
|
498
|
-
"""Merge individual nested values."""
|
499
512
|
if isinstance(value, str | list) and "crackerjack" in str(value):
|
500
513
|
nested_config[key] = self.swap_package_name(value)
|
501
514
|
elif self._is_mergeable_list(key, value):
|
@@ -510,7 +523,6 @@ class ConfigManager(BaseModel, arbitrary_types_allowed=True):
|
|
510
523
|
def _merge_direct_config(
|
511
524
|
self, setting: str, value: t.Any, pkg_config: dict[str, t.Any]
|
512
525
|
) -> None:
|
513
|
-
"""Handle direct configuration merging."""
|
514
526
|
if isinstance(value, str | list) and "crackerjack" in str(value):
|
515
527
|
pkg_config[setting] = self.swap_package_name(value)
|
516
528
|
elif self._is_mergeable_list(setting, value):
|
@@ -773,7 +785,6 @@ class Crackerjack(BaseModel, arbitrary_types_allowed=True):
|
|
773
785
|
self._add_benchmark_flags(test, options)
|
774
786
|
else:
|
775
787
|
self._add_worker_flags(test, options, project_size)
|
776
|
-
|
777
788
|
return test
|
778
789
|
|
779
790
|
def _detect_project_size(self) -> str:
|
@@ -2,7 +2,6 @@ import time
|
|
2
2
|
import typing as t
|
3
3
|
from enum import Enum, auto
|
4
4
|
from pathlib import Path
|
5
|
-
|
6
5
|
from rich.box import ROUNDED
|
7
6
|
from rich.console import Console
|
8
7
|
from rich.layout import Layout
|
@@ -19,7 +18,6 @@ from rich.prompt import Confirm, Prompt
|
|
19
18
|
from rich.table import Table
|
20
19
|
from rich.text import Text
|
21
20
|
from rich.tree import Tree
|
22
|
-
|
23
21
|
from .errors import CrackerjackError, ErrorCode, handle_error
|
24
22
|
|
25
23
|
|
@@ -289,7 +287,6 @@ class InteractiveCLI:
|
|
289
287
|
)
|
290
288
|
total_tasks = len(self.workflow.tasks)
|
291
289
|
progress_task = progress.add_task("Running workflow", total=total_tasks)
|
292
|
-
|
293
290
|
return {
|
294
291
|
"progress": progress,
|
295
292
|
"progress_task": progress_task,
|
@@ -299,14 +296,11 @@ class InteractiveCLI:
|
|
299
296
|
def _execute_workflow_loop(
|
300
297
|
self, layout: Layout, progress_tracker: dict[str, t.Any], live: Live
|
301
298
|
) -> None:
|
302
|
-
"""Execute the main workflow loop."""
|
303
299
|
while not self.workflow.all_tasks_completed():
|
304
300
|
layout["tasks"].update(self.show_task_table())
|
305
301
|
next_task = self.workflow.get_next_task()
|
306
|
-
|
307
302
|
if not next_task:
|
308
303
|
break
|
309
|
-
|
310
304
|
if self._should_execute_task(layout, next_task, live):
|
311
305
|
self._execute_task(layout, next_task, progress_tracker)
|
312
306
|
else:
|
@@ -322,20 +316,16 @@ class InteractiveCLI:
|
|
322
316
|
def _execute_task(
|
323
317
|
self, layout: Layout, task: Task, progress_tracker: dict[str, t.Any]
|
324
318
|
) -> None:
|
325
|
-
"""Execute a single task and update progress."""
|
326
319
|
task.start()
|
327
320
|
layout["details"].update(self.show_task_status(task))
|
328
321
|
time.sleep(1)
|
329
|
-
|
330
322
|
success = self._simulate_task_execution()
|
331
|
-
|
332
323
|
if success:
|
333
324
|
task.complete()
|
334
325
|
progress_tracker["completed_tasks"] += 1
|
335
326
|
else:
|
336
327
|
error = self._create_task_error(task.name)
|
337
328
|
task.fail(error)
|
338
|
-
|
339
329
|
progress_tracker["progress"].update(
|
340
330
|
progress_tracker["progress_task"],
|
341
331
|
completed=progress_tracker["completed_tasks"],
|
@@ -6,7 +6,6 @@ from dataclasses import dataclass
|
|
6
6
|
from enum import Enum
|
7
7
|
from pathlib import Path
|
8
8
|
from unittest.mock import MagicMock, patch
|
9
|
-
|
10
9
|
import pytest
|
11
10
|
from rich.console import Console
|
12
11
|
from crackerjack.crackerjack import (
|
@@ -317,12 +316,10 @@ class TestCrackerjackProcess:
|
|
317
316
|
) -> None:
|
318
317
|
options = options_factory(bump="minor", no_config_updates=True)
|
319
318
|
cj = Crackerjack(dry_run=True)
|
320
|
-
|
321
319
|
with patch("rich.prompt.Confirm.ask", return_value=True) as mock_confirm:
|
322
320
|
with patch.object(Crackerjack, "execute_command") as mock_exec:
|
323
321
|
mock_exec.return_value = MagicMock(returncode=0)
|
324
322
|
cj._bump_version(options)
|
325
|
-
|
326
323
|
mock_confirm.assert_called_once_with(
|
327
324
|
"Are you sure you want to bump the minor version?", default=False
|
328
325
|
)
|
@@ -339,12 +336,10 @@ class TestCrackerjackProcess:
|
|
339
336
|
) -> None:
|
340
337
|
options = options_factory(bump="minor", no_config_updates=True)
|
341
338
|
cj = Crackerjack(dry_run=True)
|
342
|
-
|
343
339
|
with patch("rich.prompt.Confirm.ask", return_value=False) as mock_confirm:
|
344
340
|
with patch.object(Crackerjack, "execute_command") as mock_exec:
|
345
341
|
mock_exec.return_value = MagicMock(returncode=0)
|
346
342
|
cj._bump_version(options)
|
347
|
-
|
348
343
|
mock_confirm.assert_called_once_with(
|
349
344
|
"Are you sure you want to bump the minor version?", default=False
|
350
345
|
)
|
@@ -361,12 +356,10 @@ class TestCrackerjackProcess:
|
|
361
356
|
) -> None:
|
362
357
|
options = options_factory(bump="major", no_config_updates=True)
|
363
358
|
cj = Crackerjack(dry_run=True)
|
364
|
-
|
365
359
|
with patch("rich.prompt.Confirm.ask", return_value=True) as mock_confirm:
|
366
360
|
with patch.object(Crackerjack, "execute_command") as mock_exec:
|
367
361
|
mock_exec.return_value = MagicMock(returncode=0)
|
368
362
|
cj._bump_version(options)
|
369
|
-
|
370
363
|
mock_confirm.assert_called_once_with(
|
371
364
|
"Are you sure you want to bump the major version?", default=False
|
372
365
|
)
|
@@ -383,12 +376,10 @@ class TestCrackerjackProcess:
|
|
383
376
|
) -> None:
|
384
377
|
options = options_factory(bump="micro", no_config_updates=True)
|
385
378
|
cj = Crackerjack(dry_run=True)
|
386
|
-
|
387
379
|
with patch("rich.prompt.Confirm.ask") as mock_confirm:
|
388
380
|
with patch.object(Crackerjack, "execute_command") as mock_exec:
|
389
381
|
mock_exec.return_value = MagicMock(returncode=0)
|
390
382
|
cj._bump_version(options)
|
391
|
-
|
392
383
|
mock_confirm.assert_not_called()
|
393
384
|
mock_exec.assert_called_once_with(["pdm", "bump", "micro"])
|
394
385
|
|
@@ -405,7 +396,6 @@ class TestCrackerjackProcess:
|
|
405
396
|
pytest_command = (_cj := Crackerjack(dry_run=True))._prepare_pytest_command(
|
406
397
|
options
|
407
398
|
)
|
408
|
-
|
409
399
|
assert "--junitxml=test-results.xml" in pytest_command
|
410
400
|
assert "--cov-report=json:coverage.json" in pytest_command
|
411
401
|
assert "--quiet" in pytest_command
|
@@ -427,7 +417,6 @@ class TestCrackerjackProcess:
|
|
427
417
|
pytest_command = (_cj := Crackerjack(dry_run=True))._prepare_pytest_command(
|
428
418
|
options
|
429
419
|
)
|
430
|
-
|
431
420
|
assert "--junitxml=test-results.xml" in pytest_command
|
432
421
|
assert "--cov-report=json:coverage.json" in pytest_command
|
433
422
|
assert "--benchmark-json=benchmark.json" in pytest_command
|
@@ -447,12 +436,10 @@ class TestCrackerjackProcess:
|
|
447
436
|
pytest_command = (_cj := Crackerjack(dry_run=True))._prepare_pytest_command(
|
448
437
|
options
|
449
438
|
)
|
450
|
-
|
451
439
|
assert "--junitxml=test-results.xml" not in pytest_command
|
452
440
|
assert "--cov-report=json:coverage.json" not in pytest_command
|
453
441
|
assert "--benchmark-json=benchmark.json" not in pytest_command
|
454
442
|
assert "--quiet" not in pytest_command
|
455
|
-
|
456
443
|
assert "--capture=fd" in pytest_command
|
457
444
|
assert "--disable-warnings" in pytest_command
|
458
445
|
assert "--durations=0" in pytest_command
|
@@ -943,16 +930,11 @@ class TestCrackerjackProcess:
|
|
943
930
|
options_factory: t.Callable[..., OptionsForTesting],
|
944
931
|
) -> None:
|
945
932
|
mock_project = MagicMock()
|
946
|
-
|
947
933
|
options = options_factory(ai_agent=True)
|
948
934
|
mock_project.options = options
|
949
|
-
|
950
935
|
mock_project.console = MagicMock()
|
951
|
-
|
952
936
|
mock_project.execute_command.return_value = MagicMock(returncode=0)
|
953
|
-
|
954
937
|
ProjectManager.run_pre_commit(mock_project)
|
955
|
-
|
956
938
|
mock_project.execute_command.assert_called_with(
|
957
939
|
["pre-commit", "run", "--all-files", "-c", ".pre-commit-config-ai.yaml"]
|
958
940
|
)
|
@@ -964,24 +946,17 @@ class TestCrackerjackProcess:
|
|
964
946
|
options_factory: t.Callable[..., OptionsForTesting],
|
965
947
|
) -> None:
|
966
948
|
mock_project = MagicMock(spec=ProjectManager)
|
967
|
-
|
968
949
|
options = options_factory(ai_agent=True)
|
969
950
|
mock_project.options = options
|
970
|
-
|
971
951
|
mock_project.console = MagicMock()
|
972
|
-
|
973
952
|
mock_project.config_manager = MagicMock()
|
974
|
-
|
975
953
|
with patch.object(mock_project, "execute_command") as mock_execute:
|
976
954
|
mock_execute.return_value = MagicMock(
|
977
955
|
returncode=0,
|
978
956
|
stdout="package1\npackage2\n",
|
979
957
|
)
|
980
|
-
|
981
958
|
original_method = ProjectManager.update_pkg_configs
|
982
|
-
|
983
959
|
original_method(mock_project)
|
984
|
-
|
985
960
|
mock_execute.assert_any_call(
|
986
961
|
["pre-commit", "install", "-c", ".pre-commit-config-ai.yaml"]
|
987
962
|
)
|
@@ -1125,29 +1100,21 @@ class TestCrackerjackProcess:
|
|
1125
1100
|
test_code = """
|
1126
1101
|
def empty_function():
|
1127
1102
|
pass
|
1128
|
-
|
1129
1103
|
class TestClass:
|
1130
|
-
|
1131
1104
|
def method_with_docstring_only(self):
|
1132
1105
|
pass
|
1133
|
-
|
1134
1106
|
def method_with_code(self):
|
1135
1107
|
return True
|
1136
1108
|
"""
|
1137
|
-
|
1138
1109
|
cleaned_code = code_cleaner.remove_docstrings(test_code)
|
1139
1110
|
print(f"Cleaned code: {cleaned_code!r}")
|
1140
|
-
|
1141
1111
|
assert '"""This function has only a docstring."""' not in cleaned_code
|
1142
1112
|
assert '"""Class docstring."""' not in cleaned_code
|
1143
1113
|
assert '"""Method with only docstring."""' not in cleaned_code
|
1144
1114
|
assert '"""This method has code after docstring."""' not in cleaned_code
|
1145
|
-
|
1146
1115
|
assert "def empty_function():\n pass" in cleaned_code
|
1147
1116
|
assert "def method_with_docstring_only(self):\n pass" in cleaned_code
|
1148
|
-
|
1149
1117
|
assert "def method_with_code(self):\n return True" in cleaned_code
|
1150
|
-
|
1151
1118
|
try:
|
1152
1119
|
ast.parse(cleaned_code)
|
1153
1120
|
except SyntaxError as e:
|
@@ -1,6 +1,5 @@
|
|
1
1
|
import typing as t
|
2
2
|
from unittest.mock import MagicMock, patch
|
3
|
-
|
4
3
|
import pytest
|
5
4
|
from typer.testing import CliRunner
|
6
5
|
from crackerjack.__main__ import BumpOption, Options, app
|
@@ -59,7 +58,6 @@ def test_interactive_option(
|
|
59
58
|
assert result.exit_code == 0
|
60
59
|
mock_interactive.assert_called_once()
|
61
60
|
mock_crackerjack_process.process.assert_not_called()
|
62
|
-
|
63
61
|
mock_interactive.reset_mock()
|
64
62
|
result = runner.invoke(app, ["--interactive"])
|
65
63
|
assert result.exit_code == 0
|
@@ -203,7 +201,6 @@ def test_multiple_options(
|
|
203
201
|
assert result.exit_code == 0
|
204
202
|
mock_interactive.assert_called_once()
|
205
203
|
mock_crackerjack_process.process.assert_not_called()
|
206
|
-
|
207
204
|
result = runner.invoke(app, ["-c", "-d", "-t", "-x"])
|
208
205
|
assert result.exit_code == 0
|
209
206
|
mock_crackerjack_process.process.assert_called_once()
|
@@ -0,0 +1,163 @@
|
|
1
|
+
"""Test cases for multiline function definitions in docstring removal."""
|
2
|
+
|
3
|
+
import ast
|
4
|
+
from rich.console import Console
|
5
|
+
from crackerjack.crackerjack import CodeCleaner
|
6
|
+
|
7
|
+
|
8
|
+
class TestMultilineFunctions:
|
9
|
+
def setup_method(self) -> None:
|
10
|
+
self.console = Console()
|
11
|
+
self.cleaner = CodeCleaner(console=self.console)
|
12
|
+
|
13
|
+
def test_multiline_async_function_with_docstring(self) -> None:
|
14
|
+
code = """class BackgroundTaskManager:
|
15
|
+
async def __aexit__(
|
16
|
+
self,
|
17
|
+
exc_type: type[BaseException] | None,
|
18
|
+
exc_val: BaseException | None,
|
19
|
+
exc_tb: t.Any | None,
|
20
|
+
) -> None:
|
21
|
+
del exc_type, exc_val, exc_tb
|
22
|
+
self.logger.debug("Background task manager shutting down")
|
23
|
+
await wait_for_background_tasks(self.cleanup_timeout)
|
24
|
+
"""
|
25
|
+
result = self.cleaner.remove_docstrings(code)
|
26
|
+
ast.parse(result)
|
27
|
+
assert '"""Clean up background tasks on exit."""' not in result
|
28
|
+
assert "del exc_type, exc_val, exc_tb" in result
|
29
|
+
assert "self.logger.debug" in result
|
30
|
+
|
31
|
+
def test_multiline_function_with_docstring(self) -> None:
|
32
|
+
code = '''def complex_function(
|
33
|
+
param1: str,
|
34
|
+
param2: int,
|
35
|
+
param3: dict[str, Any],
|
36
|
+
) -> bool:
|
37
|
+
"""This is a complex function with multiple parameters."""
|
38
|
+
return True
|
39
|
+
'''
|
40
|
+
result = self.cleaner.remove_docstrings(code)
|
41
|
+
ast.parse(result)
|
42
|
+
assert (
|
43
|
+
'"""This is a complex function with multiple parameters."""' not in result
|
44
|
+
)
|
45
|
+
assert "return True" in result
|
46
|
+
|
47
|
+
def test_multiline_function_no_docstring(self) -> None:
|
48
|
+
code = """def complex_function(
|
49
|
+
param1: str,
|
50
|
+
param2: int,
|
51
|
+
param3: dict[str, Any],
|
52
|
+
) -> bool:
|
53
|
+
return True
|
54
|
+
"""
|
55
|
+
result = self.cleaner.remove_docstrings(code)
|
56
|
+
ast.parse(result)
|
57
|
+
assert "return True" in result
|
58
|
+
assert "def complex_function(" in result
|
59
|
+
|
60
|
+
def test_nested_multiline_functions(self) -> None:
|
61
|
+
code = """class TestClass:
|
62
|
+
def outer_method(
|
63
|
+
self,
|
64
|
+
param: str,
|
65
|
+
) -> None:
|
66
|
+
def inner_function(
|
67
|
+
x: int,
|
68
|
+
y: int,
|
69
|
+
) -> int:
|
70
|
+
return x + y
|
71
|
+
return inner_function(1, 2)
|
72
|
+
"""
|
73
|
+
result = self.cleaner.remove_docstrings(code)
|
74
|
+
ast.parse(result)
|
75
|
+
assert '"""Outer method docstring."""' not in result
|
76
|
+
assert '"""Inner function docstring."""' not in result
|
77
|
+
assert "return x + y" in result
|
78
|
+
assert "return inner_function(1, 2)" in result
|
79
|
+
|
80
|
+
def test_multiline_function_with_decorators(self) -> None:
|
81
|
+
code = """class TestClass:
|
82
|
+
@property
|
83
|
+
@some_decorator(
|
84
|
+
param1="value1",
|
85
|
+
param2="value2"
|
86
|
+
)
|
87
|
+
def complex_property(
|
88
|
+
self,
|
89
|
+
) -> str:
|
90
|
+
return "test"
|
91
|
+
"""
|
92
|
+
result = self.cleaner.remove_docstrings(code)
|
93
|
+
ast.parse(result)
|
94
|
+
assert (
|
95
|
+
'"""Property with complex decorator and multiline signature."""'
|
96
|
+
not in result
|
97
|
+
)
|
98
|
+
assert "@property" in result
|
99
|
+
assert "@some_decorator" in result
|
100
|
+
assert 'return "test"' in result
|
101
|
+
|
102
|
+
def test_class_with_multiline_init(self) -> None:
|
103
|
+
code = '''class ComplexClass:
|
104
|
+
"""Class docstring."""
|
105
|
+
def __init__(
|
106
|
+
self,
|
107
|
+
param1: str,
|
108
|
+
param2: int = 42,
|
109
|
+
param3: Optional[dict] = None,
|
110
|
+
) -> None:
|
111
|
+
self.param1 = param1
|
112
|
+
self.param2 = param2
|
113
|
+
self.param3 = param3 or {}
|
114
|
+
'''
|
115
|
+
result = self.cleaner.remove_docstrings(code)
|
116
|
+
ast.parse(result)
|
117
|
+
assert '"""Class docstring."""' not in result
|
118
|
+
assert '"""Initialize the complex class."""' not in result
|
119
|
+
assert "self.param1 = param1" in result
|
120
|
+
assert "self.param2 = param2" in result
|
121
|
+
assert "self.param3 = param3 or {}" in result
|
122
|
+
|
123
|
+
def test_multiline_function_empty_body_gets_pass(self) -> None:
|
124
|
+
code = '''def empty_function(
|
125
|
+
param1: str,
|
126
|
+
param2: int,
|
127
|
+
) -> None:
|
128
|
+
"""This function only has a docstring."""
|
129
|
+
'''
|
130
|
+
result = self.cleaner.remove_docstrings(code)
|
131
|
+
ast.parse(result)
|
132
|
+
assert '"""This function only has a docstring."""' not in result
|
133
|
+
assert "pass" in result
|
134
|
+
|
135
|
+
def test_complex_real_world_example(self) -> None:
|
136
|
+
code = '''class BackgroundTaskManager:
|
137
|
+
"""Manage background tasks."""
|
138
|
+
def __init__(self, cleanup_timeout: float = 30.0) -> None:
|
139
|
+
self.cleanup_timeout = cleanup_timeout
|
140
|
+
self.logger = depends.get(Logger)
|
141
|
+
async def __aenter__(self) -> "BackgroundTaskManager":
|
142
|
+
self.logger.debug("Background task manager started")
|
143
|
+
return self
|
144
|
+
async def __aexit__(
|
145
|
+
self,
|
146
|
+
exc_type: type[BaseException] | None,
|
147
|
+
exc_val: BaseException | None,
|
148
|
+
exc_tb: t.Any | None,
|
149
|
+
) -> None:
|
150
|
+
del exc_type, exc_val, exc_tb
|
151
|
+
self.logger.debug("Background task manager shutting down")
|
152
|
+
await wait_for_background_tasks(self.cleanup_timeout)
|
153
|
+
'''
|
154
|
+
result = self.cleaner.remove_docstrings(code)
|
155
|
+
ast.parse(result)
|
156
|
+
assert '"""Manage background tasks."""' not in result
|
157
|
+
assert '"""Initialize the background task manager."""' not in result
|
158
|
+
assert '"""Enter async context."""' not in result
|
159
|
+
assert '"""Clean up background tasks on exit."""' not in result
|
160
|
+
assert "self.cleanup_timeout = cleanup_timeout" in result
|
161
|
+
assert 'self.logger.debug("Background task manager started")' in result
|
162
|
+
assert "del exc_type, exc_val, exc_tb" in result
|
163
|
+
assert "await wait_for_background_tasks" in result
|
Binary file
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.1.11/3256171999636029978
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.1.4/10355199064880463147
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.1.6/15140459877605758699
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.1.9/17041001205004563469
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.11/18187162184424859798
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.12/16869036553936192448
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.12/1867267426380906393
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.12/4240757255861806333
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.12/4441409093023629623
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.13/1867267426380906393
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.13/4240757255861806333
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.2/4070660268492669020
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.3/9818742842212983150
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.4/9818742842212983150
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.6/3557596832929915217
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.7/10386934055395314831
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.11.7/3557596832929915217
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.12.0/5056746222905752453
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.2.0/10047773857155985907
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.2.2/18053836298936336950
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.3.0/12548816621480535786
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.3.3/11081883392474770722
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.3.5/16311176246009842383
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.6.0/11982804814124138945
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.6.0/12055761203849489982
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.7.3/16061516852537040135
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.8.4/16354268377385700367
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.9.10/12813592349865671909
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.9.3/13948373885254993391
RENAMED
File without changes
|
{crackerjack-0.21.6 → crackerjack-0.21.7}/crackerjack/.ruff_cache/0.9.9/12813592349865671909
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|