crackerjack 0.33.0__py3-none-any.whl → 0.33.2__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.
Potentially problematic release.
This version of crackerjack might be problematic. Click here for more details.
- crackerjack/__main__.py +1350 -34
- crackerjack/adapters/__init__.py +17 -0
- crackerjack/adapters/lsp_client.py +358 -0
- crackerjack/adapters/rust_tool_adapter.py +194 -0
- crackerjack/adapters/rust_tool_manager.py +193 -0
- crackerjack/adapters/skylos_adapter.py +231 -0
- crackerjack/adapters/zuban_adapter.py +560 -0
- crackerjack/agents/base.py +7 -3
- crackerjack/agents/coordinator.py +271 -33
- crackerjack/agents/documentation_agent.py +9 -15
- crackerjack/agents/dry_agent.py +3 -15
- crackerjack/agents/formatting_agent.py +1 -1
- crackerjack/agents/import_optimization_agent.py +36 -180
- crackerjack/agents/performance_agent.py +17 -98
- crackerjack/agents/performance_helpers.py +7 -31
- crackerjack/agents/proactive_agent.py +1 -3
- crackerjack/agents/refactoring_agent.py +16 -85
- crackerjack/agents/refactoring_helpers.py +7 -42
- crackerjack/agents/security_agent.py +9 -48
- crackerjack/agents/test_creation_agent.py +356 -513
- crackerjack/agents/test_specialist_agent.py +0 -4
- crackerjack/api.py +6 -25
- crackerjack/cli/cache_handlers.py +204 -0
- crackerjack/cli/cache_handlers_enhanced.py +683 -0
- crackerjack/cli/facade.py +100 -0
- crackerjack/cli/handlers.py +224 -9
- crackerjack/cli/interactive.py +6 -4
- crackerjack/cli/options.py +642 -55
- crackerjack/cli/utils.py +2 -1
- crackerjack/code_cleaner.py +58 -117
- crackerjack/config/global_lock_config.py +8 -48
- crackerjack/config/hooks.py +53 -62
- crackerjack/core/async_workflow_orchestrator.py +24 -34
- crackerjack/core/autofix_coordinator.py +3 -17
- crackerjack/core/enhanced_container.py +4 -13
- crackerjack/core/file_lifecycle.py +12 -89
- crackerjack/core/performance.py +2 -2
- crackerjack/core/performance_monitor.py +15 -55
- crackerjack/core/phase_coordinator.py +104 -204
- crackerjack/core/resource_manager.py +14 -90
- crackerjack/core/service_watchdog.py +62 -95
- crackerjack/core/session_coordinator.py +149 -0
- crackerjack/core/timeout_manager.py +14 -72
- crackerjack/core/websocket_lifecycle.py +13 -78
- crackerjack/core/workflow_orchestrator.py +171 -174
- crackerjack/docs/INDEX.md +11 -0
- crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
- crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
- crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
- crackerjack/docs/generated/api/SERVICES.md +1252 -0
- crackerjack/documentation/__init__.py +31 -0
- crackerjack/documentation/ai_templates.py +756 -0
- crackerjack/documentation/dual_output_generator.py +765 -0
- crackerjack/documentation/mkdocs_integration.py +518 -0
- crackerjack/documentation/reference_generator.py +977 -0
- crackerjack/dynamic_config.py +55 -50
- crackerjack/executors/async_hook_executor.py +10 -15
- crackerjack/executors/cached_hook_executor.py +117 -43
- crackerjack/executors/hook_executor.py +8 -34
- crackerjack/executors/hook_lock_manager.py +26 -183
- crackerjack/executors/individual_hook_executor.py +13 -11
- crackerjack/executors/lsp_aware_hook_executor.py +270 -0
- crackerjack/executors/tool_proxy.py +417 -0
- crackerjack/hooks/lsp_hook.py +79 -0
- crackerjack/intelligence/adaptive_learning.py +25 -10
- crackerjack/intelligence/agent_orchestrator.py +2 -5
- crackerjack/intelligence/agent_registry.py +34 -24
- crackerjack/intelligence/agent_selector.py +5 -7
- crackerjack/interactive.py +17 -6
- crackerjack/managers/async_hook_manager.py +0 -1
- crackerjack/managers/hook_manager.py +79 -1
- crackerjack/managers/publish_manager.py +44 -8
- crackerjack/managers/test_command_builder.py +1 -15
- crackerjack/managers/test_executor.py +1 -3
- crackerjack/managers/test_manager.py +98 -7
- crackerjack/managers/test_manager_backup.py +10 -9
- crackerjack/mcp/cache.py +2 -2
- crackerjack/mcp/client_runner.py +1 -1
- crackerjack/mcp/context.py +191 -68
- crackerjack/mcp/dashboard.py +7 -5
- crackerjack/mcp/enhanced_progress_monitor.py +31 -28
- crackerjack/mcp/file_monitor.py +30 -23
- crackerjack/mcp/progress_components.py +31 -21
- crackerjack/mcp/progress_monitor.py +50 -53
- crackerjack/mcp/rate_limiter.py +6 -6
- crackerjack/mcp/server_core.py +17 -16
- crackerjack/mcp/service_watchdog.py +2 -1
- crackerjack/mcp/state.py +4 -7
- crackerjack/mcp/task_manager.py +11 -9
- crackerjack/mcp/tools/core_tools.py +173 -32
- crackerjack/mcp/tools/error_analyzer.py +3 -2
- crackerjack/mcp/tools/execution_tools.py +8 -10
- crackerjack/mcp/tools/execution_tools_backup.py +42 -30
- crackerjack/mcp/tools/intelligence_tool_registry.py +7 -5
- crackerjack/mcp/tools/intelligence_tools.py +5 -2
- crackerjack/mcp/tools/monitoring_tools.py +33 -70
- crackerjack/mcp/tools/proactive_tools.py +24 -11
- crackerjack/mcp/tools/progress_tools.py +5 -8
- crackerjack/mcp/tools/utility_tools.py +20 -14
- crackerjack/mcp/tools/workflow_executor.py +62 -40
- crackerjack/mcp/websocket/app.py +8 -0
- crackerjack/mcp/websocket/endpoints.py +352 -357
- crackerjack/mcp/websocket/jobs.py +40 -57
- crackerjack/mcp/websocket/monitoring_endpoints.py +2935 -0
- crackerjack/mcp/websocket/server.py +7 -25
- crackerjack/mcp/websocket/websocket_handler.py +6 -17
- crackerjack/mixins/__init__.py +0 -2
- crackerjack/mixins/error_handling.py +1 -70
- crackerjack/models/config.py +12 -1
- crackerjack/models/config_adapter.py +49 -1
- crackerjack/models/protocols.py +122 -122
- crackerjack/models/resource_protocols.py +55 -210
- crackerjack/monitoring/ai_agent_watchdog.py +13 -13
- crackerjack/monitoring/metrics_collector.py +426 -0
- crackerjack/monitoring/regression_prevention.py +8 -8
- crackerjack/monitoring/websocket_server.py +643 -0
- crackerjack/orchestration/advanced_orchestrator.py +11 -6
- crackerjack/orchestration/coverage_improvement.py +3 -3
- crackerjack/orchestration/execution_strategies.py +26 -6
- crackerjack/orchestration/test_progress_streamer.py +8 -5
- crackerjack/plugins/base.py +2 -2
- crackerjack/plugins/hooks.py +7 -0
- crackerjack/plugins/managers.py +11 -8
- crackerjack/security/__init__.py +0 -1
- crackerjack/security/audit.py +6 -35
- crackerjack/services/anomaly_detector.py +392 -0
- crackerjack/services/api_extractor.py +615 -0
- crackerjack/services/backup_service.py +2 -2
- crackerjack/services/bounded_status_operations.py +15 -152
- crackerjack/services/cache.py +127 -1
- crackerjack/services/changelog_automation.py +395 -0
- crackerjack/services/config.py +15 -9
- crackerjack/services/config_merge.py +19 -80
- crackerjack/services/config_template.py +506 -0
- crackerjack/services/contextual_ai_assistant.py +48 -22
- crackerjack/services/coverage_badge_service.py +171 -0
- crackerjack/services/coverage_ratchet.py +27 -25
- crackerjack/services/debug.py +3 -3
- crackerjack/services/dependency_analyzer.py +460 -0
- crackerjack/services/dependency_monitor.py +14 -11
- crackerjack/services/documentation_generator.py +491 -0
- crackerjack/services/documentation_service.py +675 -0
- crackerjack/services/enhanced_filesystem.py +6 -5
- crackerjack/services/enterprise_optimizer.py +865 -0
- crackerjack/services/error_pattern_analyzer.py +676 -0
- crackerjack/services/file_hasher.py +1 -1
- crackerjack/services/git.py +8 -25
- crackerjack/services/health_metrics.py +10 -8
- crackerjack/services/heatmap_generator.py +735 -0
- crackerjack/services/initialization.py +11 -30
- crackerjack/services/input_validator.py +5 -97
- crackerjack/services/intelligent_commit.py +327 -0
- crackerjack/services/log_manager.py +15 -12
- crackerjack/services/logging.py +4 -3
- crackerjack/services/lsp_client.py +628 -0
- crackerjack/services/memory_optimizer.py +19 -87
- crackerjack/services/metrics.py +42 -33
- crackerjack/services/parallel_executor.py +9 -67
- crackerjack/services/pattern_cache.py +1 -1
- crackerjack/services/pattern_detector.py +6 -6
- crackerjack/services/performance_benchmarks.py +18 -59
- crackerjack/services/performance_cache.py +20 -81
- crackerjack/services/performance_monitor.py +27 -95
- crackerjack/services/predictive_analytics.py +510 -0
- crackerjack/services/quality_baseline.py +234 -0
- crackerjack/services/quality_baseline_enhanced.py +646 -0
- crackerjack/services/quality_intelligence.py +785 -0
- crackerjack/services/regex_patterns.py +618 -524
- crackerjack/services/regex_utils.py +43 -123
- crackerjack/services/secure_path_utils.py +5 -164
- crackerjack/services/secure_status_formatter.py +30 -141
- crackerjack/services/secure_subprocess.py +11 -92
- crackerjack/services/security.py +9 -41
- crackerjack/services/security_logger.py +12 -24
- crackerjack/services/server_manager.py +124 -16
- crackerjack/services/status_authentication.py +16 -159
- crackerjack/services/status_security_manager.py +4 -131
- crackerjack/services/thread_safe_status_collector.py +19 -125
- crackerjack/services/unified_config.py +21 -13
- crackerjack/services/validation_rate_limiter.py +5 -54
- crackerjack/services/version_analyzer.py +459 -0
- crackerjack/services/version_checker.py +1 -1
- crackerjack/services/websocket_resource_limiter.py +10 -144
- crackerjack/services/zuban_lsp_service.py +390 -0
- crackerjack/slash_commands/__init__.py +2 -7
- crackerjack/slash_commands/run.md +2 -2
- crackerjack/tools/validate_input_validator_patterns.py +14 -40
- crackerjack/tools/validate_regex_patterns.py +19 -48
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/METADATA +196 -25
- crackerjack-0.33.2.dist-info/RECORD +229 -0
- crackerjack/CLAUDE.md +0 -207
- crackerjack/RULES.md +0 -380
- crackerjack/py313.py +0 -234
- crackerjack-0.33.0.dist-info/RECORD +0 -187
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/WHEEL +0 -0
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Utility functions for immediate regex pattern testing and validation.
|
|
3
|
-
|
|
4
|
-
Provides quick functions for testing regex patterns before adding them
|
|
5
|
-
to the centralized registry, and utilities for migrating existing re.sub() calls.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
1
|
import re
|
|
9
2
|
import typing as t
|
|
10
3
|
from pathlib import Path
|
|
@@ -18,11 +11,6 @@ def test_pattern_immediately(
|
|
|
18
11
|
test_cases: list[tuple[str, str]],
|
|
19
12
|
description: str = "",
|
|
20
13
|
) -> dict[str, t.Any]:
|
|
21
|
-
"""
|
|
22
|
-
Test a regex pattern immediately without adding to registry.
|
|
23
|
-
|
|
24
|
-
Returns a report of test results for quick validation.
|
|
25
|
-
"""
|
|
26
14
|
results: dict[str, t.Any] = {
|
|
27
15
|
"pattern": pattern,
|
|
28
16
|
"replacement": replacement,
|
|
@@ -33,9 +21,6 @@ def test_pattern_immediately(
|
|
|
33
21
|
"errors": [],
|
|
34
22
|
}
|
|
35
23
|
|
|
36
|
-
# Check for forbidden replacement syntax first using safe patterns\n forbidden_checks = [\n (r\"\\\\g\\\\s*<\\\\s*\\\\d+\\\\s*>\", \"\\\\g < 1 > with spaces\"),\n (r\"\\\\g<\\\\s+\\\\d+>\", \"\\\\g< 1> with space after <\"),\n (r\"\\\\g<\\\\d+\\\\s+>\", \"\\\\g<1 > with space before >\"),\n ]
|
|
37
|
-
|
|
38
|
-
# Validate pattern compilation using safe cache
|
|
39
24
|
try:
|
|
40
25
|
compiled = CompiledPatternCache.get_compiled_pattern(pattern)
|
|
41
26
|
except ValueError as e:
|
|
@@ -43,7 +28,6 @@ def test_pattern_immediately(
|
|
|
43
28
|
results["all_passed"] = False
|
|
44
29
|
return results
|
|
45
30
|
|
|
46
|
-
# Test all cases
|
|
47
31
|
for i, (input_text, expected) in enumerate(test_cases):
|
|
48
32
|
try:
|
|
49
33
|
result = compiled.sub(replacement, input_text)
|
|
@@ -71,7 +55,6 @@ def test_pattern_immediately(
|
|
|
71
55
|
)
|
|
72
56
|
results["all_passed"] = False
|
|
73
57
|
|
|
74
|
-
# Safety warnings
|
|
75
58
|
if ".*.*" in pattern:
|
|
76
59
|
results["warnings"].append(
|
|
77
60
|
"Multiple .* constructs may cause performance issues"
|
|
@@ -85,7 +68,6 @@ def test_pattern_immediately(
|
|
|
85
68
|
|
|
86
69
|
|
|
87
70
|
def print_pattern_test_report(results: dict[str, t.Any]) -> None:
|
|
88
|
-
"""Print a formatted report of pattern test results."""
|
|
89
71
|
print("\n🔍 REGEX PATTERN TEST REPORT")
|
|
90
72
|
print("=" * 50)
|
|
91
73
|
print(f"Pattern: {results['pattern']}")
|
|
@@ -95,25 +77,25 @@ def print_pattern_test_report(results: dict[str, t.Any]) -> None:
|
|
|
95
77
|
print()
|
|
96
78
|
|
|
97
79
|
if results["errors"]:
|
|
98
|
-
print("❌ ERRORS:")
|
|
80
|
+
print("❌ ERRORS: ")
|
|
99
81
|
for error in results["errors"]:
|
|
100
|
-
print(f"
|
|
82
|
+
print(f" • {error}")
|
|
101
83
|
print()
|
|
102
84
|
|
|
103
85
|
if results["warnings"]:
|
|
104
|
-
print("⚠️
|
|
86
|
+
print("⚠️ WARNINGS: ")
|
|
105
87
|
for warning in results["warnings"]:
|
|
106
|
-
print(f"
|
|
88
|
+
print(f" • {warning}")
|
|
107
89
|
print()
|
|
108
90
|
|
|
109
|
-
print("📋 TEST CASES:")
|
|
91
|
+
print("📋 TEST CASES: ")
|
|
110
92
|
for test in results["test_results"]:
|
|
111
93
|
status = "✅ PASS" if test["passed"] else "❌ FAIL"
|
|
112
94
|
print(
|
|
113
|
-
f"
|
|
95
|
+
f" {status} Test {test['test_case']}: '{test['input']}' → '{test['actual']}'"
|
|
114
96
|
)
|
|
115
97
|
if not test["passed"]:
|
|
116
|
-
print(f"
|
|
98
|
+
print(f" Expected: '{test['expected']}'")
|
|
117
99
|
|
|
118
100
|
print(
|
|
119
101
|
f"\n🎯 OVERALL: {'✅ ALL TESTS PASSED' if results['all_passed'] else '❌ TESTS FAILED'}"
|
|
@@ -127,21 +109,19 @@ def quick_pattern_test(
|
|
|
127
109
|
test_cases: list[tuple[str, str]],
|
|
128
110
|
description: str = "",
|
|
129
111
|
) -> bool:
|
|
130
|
-
"""Quick test function that returns True if all tests pass."""
|
|
131
112
|
results = test_pattern_immediately(pattern, replacement, test_cases, description)
|
|
132
113
|
print_pattern_test_report(results)
|
|
133
|
-
|
|
114
|
+
passed: bool = results["all_passed"]
|
|
115
|
+
return passed
|
|
134
116
|
|
|
135
117
|
|
|
136
118
|
def find_safe_pattern_for_text(text: str) -> list[str]:
|
|
137
|
-
"""Find which existing safe patterns would match the given text."""
|
|
138
119
|
matches = []
|
|
139
120
|
for name, pattern in SAFE_PATTERNS.items():
|
|
140
121
|
try:
|
|
141
122
|
if pattern.test(text):
|
|
142
123
|
matches.append(name)
|
|
143
124
|
except Exception:
|
|
144
|
-
# Skip patterns that error
|
|
145
125
|
continue
|
|
146
126
|
return matches
|
|
147
127
|
|
|
@@ -149,17 +129,6 @@ def find_safe_pattern_for_text(text: str) -> list[str]:
|
|
|
149
129
|
def suggest_migration_for_re_sub(
|
|
150
130
|
original_pattern: str, original_replacement: str, sample_text: str = ""
|
|
151
131
|
) -> dict[str, t.Any]:
|
|
152
|
-
"""
|
|
153
|
-
Suggest how to migrate a raw re.sub() call to use safe patterns.
|
|
154
|
-
|
|
155
|
-
Args:
|
|
156
|
-
original_pattern: The original regex pattern
|
|
157
|
-
original_replacement: The original replacement string
|
|
158
|
-
sample_text: Optional sample text to test against
|
|
159
|
-
|
|
160
|
-
Returns:
|
|
161
|
-
Dictionary with migration suggestions
|
|
162
|
-
"""
|
|
163
132
|
suggestion: dict[str, t.Any] = {
|
|
164
133
|
"original_pattern": original_pattern,
|
|
165
134
|
"original_replacement": original_replacement,
|
|
@@ -170,10 +139,9 @@ def suggest_migration_for_re_sub(
|
|
|
170
139
|
"test_cases_needed": [],
|
|
171
140
|
}
|
|
172
141
|
|
|
173
|
-
# Check for safety issues first using safe patterns
|
|
174
142
|
forbidden_checks = [
|
|
175
|
-
(r"\\g\s*<\s*\d+\s*>", "\\g
|
|
176
|
-
(r"\\g<\s+\d+>", "\\g<
|
|
143
|
+
(r"\\g\s*<\s*\d+\s*>", "\\g<1> with spaces"),
|
|
144
|
+
(r"\\g<\s+\d+>", "\\g<1> with space after <"),
|
|
177
145
|
(r"\\\\g<\\d+\\s+>", "\\\\g<1 > with space before >"),
|
|
178
146
|
]
|
|
179
147
|
for forbidden_pattern, _ in forbidden_checks:
|
|
@@ -183,14 +151,12 @@ def suggest_migration_for_re_sub(
|
|
|
183
151
|
"CRITICAL: Bad replacement syntax - spaces in \\g<1>"
|
|
184
152
|
)
|
|
185
153
|
|
|
186
|
-
# Look for existing patterns that might work
|
|
187
154
|
if sample_text:
|
|
188
155
|
matches = find_safe_pattern_for_text(sample_text)
|
|
189
156
|
suggestion["existing_matches"] = matches
|
|
190
157
|
if matches:
|
|
191
158
|
suggestion["needs_new_pattern"] = False
|
|
192
159
|
|
|
193
|
-
# Generate suggested pattern name based on original pattern
|
|
194
160
|
if "python.*-.*m" in original_pattern:
|
|
195
161
|
suggestion["suggested_name"] = "fix_python_command_spacing"
|
|
196
162
|
elif r"\-\s*\-" in original_pattern:
|
|
@@ -200,7 +166,6 @@ def suggest_migration_for_re_sub(
|
|
|
200
166
|
elif "password" in original_pattern.lower():
|
|
201
167
|
suggestion["suggested_name"] = "fix_password_pattern"
|
|
202
168
|
else:
|
|
203
|
-
# Generate name from pattern keywords using safe pattern
|
|
204
169
|
keyword_pattern = CompiledPatternCache.get_compiled_pattern(r"[a-zA-Z]+")
|
|
205
170
|
keywords = keyword_pattern.findall(original_pattern)
|
|
206
171
|
if keywords:
|
|
@@ -210,17 +175,15 @@ def suggest_migration_for_re_sub(
|
|
|
210
175
|
else:
|
|
211
176
|
suggestion["suggested_name"] = "fix_custom_pattern"
|
|
212
177
|
|
|
213
|
-
# Suggest test cases based on common patterns
|
|
214
178
|
if sample_text:
|
|
215
179
|
suggestion["test_cases_needed"].append((sample_text, "Expected output needed"))
|
|
216
180
|
|
|
217
|
-
# Common test cases for spacing issues
|
|
218
181
|
if "-" in original_pattern:
|
|
219
182
|
suggestion["test_cases_needed"].extend(
|
|
220
183
|
[
|
|
221
184
|
("word - word", "word-word"),
|
|
222
|
-
("already-good", "already-good"),
|
|
223
|
-
("multiple - word - spacing", "multiple-word - spacing"),
|
|
185
|
+
("already-good", "already-good"),
|
|
186
|
+
("multiple - word - spacing", "multiple-word - spacing"),
|
|
224
187
|
]
|
|
225
188
|
)
|
|
226
189
|
|
|
@@ -228,7 +191,6 @@ def suggest_migration_for_re_sub(
|
|
|
228
191
|
|
|
229
192
|
|
|
230
193
|
def print_migration_suggestion(suggestion: dict[str, t.Any]) -> None:
|
|
231
|
-
"""Print a formatted migration suggestion report."""
|
|
232
194
|
print("\n🔄 REGEX MIGRATION SUGGESTION")
|
|
233
195
|
print("=" * 50)
|
|
234
196
|
print(f"Original Pattern: {suggestion['original_pattern']}")
|
|
@@ -236,59 +198,54 @@ def print_migration_suggestion(suggestion: dict[str, t.Any]) -> None:
|
|
|
236
198
|
print()
|
|
237
199
|
|
|
238
200
|
if suggestion["safety_issues"]:
|
|
239
|
-
print("❌ SAFETY ISSUES:")
|
|
201
|
+
print("❌ SAFETY ISSUES: ")
|
|
240
202
|
for issue in suggestion["safety_issues"]:
|
|
241
|
-
print(f"
|
|
203
|
+
print(f" • {issue}")
|
|
242
204
|
print()
|
|
243
205
|
|
|
244
206
|
if suggestion["existing_matches"]:
|
|
245
|
-
print("✅ EXISTING PATTERNS AVAILABLE:")
|
|
207
|
+
print("✅ EXISTING PATTERNS AVAILABLE: ")
|
|
246
208
|
for pattern_name in suggestion["existing_matches"]:
|
|
247
209
|
pattern = SAFE_PATTERNS[pattern_name]
|
|
248
|
-
print(f"
|
|
210
|
+
print(f" • {pattern_name}: {pattern.description}")
|
|
249
211
|
print("💡 Consider using existing patterns instead of creating new ones.")
|
|
250
212
|
print()
|
|
251
213
|
|
|
252
214
|
if suggestion["needs_new_pattern"]:
|
|
253
|
-
print("🆕 NEW PATTERN NEEDED:")
|
|
254
|
-
print(f"
|
|
255
|
-
print("
|
|
215
|
+
print("🆕 NEW PATTERN NEEDED: ")
|
|
216
|
+
print(f" Suggested Name: {suggestion['suggested_name']}")
|
|
217
|
+
print(" Add to crackerjack/services/regex_patterns.py: ")
|
|
256
218
|
print()
|
|
257
|
-
print("
|
|
258
|
-
print(f'
|
|
259
|
-
print(f'
|
|
260
|
-
print(f'
|
|
261
|
-
print(f'
|
|
262
|
-
print('
|
|
263
|
-
print("
|
|
219
|
+
print(" ```python")
|
|
220
|
+
print(f' "{suggestion["suggested_name"]}": ValidatedPattern(')
|
|
221
|
+
print(f' name="{suggestion["suggested_name"]}", ')
|
|
222
|
+
print(f' pattern=r"{suggestion["original_pattern"]}", ')
|
|
223
|
+
print(f' replacement=r"{suggestion["original_replacement"]}", ')
|
|
224
|
+
print(' description="TODO: Add description", ')
|
|
225
|
+
print(" test_cases=[")
|
|
264
226
|
for test_input, test_output in suggestion["test_cases_needed"]:
|
|
265
|
-
print(f'
|
|
266
|
-
print("
|
|
267
|
-
print("
|
|
268
|
-
print("
|
|
227
|
+
print(f' ("{test_input}", "{test_output}"), ')
|
|
228
|
+
print(" ]")
|
|
229
|
+
print(" ), ")
|
|
230
|
+
print(" ```")
|
|
269
231
|
print()
|
|
270
232
|
|
|
271
|
-
print("🔧 MIGRATION STEPS:")
|
|
272
|
-
print("
|
|
233
|
+
print("🔧 MIGRATION STEPS: ")
|
|
234
|
+
print(" 1. Fix any safety issues in replacement syntax")
|
|
273
235
|
if suggestion["existing_matches"]:
|
|
274
|
-
print("
|
|
236
|
+
print(" 2. Use existing safe patterns if possible: ")
|
|
275
237
|
for pattern_name in suggestion["existing_matches"]:
|
|
276
|
-
print(f"
|
|
238
|
+
print(f" SAFE_PATTERNS['{pattern_name}'].apply(text)")
|
|
277
239
|
if suggestion["needs_new_pattern"]:
|
|
278
|
-
print("
|
|
279
|
-
print("
|
|
280
|
-
print("
|
|
281
|
-
print("
|
|
240
|
+
print(" 3. Add new ValidatedPattern to regex_patterns.py")
|
|
241
|
+
print(" 4. Test thoroughly with comprehensive test cases")
|
|
242
|
+
print(" 5. Replace re.sub() call with safe pattern usage")
|
|
243
|
+
print(" 6. Run pre-commit hook to validate")
|
|
282
244
|
|
|
283
245
|
print("=" * 50)
|
|
284
246
|
|
|
285
247
|
|
|
286
248
|
def audit_file_for_re_sub(file_path: Path) -> list[dict[str, t.Any]]:
|
|
287
|
-
"""
|
|
288
|
-
Audit a file for re.sub() calls and return migration suggestions.
|
|
289
|
-
|
|
290
|
-
Returns list of findings with line numbers and suggestions.
|
|
291
|
-
"""
|
|
292
249
|
findings = []
|
|
293
250
|
|
|
294
251
|
try:
|
|
@@ -296,9 +253,8 @@ def audit_file_for_re_sub(file_path: Path) -> list[dict[str, t.Any]]:
|
|
|
296
253
|
lines = content.split("\n")
|
|
297
254
|
|
|
298
255
|
for i, line in enumerate(lines, 1):
|
|
299
|
-
# Look for re.sub() calls using safe pattern
|
|
300
256
|
re_sub_pattern = CompiledPatternCache.get_compiled_pattern(
|
|
301
|
-
r're\.sub\s*\(\s*[r]?["\']([^"\']+)["\']
|
|
257
|
+
r're\.sub\s*\(\s*[r]?["\']([^"\']+)["\'], \s*[r]?["\']([^"\']*)["\']'
|
|
302
258
|
)
|
|
303
259
|
re_sub_match = re_sub_pattern.search(line)
|
|
304
260
|
if re_sub_match:
|
|
@@ -328,18 +284,11 @@ def audit_file_for_re_sub(file_path: Path) -> list[dict[str, t.Any]]:
|
|
|
328
284
|
|
|
329
285
|
|
|
330
286
|
def audit_codebase_re_sub() -> dict[str, list[dict[str, t.Any]]]:
|
|
331
|
-
"""
|
|
332
|
-
Audit entire crackerjack codebase for re.sub() usage.
|
|
333
|
-
|
|
334
|
-
Returns dictionary mapping file paths to findings.
|
|
335
|
-
"""
|
|
336
287
|
findings_by_file = {}
|
|
337
288
|
|
|
338
|
-
# Audit crackerjack package
|
|
339
289
|
crackerjack_dir = Path(__file__).parent.parent
|
|
340
290
|
|
|
341
291
|
for py_file in crackerjack_dir.rglob("*.py"):
|
|
342
|
-
# Skip test files and __pycache__
|
|
343
292
|
if "test_" in py_file.name or "__pycache__" in str(py_file):
|
|
344
293
|
continue
|
|
345
294
|
|
|
@@ -351,18 +300,6 @@ def audit_codebase_re_sub() -> dict[str, list[dict[str, t.Any]]]:
|
|
|
351
300
|
|
|
352
301
|
|
|
353
302
|
def replace_unsafe_regex_with_safe_patterns(content: str) -> str:
|
|
354
|
-
"""
|
|
355
|
-
Replace unsafe regex patterns in content with safe alternatives.
|
|
356
|
-
|
|
357
|
-
This function looks for common regex patterns and replaces them with
|
|
358
|
-
calls to SAFE_PATTERNS where possible.
|
|
359
|
-
|
|
360
|
-
Args:
|
|
361
|
-
content: The source code content to fix
|
|
362
|
-
|
|
363
|
-
Returns:
|
|
364
|
-
Fixed content with safe patterns applied
|
|
365
|
-
"""
|
|
366
303
|
lines = content.split("\n")
|
|
367
304
|
modified = False
|
|
368
305
|
|
|
@@ -371,10 +308,8 @@ def replace_unsafe_regex_with_safe_patterns(content: str) -> str:
|
|
|
371
308
|
for i, line in enumerate(lines):
|
|
372
309
|
original_line = line
|
|
373
310
|
|
|
374
|
-
# Fix critical replacement syntax issues first
|
|
375
311
|
line = _fix_replacement_syntax_issues(line)
|
|
376
312
|
|
|
377
|
-
# Process re.sub patterns
|
|
378
313
|
line, _, needs_import = _process_re_sub_patterns(line, has_safe_patterns_import)
|
|
379
314
|
|
|
380
315
|
if needs_import and not has_safe_patterns_import:
|
|
@@ -384,9 +319,8 @@ def replace_unsafe_regex_with_safe_patterns(content: str) -> str:
|
|
|
384
319
|
"from crackerjack.services.regex_patterns import SAFE_PATTERNS",
|
|
385
320
|
)
|
|
386
321
|
has_safe_patterns_import = True
|
|
387
|
-
i += 1
|
|
322
|
+
i += 1
|
|
388
323
|
|
|
389
|
-
# Update the line if it changed
|
|
390
324
|
if line != original_line:
|
|
391
325
|
lines[i] = line
|
|
392
326
|
modified = True
|
|
@@ -395,7 +329,6 @@ def replace_unsafe_regex_with_safe_patterns(content: str) -> str:
|
|
|
395
329
|
|
|
396
330
|
|
|
397
331
|
def _check_for_safe_patterns_import(lines: list[str]) -> bool:
|
|
398
|
-
"""Check if SAFE_PATTERNS import is already present."""
|
|
399
332
|
return any(
|
|
400
333
|
"from crackerjack.services.regex_patterns import SAFE_PATTERNS" in line
|
|
401
334
|
or "SAFE_PATTERNS" in line
|
|
@@ -404,9 +337,7 @@ def _check_for_safe_patterns_import(lines: list[str]) -> bool:
|
|
|
404
337
|
|
|
405
338
|
|
|
406
339
|
def _fix_replacement_syntax_issues(line: str) -> str:
|
|
407
|
-
"""Fix critical replacement syntax issues in regex patterns."""
|
|
408
340
|
if r"\g < " in line or r"\g< " in line or r"\g <" in line:
|
|
409
|
-
# Fix spacing in replacement groups using safe pattern
|
|
410
341
|
spacing_fix_pattern = CompiledPatternCache.get_compiled_pattern(
|
|
411
342
|
r"\\g\s*<\s*(\d+)\s*>"
|
|
412
343
|
)
|
|
@@ -417,9 +348,8 @@ def _fix_replacement_syntax_issues(line: str) -> str:
|
|
|
417
348
|
def _process_re_sub_patterns(
|
|
418
349
|
line: str, has_safe_patterns_import: bool
|
|
419
350
|
) -> tuple[str, bool, bool]:
|
|
420
|
-
"""Process re.sub patterns and replace with safe alternatives."""
|
|
421
351
|
re_sub_match = CompiledPatternCache.get_compiled_pattern(
|
|
422
|
-
r're\.sub\s*\(\s*r?["\']([^"\']+)["\']\s
|
|
352
|
+
r're\.sub\s*\(\s*r?["\']([^"\']+)["\']\s*, \s*r?["\']([^"\']*)["\']'
|
|
423
353
|
).search(line)
|
|
424
354
|
|
|
425
355
|
if not re_sub_match:
|
|
@@ -436,8 +366,6 @@ def _process_re_sub_patterns(
|
|
|
436
366
|
|
|
437
367
|
|
|
438
368
|
def _identify_safe_pattern(pattern: str, replacement: str) -> str | None:
|
|
439
|
-
"""Identify which safe pattern matches the given regex pattern."""
|
|
440
|
-
# Common patterns we can automatically replace
|
|
441
369
|
if pattern == r"(\w+)\s*-\s*(\w+)" and replacement in (
|
|
442
370
|
r"\1-\2",
|
|
443
371
|
r"\g<1>-\g<2>",
|
|
@@ -453,11 +381,9 @@ def _identify_safe_pattern(pattern: str, replacement: str) -> str | None:
|
|
|
453
381
|
def _replace_with_safe_pattern(
|
|
454
382
|
line: str, re_sub_match: re.Match[str], safe_pattern_name: str
|
|
455
383
|
) -> tuple[str, bool, bool]:
|
|
456
|
-
"""Replace re.sub call with safe pattern call."""
|
|
457
384
|
before_re_sub = line[: re_sub_match.start()]
|
|
458
385
|
after_re_sub = line[re_sub_match.end() :]
|
|
459
386
|
|
|
460
|
-
# Look for assignment pattern: var = re.sub(...)
|
|
461
387
|
assign_match = CompiledPatternCache.get_compiled_pattern(r"(\w+)\s*=\s*$").search(
|
|
462
388
|
before_re_sub
|
|
463
389
|
)
|
|
@@ -476,7 +402,6 @@ def _handle_assignment_pattern(
|
|
|
476
402
|
after_re_sub: str,
|
|
477
403
|
safe_pattern_name: str,
|
|
478
404
|
) -> tuple[str, bool, bool]:
|
|
479
|
-
"""Handle assignment pattern replacement."""
|
|
480
405
|
var_name = assign_match.group(1)
|
|
481
406
|
text_var = _extract_source_variable(line)
|
|
482
407
|
new_line = f"{var_name} = SAFE_PATTERNS['{safe_pattern_name}'].apply({text_var})"
|
|
@@ -486,7 +411,6 @@ def _handle_assignment_pattern(
|
|
|
486
411
|
def _handle_direct_replacement(
|
|
487
412
|
line: str, re_sub_match: re.Match[str], safe_pattern_name: str
|
|
488
413
|
) -> tuple[str, bool, bool]:
|
|
489
|
-
"""Handle direct replacement of re.sub call."""
|
|
490
414
|
text_var = _extract_source_variable(line)
|
|
491
415
|
new_line = line.replace(
|
|
492
416
|
re_sub_match.group(0),
|
|
@@ -496,15 +420,13 @@ def _handle_direct_replacement(
|
|
|
496
420
|
|
|
497
421
|
|
|
498
422
|
def _extract_source_variable(line: str) -> str:
|
|
499
|
-
"""Extract the source variable from re.sub call."""
|
|
500
423
|
full_match = CompiledPatternCache.get_compiled_pattern(
|
|
501
|
-
r"re\.sub\s*\([^,]
|
|
424
|
+
r"re\.sub\s*\([^, ]+, \s*[^, ]+, \s*(\w+)"
|
|
502
425
|
).search(line)
|
|
503
426
|
return full_match.group(1) if full_match else "text"
|
|
504
427
|
|
|
505
428
|
|
|
506
429
|
def _find_import_insertion_point(lines: list[str]) -> int:
|
|
507
|
-
"""Find the right place to insert the import statement."""
|
|
508
430
|
import_index = 0
|
|
509
431
|
for j, check_line in enumerate(lines):
|
|
510
432
|
if check_line.strip().startswith(("import ", "from ")):
|
|
@@ -517,7 +439,6 @@ def _find_import_insertion_point(lines: list[str]) -> int:
|
|
|
517
439
|
|
|
518
440
|
|
|
519
441
|
if __name__ == "__main__":
|
|
520
|
-
# Example usage for testing patterns
|
|
521
442
|
test_result = quick_pattern_test(
|
|
522
443
|
pattern=r"(\w+)\s*-\s*(\w+)",
|
|
523
444
|
replacement=r"\1-\2",
|
|
@@ -529,7 +450,6 @@ if __name__ == "__main__":
|
|
|
529
450
|
description="Fix spacing in hyphenated names",
|
|
530
451
|
)
|
|
531
452
|
|
|
532
|
-
# Example migration suggestion
|
|
533
453
|
print("\n" + "=" * 60)
|
|
534
454
|
migration = suggest_migration_for_re_sub(
|
|
535
455
|
r"python\s*-\s*m\s+", "python -m ", "python - m crackerjack"
|