crackerjack 0.24.10__tar.gz → 0.26.0__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.
Potentially problematic release.
This version of crackerjack might be problematic. Click here for more details.
- {crackerjack-0.24.10 → crackerjack-0.26.0}/.pre-commit-config-ai.yaml +7 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/.pre-commit-config.yaml +8 -1
- {crackerjack-0.24.10 → crackerjack-0.26.0}/CLAUDE.md +111 -34
- {crackerjack-0.24.10 → crackerjack-0.26.0}/PKG-INFO +187 -128
- {crackerjack-0.24.10 → crackerjack-0.26.0}/README-AI-AGENT.md +29 -20
- {crackerjack-0.24.10 → crackerjack-0.26.0}/README.md +185 -127
- {crackerjack-0.24.10 → crackerjack-0.26.0}/RULES.md +21 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/crackerjack/.pre-commit-config-ai.yaml +7 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/crackerjack/.pre-commit-config.yaml +8 -1
- {crackerjack-0.24.10 → crackerjack-0.26.0}/crackerjack/crackerjack.py +5 -5
- {crackerjack-0.24.10 → crackerjack-0.26.0/crackerjack}/pyproject.toml +4 -1
- {crackerjack-0.24.10/crackerjack → crackerjack-0.26.0}/pyproject.toml +4 -1
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/TESTING.md +17 -17
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/test_crackerjack.py +28 -4
- {crackerjack-0.24.10 → crackerjack-0.26.0}/uv.lock +45 -22
- {crackerjack-0.24.10 → crackerjack-0.26.0}/.envrc +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/.github/FUNDING.yml +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/.gitignore +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/.libcst.codemod.yaml +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/LICENSE +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/crackerjack/.gitignore +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/crackerjack/.libcst.codemod.yaml +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/crackerjack/.pdm.toml +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/crackerjack/__init__.py +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/crackerjack/__main__.py +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/crackerjack/errors.py +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/crackerjack/interactive.py +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/crackerjack/py313.py +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/__init__.py +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/conftest.py +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/data/comments_sample.txt +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/data/docstrings_sample.txt +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/data/expected_comments_sample.txt +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/data/init.py +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/test_crackerjack_runner.py +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/test_errors.py +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/test_interactive.py +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/test_interactive_run.py +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/test_main.py +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/test_multiline_functions.py +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/test_py313_advanced.py +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/test_py313_features.py +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/test_pytest_features.py +0 -0
- {crackerjack-0.24.10 → crackerjack-0.26.0}/tests/test_structured_errors.py +0 -0
|
@@ -66,6 +66,13 @@ repos:
|
|
|
66
66
|
- id: ruff-format
|
|
67
67
|
verbose: true
|
|
68
68
|
|
|
69
|
+
- repo: https://github.com/executablebooks/mdformat
|
|
70
|
+
rev: 0.7.18
|
|
71
|
+
hooks:
|
|
72
|
+
- id: mdformat
|
|
73
|
+
additional_dependencies:
|
|
74
|
+
- mdformat-ruff
|
|
75
|
+
|
|
69
76
|
# Code quality tier 2 - analysis
|
|
70
77
|
- repo: https://github.com/jendrikseipp/vulture
|
|
71
78
|
rev: 'v2.14'
|
|
@@ -22,7 +22,7 @@ repos:
|
|
|
22
22
|
|
|
23
23
|
# Package management - once structure is valid
|
|
24
24
|
- repo: https://github.com/astral-sh/uv-pre-commit
|
|
25
|
-
rev: 0.7.
|
|
25
|
+
rev: 0.7.21
|
|
26
26
|
hooks:
|
|
27
27
|
- id: uv-lock
|
|
28
28
|
files: ^pyproject\.toml$
|
|
@@ -48,6 +48,13 @@ repos:
|
|
|
48
48
|
- id: ruff-check
|
|
49
49
|
- id: ruff-format
|
|
50
50
|
|
|
51
|
+
- repo: https://github.com/executablebooks/mdformat
|
|
52
|
+
rev: 0.7.22
|
|
53
|
+
hooks:
|
|
54
|
+
- id: mdformat
|
|
55
|
+
additional_dependencies:
|
|
56
|
+
- mdformat-ruff
|
|
57
|
+
|
|
51
58
|
# Code quality tier 2 - analysis
|
|
52
59
|
- repo: https://github.com/jendrikseipp/vulture
|
|
53
60
|
rev: 'v2.14'
|
|
@@ -124,6 +124,7 @@ Crackerjack is designed with modern Python principles and consists of several ke
|
|
|
124
124
|
### Core Components
|
|
125
125
|
|
|
126
126
|
1. **Crackerjack** (`crackerjack.py`): Main class that orchestrates the entire workflow
|
|
127
|
+
|
|
127
128
|
- Manages configuration updates
|
|
128
129
|
- Runs pre-commit hooks
|
|
129
130
|
- Handles code cleaning (with enhanced error handling)
|
|
@@ -132,25 +133,29 @@ Crackerjack is designed with modern Python principles and consists of several ke
|
|
|
132
133
|
- Handles Git operations
|
|
133
134
|
- Integrates with Rich console for status output
|
|
134
135
|
|
|
135
|
-
|
|
136
|
+
1. **CodeCleaner**: Responsible for cleaning code
|
|
137
|
+
|
|
136
138
|
- Removes docstrings (with syntax-aware pass statement insertion)
|
|
137
139
|
- Removes line comments
|
|
138
140
|
- Removes extra whitespace
|
|
139
141
|
- Reformats code using Ruff
|
|
140
142
|
- Handles file encoding issues gracefully
|
|
141
143
|
|
|
142
|
-
|
|
144
|
+
1. **ConfigManager**: Handles configuration file management
|
|
145
|
+
|
|
143
146
|
- Updates pyproject.toml settings
|
|
144
147
|
- Manages configuration files (.gitignore, .pre-commit-config.yaml)
|
|
145
148
|
- Supports dynamic configuration based on project size
|
|
146
149
|
|
|
147
|
-
|
|
150
|
+
1. **ProjectManager**: Manages project-level operations
|
|
151
|
+
|
|
148
152
|
- Runs pre-commit hooks
|
|
149
153
|
- Updates package configurations
|
|
150
154
|
- Runs interactive hooks
|
|
151
155
|
- Detects project size for optimization
|
|
152
156
|
|
|
153
|
-
|
|
157
|
+
1. **ErrorHandler** (`errors.py`): Structured error handling system
|
|
158
|
+
|
|
154
159
|
- Provides consistent error codes and messages
|
|
155
160
|
- Handles graceful degradation on failures
|
|
156
161
|
- Supports both fatal and non-fatal error patterns
|
|
@@ -186,14 +191,15 @@ Crackerjack includes a Rich-based interactive UI that provides:
|
|
|
186
191
|
- **Task Dependencies**: Visual display of task relationships and execution order
|
|
187
192
|
|
|
188
193
|
The interactive mode runs a predefined workflow with tasks:
|
|
194
|
+
|
|
189
195
|
1. **Setup** - Initialize project structure
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
196
|
+
1. **Config** - Update configuration files
|
|
197
|
+
1. **Clean** - Remove docstrings and comments
|
|
198
|
+
1. **Hooks** - Run pre-commit hooks
|
|
199
|
+
1. **Test** - Execute test suite
|
|
200
|
+
1. **Version** - Bump version numbers
|
|
201
|
+
1. **Publish** - Publish to PyPI
|
|
202
|
+
1. **Commit** - Commit changes to Git
|
|
197
203
|
|
|
198
204
|
Access interactive mode with: `python -m crackerjack -i`
|
|
199
205
|
|
|
@@ -202,6 +208,7 @@ Access interactive mode with: `python -m crackerjack -i`
|
|
|
202
208
|
Crackerjack follows a single-file architecture for simplicity and maintainability:
|
|
203
209
|
|
|
204
210
|
### File Structure
|
|
211
|
+
|
|
205
212
|
- `crackerjack.py` - Main module (~3000 lines) containing all core functionality
|
|
206
213
|
- `errors.py` - Structured error handling system
|
|
207
214
|
- `interactive.py` - Rich-based interactive UI implementation
|
|
@@ -209,6 +216,7 @@ Crackerjack follows a single-file architecture for simplicity and maintainabilit
|
|
|
209
216
|
- `__main__.py` - Entry point for `python -m crackerjack`
|
|
210
217
|
|
|
211
218
|
### Key Implementation Details
|
|
219
|
+
|
|
212
220
|
- **Single-file Design**: Most functionality concentrated in `crackerjack.py` for easier maintenance
|
|
213
221
|
- **Type Safety**: Extensive use of protocols and type hints throughout
|
|
214
222
|
- **Error Handling**: Comprehensive error handling with structured error codes
|
|
@@ -253,6 +261,7 @@ Crackerjack follows a single-file architecture for simplicity and maintainabilit
|
|
|
253
261
|
## Development Guidelines
|
|
254
262
|
|
|
255
263
|
1. **Code Style**: Follow the Crackerjack style guide (see RULES.md):
|
|
264
|
+
|
|
256
265
|
- **Target Python 3.13+** - Use latest Python features
|
|
257
266
|
- Use static typing throughout with modern syntax (import typing as `t`)
|
|
258
267
|
- Use pathlib for file operations
|
|
@@ -260,7 +269,8 @@ Crackerjack follows a single-file architecture for simplicity and maintainabilit
|
|
|
260
269
|
- Use built-in collection types (`list[str]`, `dict[str, int]`) instead of typing equivalents
|
|
261
270
|
- Leverage Python 3.13+ performance and language improvements
|
|
262
271
|
|
|
263
|
-
|
|
272
|
+
1. **Testing Approach**:
|
|
273
|
+
|
|
264
274
|
- Write unit tests for all functionality
|
|
265
275
|
- Add benchmark tests for performance-critical code
|
|
266
276
|
- Tests are run in parallel by default
|
|
@@ -268,18 +278,21 @@ Crackerjack follows a single-file architecture for simplicity and maintainabilit
|
|
|
268
278
|
- Use pytest's `tmp_path` and `tmp_path_factory` fixtures
|
|
269
279
|
- Tests should be isolated and not affect the surrounding environment
|
|
270
280
|
|
|
271
|
-
|
|
281
|
+
1. **Dependencies**:
|
|
282
|
+
|
|
272
283
|
- UV for dependency management and tool execution
|
|
273
284
|
- Ruff for linting and formatting
|
|
274
285
|
- Pytest for testing
|
|
275
286
|
- All tools should be run through UV for environment isolation
|
|
276
287
|
|
|
277
|
-
|
|
288
|
+
1. **Python Version**:
|
|
289
|
+
|
|
278
290
|
- **Target: Python 3.13+** - Crackerjack requires Python 3.13 or newer
|
|
279
291
|
- Use modern Python 3.13+ features and syntax
|
|
280
292
|
- Code must be compatible with Python 3.13+ only
|
|
281
293
|
|
|
282
|
-
|
|
294
|
+
1. **Version Management**:
|
|
295
|
+
|
|
283
296
|
- Version bumping is handled through UV
|
|
284
297
|
- Follows semantic versioning
|
|
285
298
|
|
|
@@ -292,6 +305,7 @@ When generating code, AI assistants MUST follow these standards to ensure compli
|
|
|
292
305
|
### Refurb Standards (Modern Python Patterns up to 3.12)
|
|
293
306
|
|
|
294
307
|
**Use modern syntax and built-ins:**
|
|
308
|
+
|
|
295
309
|
- Use `pathlib.Path` instead of `os.path` operations
|
|
296
310
|
- Use `str.removeprefix()` and `str.removesuffix()` instead of string slicing
|
|
297
311
|
- Use `itertools.batched()` for chunking sequences (Python 3.12+)
|
|
@@ -300,20 +314,24 @@ When generating code, AI assistants MUST follow these standards to ensure compli
|
|
|
300
314
|
- Use `dict1 | dict2` for merging instead of `{**dict1, **dict2}`
|
|
301
315
|
|
|
302
316
|
**Use efficient built-in functions:**
|
|
317
|
+
|
|
303
318
|
- Use `any()` and `all()` instead of manual boolean loops
|
|
304
319
|
- Use list/dict comprehensions over manual loops when appropriate
|
|
305
320
|
- Use `enumerate()` instead of manual indexing with `range(len())`
|
|
306
321
|
- Use `zip()` for parallel iteration instead of manual indexing
|
|
307
322
|
|
|
308
323
|
**Resource management:**
|
|
324
|
+
|
|
309
325
|
- Always use context managers (`with` statements) for file operations
|
|
310
326
|
- Use `tempfile` module for temporary files instead of manual paths
|
|
311
327
|
- Prefer `subprocess.run()` over `subprocess.Popen()` when possible
|
|
312
328
|
|
|
313
329
|
**Example of good patterns:**
|
|
330
|
+
|
|
314
331
|
```python
|
|
315
332
|
# Good: Modern pathlib usage
|
|
316
333
|
from pathlib import Path
|
|
334
|
+
|
|
317
335
|
config_file = Path("config") / "settings.yaml"
|
|
318
336
|
if config_file.exists():
|
|
319
337
|
content = config_file.read_text(encoding="utf-8")
|
|
@@ -322,10 +340,12 @@ if config_file.exists():
|
|
|
322
340
|
if name.startswith("test_"):
|
|
323
341
|
name = name.removeprefix("test_")
|
|
324
342
|
|
|
343
|
+
|
|
325
344
|
# Good: Union types (Python 3.13+)
|
|
326
345
|
def process_data(data: str | bytes) -> dict[str, Any]:
|
|
327
346
|
pass
|
|
328
347
|
|
|
348
|
+
|
|
329
349
|
# Good: Context managers
|
|
330
350
|
with open(file_path, encoding="utf-8") as f:
|
|
331
351
|
data = f.read()
|
|
@@ -334,46 +354,55 @@ with open(file_path, encoding="utf-8") as f:
|
|
|
334
354
|
### Bandit Security Standards
|
|
335
355
|
|
|
336
356
|
**Never use dangerous functions:**
|
|
357
|
+
|
|
337
358
|
- Avoid `eval()`, `exec()`, or `compile()` with any user input
|
|
338
359
|
- Never use `subprocess.shell=True` or `os.system()`
|
|
339
360
|
- Don't use `pickle` with untrusted data
|
|
340
361
|
- Avoid `yaml.load()` - use `yaml.safe_load()` instead
|
|
341
362
|
|
|
342
363
|
**Cryptography and secrets:**
|
|
364
|
+
|
|
343
365
|
- Use `secrets` module for cryptographic operations, never `random`
|
|
344
366
|
- Never hardcode passwords, API keys, or secrets in source code
|
|
345
367
|
- Use environment variables or secure configuration for sensitive data
|
|
346
368
|
- Use `hashlib` with explicit algorithms, avoid MD5/SHA1 for security
|
|
347
369
|
|
|
348
370
|
**File and path security:**
|
|
371
|
+
|
|
349
372
|
- Always validate file paths to prevent directory traversal
|
|
350
373
|
- Use `tempfile.mkstemp()` instead of predictable temporary file names
|
|
351
374
|
- Always specify encoding when opening files
|
|
352
375
|
- Validate all external inputs before processing
|
|
353
376
|
|
|
354
377
|
**Database and injection prevention:**
|
|
378
|
+
|
|
355
379
|
- Use parameterized queries, never string concatenation for SQL
|
|
356
380
|
- Validate and sanitize all user inputs
|
|
357
381
|
- Use prepared statements for database operations
|
|
358
382
|
|
|
359
383
|
**Example of secure patterns:**
|
|
384
|
+
|
|
360
385
|
```python
|
|
361
386
|
# Good: Secure random generation
|
|
362
387
|
import secrets
|
|
388
|
+
|
|
363
389
|
token = secrets.token_urlsafe(32)
|
|
364
390
|
|
|
365
391
|
# Good: Safe subprocess usage
|
|
366
392
|
import subprocess
|
|
393
|
+
|
|
367
394
|
result = subprocess.run(["ls", "-la"], capture_output=True, text=True)
|
|
368
395
|
|
|
369
396
|
# Good: Secure file operations
|
|
370
397
|
import tempfile
|
|
371
|
-
|
|
398
|
+
|
|
399
|
+
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", delete=False) as f:
|
|
372
400
|
f.write(data)
|
|
373
401
|
temp_path = f.name
|
|
374
402
|
|
|
375
403
|
# Good: Environment variables for secrets
|
|
376
404
|
import os
|
|
405
|
+
|
|
377
406
|
api_key = os.environ.get("API_KEY")
|
|
378
407
|
if not api_key:
|
|
379
408
|
raise ValueError("API_KEY environment variable required")
|
|
@@ -385,39 +414,46 @@ cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
|
|
|
385
414
|
### Pyright Type Safety Standards
|
|
386
415
|
|
|
387
416
|
**Always use explicit type annotations:**
|
|
417
|
+
|
|
388
418
|
- Function parameters must have type hints
|
|
389
419
|
- Function return types must be annotated
|
|
390
420
|
- Class attributes should have type annotations
|
|
391
421
|
- Use `from __future__ import annotations` for forward references
|
|
392
422
|
|
|
393
423
|
**Handle Optional types properly:**
|
|
424
|
+
|
|
394
425
|
- Use `str | None` instead of `Optional[str]` (required for Python 3.13+)
|
|
395
426
|
- Always check for None before using optional values
|
|
396
427
|
- Use explicit `assert` statements or type guards when narrowing types
|
|
397
428
|
|
|
398
429
|
**Generic types and collections:**
|
|
430
|
+
|
|
399
431
|
- Use `list[str]` instead of `List[str]` (required for Python 3.13+)
|
|
400
432
|
- Use `dict[str, Any]` instead of `Dict[str, Any]` (required for Python 3.13+)
|
|
401
433
|
- Properly type generic classes with `TypeVar` when needed
|
|
402
434
|
- Use `Sequence` or `Iterable` for function parameters when appropriate
|
|
403
435
|
|
|
404
436
|
**Protocol and ABC usage:**
|
|
437
|
+
|
|
405
438
|
- Prefer `typing.Protocol` over abstract base classes for duck typing
|
|
406
439
|
- Use `@runtime_checkable` when protocols need runtime checks
|
|
407
440
|
- Define clear interfaces with protocols
|
|
408
441
|
|
|
409
442
|
**Python 3.13+ specific features:**
|
|
443
|
+
|
|
410
444
|
- Leverage improved error messages and performance optimizations
|
|
411
445
|
- Use enhanced type system features available in 3.13+
|
|
412
446
|
- Take advantage of improved pathlib and asyncio features
|
|
413
447
|
- Use any new syntax or standard library improvements
|
|
414
448
|
|
|
415
449
|
**Import and module organization:**
|
|
450
|
+
|
|
416
451
|
- Import types in TYPE_CHECKING blocks when needed for forward references
|
|
417
452
|
- Use proper module-level `__all__` declarations
|
|
418
453
|
- Organize imports: standard library, third-party, local imports
|
|
419
454
|
|
|
420
455
|
**Example of proper typing:**
|
|
456
|
+
|
|
421
457
|
```python
|
|
422
458
|
from __future__ import annotations
|
|
423
459
|
|
|
@@ -427,10 +463,12 @@ from pathlib import Path
|
|
|
427
463
|
if TYPE_CHECKING:
|
|
428
464
|
from collections.abc import Sequence
|
|
429
465
|
|
|
466
|
+
|
|
430
467
|
@runtime_checkable
|
|
431
468
|
class Writable(Protocol):
|
|
432
469
|
def write(self, data: str) -> None: ...
|
|
433
470
|
|
|
471
|
+
|
|
434
472
|
def process_files(
|
|
435
473
|
paths: Sequence[Path],
|
|
436
474
|
output: Writable,
|
|
@@ -447,6 +485,7 @@ def process_files(
|
|
|
447
485
|
|
|
448
486
|
return stats
|
|
449
487
|
|
|
488
|
+
|
|
450
489
|
# Good: Type narrowing with assertion
|
|
451
490
|
def validate_config(config: dict[str, str | None]) -> dict[str, str]:
|
|
452
491
|
"""Validate that all config values are non-None."""
|
|
@@ -460,6 +499,7 @@ def validate_config(config: dict[str, str | None]) -> dict[str, str]:
|
|
|
460
499
|
### Integration with Pre-commit Hooks
|
|
461
500
|
|
|
462
501
|
These standards align with the project's pre-commit hooks:
|
|
502
|
+
|
|
463
503
|
- **Refurb**: Automatically suggests modern Python patterns
|
|
464
504
|
- **Bandit**: Scans for security vulnerabilities
|
|
465
505
|
- **Pyright**: Enforces type safety
|
|
@@ -480,13 +520,16 @@ By following these guidelines during code generation, AI assistants will produce
|
|
|
480
520
|
### CodeCleaner Docstring Removal Fix (December 2024)
|
|
481
521
|
|
|
482
522
|
#### Problem Description
|
|
523
|
+
|
|
483
524
|
The original `remove_docstrings` method in the `CodeCleaner` class had a critical bug where removing docstrings from functions or methods that contained only docstrings would result in syntactically invalid Python code. This caused the `-x` (clean) flag to fail with Ruff formatting errors.
|
|
484
525
|
|
|
485
526
|
**Example of problematic code:**
|
|
527
|
+
|
|
486
528
|
```python
|
|
487
529
|
def empty_function():
|
|
488
530
|
"""This function has only a docstring."""
|
|
489
531
|
|
|
532
|
+
|
|
490
533
|
class TestClass:
|
|
491
534
|
"""Class docstring."""
|
|
492
535
|
|
|
@@ -495,78 +538,95 @@ class TestClass:
|
|
|
495
538
|
```
|
|
496
539
|
|
|
497
540
|
**After broken docstring removal:**
|
|
541
|
+
|
|
498
542
|
```python
|
|
499
543
|
def empty_function():
|
|
544
|
+
pass
|
|
545
|
+
|
|
500
546
|
|
|
501
547
|
class TestClass:
|
|
548
|
+
pass
|
|
502
549
|
|
|
503
550
|
def method_with_docstring_only(self):
|
|
551
|
+
pass
|
|
504
552
|
```
|
|
505
553
|
|
|
506
554
|
This resulted in syntax errors: `expected an indented block after function definition`.
|
|
507
555
|
|
|
508
556
|
#### Root Cause Analysis
|
|
557
|
+
|
|
509
558
|
The issue occurred because the original algorithm:
|
|
559
|
+
|
|
510
560
|
1. Removed docstrings without checking if they were the only content in function/method bodies
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
561
|
+
1. Did not track function indentation levels
|
|
562
|
+
1. Failed to detect when `pass` statements were needed to maintain valid Python syntax
|
|
563
|
+
1. Had a bug in `_handle_docstring_end` that returned `True` instead of `False`
|
|
514
564
|
|
|
515
565
|
#### Solution Implementation
|
|
566
|
+
|
|
516
567
|
The fix involved a comprehensive rewrite of the docstring removal algorithm with these enhancements:
|
|
517
568
|
|
|
518
569
|
1. **Function Context Tracking**: Added `function_indent` and `removed_docstring` to track the context of function/class definitions
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
570
|
+
1. **Lookahead Logic**: Implemented `_needs_pass_statement()` helper method that analyzes remaining code to determine if a `pass` statement is needed
|
|
571
|
+
1. **Automatic Pass Insertion**: Added logic to insert properly indented `pass` statements when removing docstrings leaves empty function bodies
|
|
572
|
+
1. **Single vs Multi-line Handling**: Enhanced handling for both single-line and multi-line docstrings
|
|
573
|
+
1. **Bug Fixes**: Fixed the `_handle_docstring_end` method to return correct boolean values
|
|
523
574
|
|
|
524
575
|
#### Key Changes Made
|
|
525
576
|
|
|
526
577
|
**Enhanced State Management:**
|
|
578
|
+
|
|
527
579
|
```python
|
|
528
580
|
docstring_state = {
|
|
529
581
|
"in_docstring": False,
|
|
530
582
|
"delimiter": None,
|
|
531
583
|
"waiting": False,
|
|
532
|
-
"function_indent": 0,
|
|
533
|
-
"removed_docstring": False
|
|
584
|
+
"function_indent": 0, # NEW: Track function indentation
|
|
585
|
+
"removed_docstring": False, # NEW: Track if we just removed a docstring
|
|
534
586
|
}
|
|
535
587
|
```
|
|
536
588
|
|
|
537
589
|
**New Helper Method:**
|
|
590
|
+
|
|
538
591
|
```python
|
|
539
|
-
def _needs_pass_statement(
|
|
592
|
+
def _needs_pass_statement(
|
|
593
|
+
self, lines: list[str], start_index: int, function_indent: int
|
|
594
|
+
) -> bool:
|
|
540
595
|
"""Check if we need to add a pass statement after removing a docstring."""
|
|
541
596
|
# Looks ahead to see if there are any statements at the correct indentation level
|
|
542
597
|
# Returns True if no statements found (pass needed), False if statements exist
|
|
543
598
|
```
|
|
544
599
|
|
|
545
600
|
**Enhanced Docstring Removal Logic:**
|
|
601
|
+
|
|
546
602
|
- For single-line docstrings: Check immediately if pass statement needed
|
|
547
603
|
- For multi-line docstrings: Check after docstring end is reached
|
|
548
604
|
- Proper indentation calculation: `function_indent + 4` spaces for pass statements
|
|
549
605
|
- Context-aware pass insertion that respects Python indentation rules
|
|
550
606
|
|
|
551
607
|
#### Testing Enhancements
|
|
608
|
+
|
|
552
609
|
Added comprehensive tests to prevent regression:
|
|
553
610
|
|
|
554
611
|
1. **Enhanced Existing Test**: Added AST parsing validation to `test_code_cleaner_remove_docstrings`
|
|
555
|
-
|
|
612
|
+
1. **New Comprehensive Test**: Created `test_code_cleaner_remove_docstrings_empty_functions` that specifically tests:
|
|
556
613
|
- Functions with only docstrings get `pass` statements
|
|
557
614
|
- Functions with code after docstrings don't get unnecessary `pass` statements
|
|
558
615
|
- Classes with only docstrings are handled correctly
|
|
559
616
|
- All resulting code is syntactically valid Python
|
|
560
617
|
|
|
561
618
|
#### Test Cases Covered
|
|
619
|
+
|
|
562
620
|
```python
|
|
563
621
|
# Test cases that now pass:
|
|
564
622
|
def empty_function():
|
|
565
623
|
"""This function has only a docstring."""
|
|
566
624
|
# Becomes: def empty_function():\n pass
|
|
567
625
|
|
|
626
|
+
|
|
568
627
|
class TestClass:
|
|
569
628
|
"""Class docstring."""
|
|
629
|
+
|
|
570
630
|
# Becomes: class TestClass:\n (no pass needed for classes)
|
|
571
631
|
|
|
572
632
|
def method_with_docstring_only(self):
|
|
@@ -580,6 +640,7 @@ class TestClass:
|
|
|
580
640
|
```
|
|
581
641
|
|
|
582
642
|
#### Impact and Benefits
|
|
643
|
+
|
|
583
644
|
- **Immediate Fix**: The `-x` flag now works without causing syntax errors
|
|
584
645
|
- **Robust Solution**: Handles all edge cases including nested functions, mixed indentation, and various docstring styles
|
|
585
646
|
- **Maintains Functionality**: All existing docstring removal capabilities preserved
|
|
@@ -587,13 +648,16 @@ class TestClass:
|
|
|
587
648
|
- **Performance**: Minimal performance impact due to efficient lookahead algorithm
|
|
588
649
|
|
|
589
650
|
#### Backward Compatibility
|
|
651
|
+
|
|
590
652
|
This fix is fully backward compatible:
|
|
653
|
+
|
|
591
654
|
- All existing functionality is preserved
|
|
592
655
|
- No breaking changes to the API
|
|
593
656
|
- Existing tests continue to pass
|
|
594
657
|
- Only adds pass statements where syntactically required
|
|
595
658
|
|
|
596
659
|
#### Files Modified
|
|
660
|
+
|
|
597
661
|
- `crackerjack/crackerjack.py`: Enhanced `remove_docstrings` method and added `_needs_pass_statement` helper
|
|
598
662
|
- `tests/test_crackerjack.py`: Added syntax validation and comprehensive test cases
|
|
599
663
|
|
|
@@ -602,32 +666,37 @@ This fix ensures that the code cleaning functionality (`-x` flag) works reliably
|
|
|
602
666
|
### Enhanced Error Handling (December 2024)
|
|
603
667
|
|
|
604
668
|
#### Problem Description
|
|
669
|
+
|
|
605
670
|
The original code cleaner had basic error handling but would stop processing if any individual file encountered an error during cleaning. This could prevent the tool from cleaning other files in the project, particularly in cases involving:
|
|
671
|
+
|
|
606
672
|
- File permission issues
|
|
607
673
|
- Encoding problems (non-UTF-8 files)
|
|
608
674
|
- File system errors (file locks, disk space)
|
|
609
675
|
- Malformed Python code that causes parsing issues
|
|
610
676
|
|
|
611
677
|
#### Solution Implementation
|
|
678
|
+
|
|
612
679
|
Enhanced the `clean_file` method with comprehensive error handling that:
|
|
613
680
|
|
|
614
681
|
1. **Graceful Degradation**: Each cleaning step (remove comments, remove docstrings, remove whitespace, reformat) is wrapped in individual try-catch blocks
|
|
615
|
-
|
|
616
|
-
|
|
682
|
+
1. **Step-by-Step Recovery**: If one cleaning step fails, the cleaner falls back to the previous state and continues with remaining steps
|
|
683
|
+
1. **Detailed Error Reporting**: Uses the structured error handling system with specific error codes:
|
|
617
684
|
- `PERMISSION_ERROR (6002)`: File permission issues
|
|
618
685
|
- `FILE_WRITE_ERROR (6004)`: File system errors
|
|
619
686
|
- `FILE_READ_ERROR (6003)`: Encoding or read errors
|
|
620
687
|
- `UNEXPECTED_ERROR (9999)`: Unexpected errors with detailed context
|
|
621
|
-
|
|
622
|
-
|
|
688
|
+
1. **Non-Fatal Errors**: All errors are handled gracefully without stopping the overall cleaning process (`exit_on_error=False`)
|
|
689
|
+
1. **UTF-8 Encoding**: Explicit UTF-8 encoding for file operations to prevent encoding issues
|
|
623
690
|
|
|
624
691
|
#### Key Improvements
|
|
692
|
+
|
|
625
693
|
- **Resilient Processing**: The cleaner continues processing other files even if individual files fail
|
|
626
694
|
- **Clear Diagnostics**: Detailed error messages with recovery suggestions help users understand and fix issues
|
|
627
695
|
- **Fallback Mechanism**: Each cleaning step can fall back to the original code if specific operations fail
|
|
628
696
|
- **Step Isolation**: Failure in one cleaning step doesn't prevent other steps from running
|
|
629
697
|
|
|
630
698
|
#### Example Error Handling Flow
|
|
699
|
+
|
|
631
700
|
```python
|
|
632
701
|
try:
|
|
633
702
|
code = self.remove_line_comments(code)
|
|
@@ -643,6 +712,7 @@ except Exception as e:
|
|
|
643
712
|
```
|
|
644
713
|
|
|
645
714
|
#### Benefits
|
|
715
|
+
|
|
646
716
|
- **Robustness**: Code cleaning continues even when encountering problematic files
|
|
647
717
|
- **Better User Experience**: Clear error messages with actionable recovery suggestions
|
|
648
718
|
- **Debugging Support**: Detailed error information helps identify and fix underlying issues
|
|
@@ -650,6 +720,7 @@ except Exception as e:
|
|
|
650
720
|
- **Project-Wide Cleaning**: Ensures that cleaning doesn't stop due to individual file issues
|
|
651
721
|
|
|
652
722
|
#### Files Modified
|
|
723
|
+
|
|
653
724
|
- `crackerjack/crackerjack.py`: Enhanced `clean_file` method with comprehensive error handling
|
|
654
725
|
|
|
655
726
|
This enhancement ensures that the code cleaner (`-x` flag) can handle edge cases and problematic files while continuing to process the rest of the project successfully.
|
|
@@ -659,14 +730,20 @@ This enhancement ensures that the code cleaner (`-x` flag) can handle edge cases
|
|
|
659
730
|
**MANDATORY: Before marking any task as complete, AI assistants MUST:**
|
|
660
731
|
|
|
661
732
|
1. **Run crackerjack verification**: Execute `python -m crackerjack -t --ai-agent` to run all quality checks and tests with AI-optimized output
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
733
|
+
1. **Fix any issues found**: Address all formatting, linting, type checking, and test failures
|
|
734
|
+
1. **Re-run verification**: Ensure crackerjack passes completely (all hooks pass, all tests pass)
|
|
735
|
+
1. **Document verification**: Mention that crackerjack verification was completed successfully
|
|
665
736
|
|
|
666
737
|
**Why this is critical:**
|
|
738
|
+
|
|
667
739
|
- Ensures all code meets project quality standards
|
|
668
740
|
- Prevents broken code from being committed
|
|
669
741
|
- Maintains consistency with project development workflow
|
|
670
742
|
- Catches issues early before they become problems
|
|
671
743
|
|
|
672
744
|
**Never skip crackerjack verification** - it's the project's standard quality gate.
|
|
745
|
+
|
|
746
|
+
## Development Memories
|
|
747
|
+
|
|
748
|
+
- Non-critical type errors are still errors that need to be fixed to pass crackerjack validation
|
|
749
|
+
- test directory files need to pass pyright tests as well in order for crackerjack to successfully complete
|