crackerjack 0.18.2__py3-none-any.whl → 0.45.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.
- crackerjack/README.md +19 -0
- crackerjack/__init__.py +96 -2
- crackerjack/__main__.py +637 -138
- crackerjack/adapters/README.md +18 -0
- crackerjack/adapters/__init__.py +39 -0
- crackerjack/adapters/_output_paths.py +167 -0
- crackerjack/adapters/_qa_adapter_base.py +309 -0
- crackerjack/adapters/_tool_adapter_base.py +706 -0
- crackerjack/adapters/ai/README.md +65 -0
- crackerjack/adapters/ai/__init__.py +5 -0
- crackerjack/adapters/ai/claude.py +853 -0
- crackerjack/adapters/complexity/README.md +53 -0
- crackerjack/adapters/complexity/__init__.py +10 -0
- crackerjack/adapters/complexity/complexipy.py +641 -0
- crackerjack/adapters/dependency/__init__.py +22 -0
- crackerjack/adapters/dependency/pip_audit.py +418 -0
- crackerjack/adapters/format/README.md +72 -0
- crackerjack/adapters/format/__init__.py +11 -0
- crackerjack/adapters/format/mdformat.py +313 -0
- crackerjack/adapters/format/ruff.py +516 -0
- crackerjack/adapters/lint/README.md +47 -0
- crackerjack/adapters/lint/__init__.py +11 -0
- crackerjack/adapters/lint/codespell.py +273 -0
- crackerjack/adapters/lsp/README.md +49 -0
- crackerjack/adapters/lsp/__init__.py +27 -0
- crackerjack/adapters/lsp/_base.py +194 -0
- crackerjack/adapters/lsp/_client.py +358 -0
- crackerjack/adapters/lsp/_manager.py +193 -0
- crackerjack/adapters/lsp/skylos.py +283 -0
- crackerjack/adapters/lsp/zuban.py +557 -0
- crackerjack/adapters/refactor/README.md +59 -0
- crackerjack/adapters/refactor/__init__.py +12 -0
- crackerjack/adapters/refactor/creosote.py +318 -0
- crackerjack/adapters/refactor/refurb.py +406 -0
- crackerjack/adapters/refactor/skylos.py +494 -0
- crackerjack/adapters/sast/README.md +132 -0
- crackerjack/adapters/sast/__init__.py +32 -0
- crackerjack/adapters/sast/_base.py +201 -0
- crackerjack/adapters/sast/bandit.py +423 -0
- crackerjack/adapters/sast/pyscn.py +405 -0
- crackerjack/adapters/sast/semgrep.py +241 -0
- crackerjack/adapters/security/README.md +111 -0
- crackerjack/adapters/security/__init__.py +17 -0
- crackerjack/adapters/security/gitleaks.py +339 -0
- crackerjack/adapters/type/README.md +52 -0
- crackerjack/adapters/type/__init__.py +12 -0
- crackerjack/adapters/type/pyrefly.py +402 -0
- crackerjack/adapters/type/ty.py +402 -0
- crackerjack/adapters/type/zuban.py +522 -0
- crackerjack/adapters/utility/README.md +51 -0
- crackerjack/adapters/utility/__init__.py +10 -0
- crackerjack/adapters/utility/checks.py +884 -0
- crackerjack/agents/README.md +264 -0
- crackerjack/agents/__init__.py +66 -0
- crackerjack/agents/architect_agent.py +238 -0
- crackerjack/agents/base.py +167 -0
- crackerjack/agents/claude_code_bridge.py +641 -0
- crackerjack/agents/coordinator.py +600 -0
- crackerjack/agents/documentation_agent.py +520 -0
- crackerjack/agents/dry_agent.py +585 -0
- crackerjack/agents/enhanced_coordinator.py +279 -0
- crackerjack/agents/enhanced_proactive_agent.py +185 -0
- crackerjack/agents/error_middleware.py +53 -0
- crackerjack/agents/formatting_agent.py +230 -0
- crackerjack/agents/helpers/__init__.py +9 -0
- crackerjack/agents/helpers/performance/__init__.py +22 -0
- crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
- crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
- crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
- crackerjack/agents/helpers/refactoring/__init__.py +22 -0
- crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
- crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
- crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
- crackerjack/agents/helpers/test_creation/__init__.py +19 -0
- crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
- crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
- crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
- crackerjack/agents/import_optimization_agent.py +1181 -0
- crackerjack/agents/performance_agent.py +325 -0
- crackerjack/agents/performance_helpers.py +205 -0
- crackerjack/agents/proactive_agent.py +55 -0
- crackerjack/agents/refactoring_agent.py +511 -0
- crackerjack/agents/refactoring_helpers.py +247 -0
- crackerjack/agents/security_agent.py +793 -0
- crackerjack/agents/semantic_agent.py +479 -0
- crackerjack/agents/semantic_helpers.py +356 -0
- crackerjack/agents/test_creation_agent.py +570 -0
- crackerjack/agents/test_specialist_agent.py +526 -0
- crackerjack/agents/tracker.py +110 -0
- crackerjack/api.py +647 -0
- crackerjack/cli/README.md +394 -0
- crackerjack/cli/__init__.py +24 -0
- crackerjack/cli/cache_handlers.py +209 -0
- crackerjack/cli/cache_handlers_enhanced.py +680 -0
- crackerjack/cli/facade.py +162 -0
- crackerjack/cli/formatting.py +13 -0
- crackerjack/cli/handlers/__init__.py +85 -0
- crackerjack/cli/handlers/advanced.py +103 -0
- crackerjack/cli/handlers/ai_features.py +62 -0
- crackerjack/cli/handlers/analytics.py +479 -0
- crackerjack/cli/handlers/changelog.py +271 -0
- crackerjack/cli/handlers/config_handlers.py +16 -0
- crackerjack/cli/handlers/coverage.py +84 -0
- crackerjack/cli/handlers/documentation.py +280 -0
- crackerjack/cli/handlers/main_handlers.py +497 -0
- crackerjack/cli/handlers/monitoring.py +371 -0
- crackerjack/cli/handlers.py +700 -0
- crackerjack/cli/interactive.py +488 -0
- crackerjack/cli/options.py +1216 -0
- crackerjack/cli/semantic_handlers.py +292 -0
- crackerjack/cli/utils.py +19 -0
- crackerjack/cli/version.py +19 -0
- crackerjack/code_cleaner.py +1307 -0
- crackerjack/config/README.md +472 -0
- crackerjack/config/__init__.py +275 -0
- crackerjack/config/global_lock_config.py +207 -0
- crackerjack/config/hooks.py +390 -0
- crackerjack/config/loader.py +239 -0
- crackerjack/config/settings.py +141 -0
- crackerjack/config/tool_commands.py +331 -0
- crackerjack/core/README.md +393 -0
- crackerjack/core/__init__.py +0 -0
- crackerjack/core/async_workflow_orchestrator.py +738 -0
- crackerjack/core/autofix_coordinator.py +282 -0
- crackerjack/core/container.py +105 -0
- crackerjack/core/enhanced_container.py +583 -0
- crackerjack/core/file_lifecycle.py +472 -0
- crackerjack/core/performance.py +244 -0
- crackerjack/core/performance_monitor.py +357 -0
- crackerjack/core/phase_coordinator.py +1227 -0
- crackerjack/core/proactive_workflow.py +267 -0
- crackerjack/core/resource_manager.py +425 -0
- crackerjack/core/retry.py +275 -0
- crackerjack/core/service_watchdog.py +601 -0
- crackerjack/core/session_coordinator.py +239 -0
- crackerjack/core/timeout_manager.py +563 -0
- crackerjack/core/websocket_lifecycle.py +410 -0
- crackerjack/core/workflow/__init__.py +21 -0
- crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
- crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
- crackerjack/core/workflow/workflow_issue_parser.py +714 -0
- crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
- crackerjack/core/workflow/workflow_security_gates.py +400 -0
- crackerjack/core/workflow_orchestrator.py +2243 -0
- crackerjack/data/README.md +11 -0
- crackerjack/data/__init__.py +8 -0
- crackerjack/data/models.py +79 -0
- crackerjack/data/repository.py +210 -0
- crackerjack/decorators/README.md +180 -0
- crackerjack/decorators/__init__.py +35 -0
- crackerjack/decorators/error_handling.py +649 -0
- crackerjack/decorators/error_handling_decorators.py +334 -0
- crackerjack/decorators/helpers.py +58 -0
- crackerjack/decorators/patterns.py +281 -0
- crackerjack/decorators/utils.py +58 -0
- crackerjack/docs/INDEX.md +11 -0
- crackerjack/docs/README.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/README.md +11 -0
- crackerjack/documentation/__init__.py +31 -0
- crackerjack/documentation/ai_templates.py +756 -0
- crackerjack/documentation/dual_output_generator.py +767 -0
- crackerjack/documentation/mkdocs_integration.py +518 -0
- crackerjack/documentation/reference_generator.py +1065 -0
- crackerjack/dynamic_config.py +678 -0
- crackerjack/errors.py +378 -0
- crackerjack/events/README.md +11 -0
- crackerjack/events/__init__.py +16 -0
- crackerjack/events/telemetry.py +175 -0
- crackerjack/events/workflow_bus.py +346 -0
- crackerjack/exceptions/README.md +301 -0
- crackerjack/exceptions/__init__.py +5 -0
- crackerjack/exceptions/config.py +4 -0
- crackerjack/exceptions/tool_execution_error.py +245 -0
- crackerjack/executors/README.md +591 -0
- crackerjack/executors/__init__.py +13 -0
- crackerjack/executors/async_hook_executor.py +938 -0
- crackerjack/executors/cached_hook_executor.py +316 -0
- crackerjack/executors/hook_executor.py +1295 -0
- crackerjack/executors/hook_lock_manager.py +708 -0
- crackerjack/executors/individual_hook_executor.py +739 -0
- crackerjack/executors/lsp_aware_hook_executor.py +349 -0
- crackerjack/executors/progress_hook_executor.py +282 -0
- crackerjack/executors/tool_proxy.py +433 -0
- crackerjack/hooks/README.md +485 -0
- crackerjack/hooks/lsp_hook.py +93 -0
- crackerjack/intelligence/README.md +557 -0
- crackerjack/intelligence/__init__.py +37 -0
- crackerjack/intelligence/adaptive_learning.py +693 -0
- crackerjack/intelligence/agent_orchestrator.py +485 -0
- crackerjack/intelligence/agent_registry.py +377 -0
- crackerjack/intelligence/agent_selector.py +439 -0
- crackerjack/intelligence/integration.py +250 -0
- crackerjack/interactive.py +719 -0
- crackerjack/managers/README.md +369 -0
- crackerjack/managers/__init__.py +11 -0
- crackerjack/managers/async_hook_manager.py +135 -0
- crackerjack/managers/hook_manager.py +585 -0
- crackerjack/managers/publish_manager.py +631 -0
- crackerjack/managers/test_command_builder.py +391 -0
- crackerjack/managers/test_executor.py +474 -0
- crackerjack/managers/test_manager.py +1357 -0
- crackerjack/managers/test_progress.py +187 -0
- crackerjack/mcp/README.md +374 -0
- crackerjack/mcp/__init__.py +0 -0
- crackerjack/mcp/cache.py +352 -0
- crackerjack/mcp/client_runner.py +121 -0
- crackerjack/mcp/context.py +802 -0
- crackerjack/mcp/dashboard.py +657 -0
- crackerjack/mcp/enhanced_progress_monitor.py +493 -0
- crackerjack/mcp/file_monitor.py +394 -0
- crackerjack/mcp/progress_components.py +607 -0
- crackerjack/mcp/progress_monitor.py +1016 -0
- crackerjack/mcp/rate_limiter.py +336 -0
- crackerjack/mcp/server.py +24 -0
- crackerjack/mcp/server_core.py +526 -0
- crackerjack/mcp/service_watchdog.py +505 -0
- crackerjack/mcp/state.py +407 -0
- crackerjack/mcp/task_manager.py +259 -0
- crackerjack/mcp/tools/README.md +27 -0
- crackerjack/mcp/tools/__init__.py +19 -0
- crackerjack/mcp/tools/core_tools.py +469 -0
- crackerjack/mcp/tools/error_analyzer.py +283 -0
- crackerjack/mcp/tools/execution_tools.py +384 -0
- crackerjack/mcp/tools/intelligence_tool_registry.py +46 -0
- crackerjack/mcp/tools/intelligence_tools.py +264 -0
- crackerjack/mcp/tools/monitoring_tools.py +628 -0
- crackerjack/mcp/tools/proactive_tools.py +367 -0
- crackerjack/mcp/tools/progress_tools.py +222 -0
- crackerjack/mcp/tools/semantic_tools.py +584 -0
- crackerjack/mcp/tools/utility_tools.py +358 -0
- crackerjack/mcp/tools/workflow_executor.py +699 -0
- crackerjack/mcp/websocket/README.md +31 -0
- crackerjack/mcp/websocket/__init__.py +14 -0
- crackerjack/mcp/websocket/app.py +54 -0
- crackerjack/mcp/websocket/endpoints.py +492 -0
- crackerjack/mcp/websocket/event_bridge.py +188 -0
- crackerjack/mcp/websocket/jobs.py +406 -0
- crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
- crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
- crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
- crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
- crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
- crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
- crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
- crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
- crackerjack/mcp/websocket/monitoring/factory.py +109 -0
- crackerjack/mcp/websocket/monitoring/filters.py +10 -0
- crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
- crackerjack/mcp/websocket/monitoring/models.py +90 -0
- crackerjack/mcp/websocket/monitoring/utils.py +171 -0
- crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
- crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
- crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
- crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
- crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
- crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
- crackerjack/mcp/websocket/monitoring_endpoints.py +21 -0
- crackerjack/mcp/websocket/server.py +174 -0
- crackerjack/mcp/websocket/websocket_handler.py +276 -0
- crackerjack/mcp/websocket_server.py +10 -0
- crackerjack/models/README.md +308 -0
- crackerjack/models/__init__.py +40 -0
- crackerjack/models/config.py +730 -0
- crackerjack/models/config_adapter.py +265 -0
- crackerjack/models/protocols.py +1535 -0
- crackerjack/models/pydantic_models.py +320 -0
- crackerjack/models/qa_config.py +145 -0
- crackerjack/models/qa_results.py +134 -0
- crackerjack/models/resource_protocols.py +299 -0
- crackerjack/models/results.py +35 -0
- crackerjack/models/semantic_models.py +258 -0
- crackerjack/models/task.py +173 -0
- crackerjack/models/test_models.py +60 -0
- crackerjack/monitoring/README.md +11 -0
- crackerjack/monitoring/__init__.py +0 -0
- crackerjack/monitoring/ai_agent_watchdog.py +405 -0
- crackerjack/monitoring/metrics_collector.py +427 -0
- crackerjack/monitoring/regression_prevention.py +580 -0
- crackerjack/monitoring/websocket_server.py +406 -0
- crackerjack/orchestration/README.md +340 -0
- crackerjack/orchestration/__init__.py +43 -0
- crackerjack/orchestration/advanced_orchestrator.py +894 -0
- crackerjack/orchestration/cache/README.md +312 -0
- crackerjack/orchestration/cache/__init__.py +37 -0
- crackerjack/orchestration/cache/memory_cache.py +338 -0
- crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
- crackerjack/orchestration/config.py +297 -0
- crackerjack/orchestration/coverage_improvement.py +180 -0
- crackerjack/orchestration/execution_strategies.py +361 -0
- crackerjack/orchestration/hook_orchestrator.py +1398 -0
- crackerjack/orchestration/strategies/README.md +401 -0
- crackerjack/orchestration/strategies/__init__.py +39 -0
- crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
- crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
- crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
- crackerjack/orchestration/test_progress_streamer.py +647 -0
- crackerjack/plugins/README.md +11 -0
- crackerjack/plugins/__init__.py +15 -0
- crackerjack/plugins/base.py +200 -0
- crackerjack/plugins/hooks.py +254 -0
- crackerjack/plugins/loader.py +335 -0
- crackerjack/plugins/managers.py +264 -0
- crackerjack/py313.py +191 -0
- crackerjack/security/README.md +11 -0
- crackerjack/security/__init__.py +0 -0
- crackerjack/security/audit.py +197 -0
- crackerjack/services/README.md +374 -0
- crackerjack/services/__init__.py +9 -0
- crackerjack/services/ai/README.md +295 -0
- crackerjack/services/ai/__init__.py +7 -0
- crackerjack/services/ai/advanced_optimizer.py +878 -0
- crackerjack/services/ai/contextual_ai_assistant.py +542 -0
- crackerjack/services/ai/embeddings.py +444 -0
- crackerjack/services/ai/intelligent_commit.py +328 -0
- crackerjack/services/ai/predictive_analytics.py +510 -0
- crackerjack/services/anomaly_detector.py +392 -0
- crackerjack/services/api_extractor.py +617 -0
- crackerjack/services/backup_service.py +467 -0
- crackerjack/services/bounded_status_operations.py +530 -0
- crackerjack/services/cache.py +369 -0
- crackerjack/services/changelog_automation.py +399 -0
- crackerjack/services/command_execution_service.py +305 -0
- crackerjack/services/config_integrity.py +132 -0
- crackerjack/services/config_merge.py +546 -0
- crackerjack/services/config_service.py +198 -0
- crackerjack/services/config_template.py +493 -0
- crackerjack/services/coverage_badge_service.py +173 -0
- crackerjack/services/coverage_ratchet.py +381 -0
- crackerjack/services/debug.py +733 -0
- crackerjack/services/dependency_analyzer.py +460 -0
- crackerjack/services/dependency_monitor.py +622 -0
- crackerjack/services/documentation_generator.py +493 -0
- crackerjack/services/documentation_service.py +704 -0
- crackerjack/services/enhanced_filesystem.py +497 -0
- crackerjack/services/enterprise_optimizer.py +865 -0
- crackerjack/services/error_pattern_analyzer.py +676 -0
- crackerjack/services/file_filter.py +221 -0
- crackerjack/services/file_hasher.py +149 -0
- crackerjack/services/file_io_service.py +361 -0
- crackerjack/services/file_modifier.py +615 -0
- crackerjack/services/filesystem.py +381 -0
- crackerjack/services/git.py +422 -0
- crackerjack/services/health_metrics.py +615 -0
- crackerjack/services/heatmap_generator.py +744 -0
- crackerjack/services/incremental_executor.py +380 -0
- crackerjack/services/initialization.py +823 -0
- crackerjack/services/input_validator.py +668 -0
- crackerjack/services/intelligent_commit.py +327 -0
- crackerjack/services/log_manager.py +289 -0
- crackerjack/services/logging.py +228 -0
- crackerjack/services/lsp_client.py +628 -0
- crackerjack/services/memory_optimizer.py +414 -0
- crackerjack/services/metrics.py +587 -0
- crackerjack/services/monitoring/README.md +30 -0
- crackerjack/services/monitoring/__init__.py +9 -0
- crackerjack/services/monitoring/dependency_monitor.py +678 -0
- crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
- crackerjack/services/monitoring/health_metrics.py +716 -0
- crackerjack/services/monitoring/metrics.py +587 -0
- crackerjack/services/monitoring/performance_benchmarks.py +410 -0
- crackerjack/services/monitoring/performance_cache.py +388 -0
- crackerjack/services/monitoring/performance_monitor.py +569 -0
- crackerjack/services/parallel_executor.py +527 -0
- crackerjack/services/pattern_cache.py +333 -0
- crackerjack/services/pattern_detector.py +478 -0
- crackerjack/services/patterns/__init__.py +142 -0
- crackerjack/services/patterns/agents.py +107 -0
- crackerjack/services/patterns/code/__init__.py +15 -0
- crackerjack/services/patterns/code/detection.py +118 -0
- crackerjack/services/patterns/code/imports.py +107 -0
- crackerjack/services/patterns/code/paths.py +159 -0
- crackerjack/services/patterns/code/performance.py +119 -0
- crackerjack/services/patterns/code/replacement.py +36 -0
- crackerjack/services/patterns/core.py +212 -0
- crackerjack/services/patterns/documentation/__init__.py +14 -0
- crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
- crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
- crackerjack/services/patterns/documentation/docstrings.py +89 -0
- crackerjack/services/patterns/formatting.py +226 -0
- crackerjack/services/patterns/operations.py +339 -0
- crackerjack/services/patterns/security/__init__.py +23 -0
- crackerjack/services/patterns/security/code_injection.py +122 -0
- crackerjack/services/patterns/security/credentials.py +190 -0
- crackerjack/services/patterns/security/path_traversal.py +221 -0
- crackerjack/services/patterns/security/unsafe_operations.py +216 -0
- crackerjack/services/patterns/templates.py +62 -0
- crackerjack/services/patterns/testing/__init__.py +18 -0
- crackerjack/services/patterns/testing/error_patterns.py +107 -0
- crackerjack/services/patterns/testing/pytest_output.py +126 -0
- crackerjack/services/patterns/tool_output/__init__.py +16 -0
- crackerjack/services/patterns/tool_output/bandit.py +72 -0
- crackerjack/services/patterns/tool_output/other.py +97 -0
- crackerjack/services/patterns/tool_output/pyright.py +67 -0
- crackerjack/services/patterns/tool_output/ruff.py +44 -0
- crackerjack/services/patterns/url_sanitization.py +114 -0
- crackerjack/services/patterns/utilities.py +42 -0
- crackerjack/services/patterns/utils.py +339 -0
- crackerjack/services/patterns/validation.py +46 -0
- crackerjack/services/patterns/versioning.py +62 -0
- crackerjack/services/predictive_analytics.py +523 -0
- crackerjack/services/profiler.py +280 -0
- crackerjack/services/quality/README.md +415 -0
- crackerjack/services/quality/__init__.py +11 -0
- crackerjack/services/quality/anomaly_detector.py +392 -0
- crackerjack/services/quality/pattern_cache.py +333 -0
- crackerjack/services/quality/pattern_detector.py +479 -0
- crackerjack/services/quality/qa_orchestrator.py +491 -0
- crackerjack/services/quality/quality_baseline.py +395 -0
- crackerjack/services/quality/quality_baseline_enhanced.py +649 -0
- crackerjack/services/quality/quality_intelligence.py +949 -0
- crackerjack/services/regex_patterns.py +58 -0
- crackerjack/services/regex_utils.py +483 -0
- crackerjack/services/secure_path_utils.py +524 -0
- crackerjack/services/secure_status_formatter.py +450 -0
- crackerjack/services/secure_subprocess.py +635 -0
- crackerjack/services/security.py +239 -0
- crackerjack/services/security_logger.py +495 -0
- crackerjack/services/server_manager.py +411 -0
- crackerjack/services/smart_scheduling.py +167 -0
- crackerjack/services/status_authentication.py +460 -0
- crackerjack/services/status_security_manager.py +315 -0
- crackerjack/services/terminal_utils.py +0 -0
- crackerjack/services/thread_safe_status_collector.py +441 -0
- crackerjack/services/tool_filter.py +368 -0
- crackerjack/services/tool_version_service.py +43 -0
- crackerjack/services/unified_config.py +115 -0
- crackerjack/services/validation_rate_limiter.py +220 -0
- crackerjack/services/vector_store.py +689 -0
- crackerjack/services/version_analyzer.py +461 -0
- crackerjack/services/version_checker.py +223 -0
- crackerjack/services/websocket_resource_limiter.py +438 -0
- crackerjack/services/zuban_lsp_service.py +391 -0
- crackerjack/slash_commands/README.md +11 -0
- crackerjack/slash_commands/__init__.py +59 -0
- crackerjack/slash_commands/init.md +112 -0
- crackerjack/slash_commands/run.md +197 -0
- crackerjack/slash_commands/status.md +127 -0
- crackerjack/tools/README.md +11 -0
- crackerjack/tools/__init__.py +30 -0
- crackerjack/tools/_git_utils.py +105 -0
- crackerjack/tools/check_added_large_files.py +139 -0
- crackerjack/tools/check_ast.py +105 -0
- crackerjack/tools/check_json.py +103 -0
- crackerjack/tools/check_jsonschema.py +297 -0
- crackerjack/tools/check_toml.py +103 -0
- crackerjack/tools/check_yaml.py +110 -0
- crackerjack/tools/codespell_wrapper.py +72 -0
- crackerjack/tools/end_of_file_fixer.py +202 -0
- crackerjack/tools/format_json.py +128 -0
- crackerjack/tools/mdformat_wrapper.py +114 -0
- crackerjack/tools/trailing_whitespace.py +198 -0
- crackerjack/tools/validate_input_validator_patterns.py +236 -0
- crackerjack/tools/validate_regex_patterns.py +188 -0
- crackerjack/ui/README.md +11 -0
- crackerjack/ui/__init__.py +1 -0
- crackerjack/ui/dashboard_renderer.py +28 -0
- crackerjack/ui/templates/README.md +11 -0
- crackerjack/utils/console_utils.py +13 -0
- crackerjack/utils/dependency_guard.py +230 -0
- crackerjack/utils/retry_utils.py +275 -0
- crackerjack/workflows/README.md +590 -0
- crackerjack/workflows/__init__.py +46 -0
- crackerjack/workflows/actions.py +811 -0
- crackerjack/workflows/auto_fix.py +444 -0
- crackerjack/workflows/container_builder.py +499 -0
- crackerjack/workflows/definitions.py +443 -0
- crackerjack/workflows/engine.py +177 -0
- crackerjack/workflows/event_bridge.py +242 -0
- crackerjack-0.45.2.dist-info/METADATA +1678 -0
- crackerjack-0.45.2.dist-info/RECORD +478 -0
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
- crackerjack-0.45.2.dist-info/entry_points.txt +2 -0
- crackerjack/.gitignore +0 -14
- crackerjack/.libcst.codemod.yaml +0 -18
- crackerjack/.pdm.toml +0 -1
- crackerjack/.pre-commit-config.yaml +0 -91
- crackerjack/.pytest_cache/.gitignore +0 -2
- crackerjack/.pytest_cache/CACHEDIR.TAG +0 -4
- crackerjack/.pytest_cache/README.md +0 -8
- crackerjack/.pytest_cache/v/cache/nodeids +0 -1
- crackerjack/.pytest_cache/v/cache/stepwise +0 -1
- crackerjack/.ruff_cache/.gitignore +0 -1
- crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
- crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
- crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
- crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
- crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
- crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
- crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
- crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
- crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
- crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
- crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
- crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
- crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
- crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
- crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
- crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
- crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
- crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
- crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
- crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
- crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
- crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
- crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
- crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
- crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
- crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
- crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
- crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
- crackerjack/.ruff_cache/CACHEDIR.TAG +0 -1
- crackerjack/crackerjack.py +0 -855
- crackerjack/pyproject.toml +0 -214
- crackerjack-0.18.2.dist-info/METADATA +0 -420
- crackerjack-0.18.2.dist-info/RECORD +0 -59
- crackerjack-0.18.2.dist-info/entry_points.txt +0 -4
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
"""Dead code detection and removal logic."""
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
import typing as t
|
|
5
|
+
|
|
6
|
+
from ...base import AgentContext
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DeadCodeDetector:
|
|
10
|
+
"""Detects dead code including unused imports and unreachable code."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, context: AgentContext) -> None:
|
|
13
|
+
"""Initialize detector with agent context.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
context: AgentContext for logging
|
|
17
|
+
"""
|
|
18
|
+
self.context = context
|
|
19
|
+
|
|
20
|
+
def analyze_dead_code(self, tree: ast.AST, content: str) -> dict[str, t.Any]:
|
|
21
|
+
"""Analyze code for dead code patterns.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
tree: AST tree
|
|
25
|
+
content: File content
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Analysis results
|
|
29
|
+
"""
|
|
30
|
+
analysis: dict[str, list[t.Any]] = {
|
|
31
|
+
"unused_imports": [],
|
|
32
|
+
"unused_variables": [],
|
|
33
|
+
"unused_functions": [],
|
|
34
|
+
"unused_classes": [],
|
|
35
|
+
"unreachable_code": [],
|
|
36
|
+
"removable_items": [],
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
analyzer_result = self._collect_usage_data(tree)
|
|
40
|
+
self._process_unused_imports(analysis, analyzer_result)
|
|
41
|
+
self._process_unused_functions(analysis, analyzer_result)
|
|
42
|
+
self._process_unused_classes(analysis, analyzer_result)
|
|
43
|
+
self._detect_unreachable_code(analysis, tree, content)
|
|
44
|
+
self._detect_redundant_code(analysis, tree, content)
|
|
45
|
+
|
|
46
|
+
return analysis
|
|
47
|
+
|
|
48
|
+
def _collect_usage_data(self, tree: ast.AST) -> dict[str, t.Any]:
|
|
49
|
+
"""Collect usage data from AST.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
tree: AST tree
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Usage data
|
|
56
|
+
"""
|
|
57
|
+
collector = UsageDataCollector()
|
|
58
|
+
analyzer = EnhancedUsageAnalyzer(collector)
|
|
59
|
+
analyzer.visit(tree)
|
|
60
|
+
return collector.get_results(analyzer)
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def _process_unused_imports(
|
|
64
|
+
analysis: dict[str, t.Any],
|
|
65
|
+
analyzer_result: dict[str, t.Any],
|
|
66
|
+
) -> None:
|
|
67
|
+
"""Process unused imports.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
analysis: Analysis dict
|
|
71
|
+
analyzer_result: Analyzer result
|
|
72
|
+
"""
|
|
73
|
+
import_lines: list[tuple[int, str, str]] = analyzer_result["import_lines"]
|
|
74
|
+
for line_no, name, import_type in import_lines:
|
|
75
|
+
if name not in analyzer_result["used_names"]:
|
|
76
|
+
analysis["unused_imports"].append(
|
|
77
|
+
{
|
|
78
|
+
"name": name,
|
|
79
|
+
"line": line_no,
|
|
80
|
+
"type": import_type,
|
|
81
|
+
},
|
|
82
|
+
)
|
|
83
|
+
analysis["removable_items"].append(f"unused import: {name}")
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def _process_unused_functions(
|
|
87
|
+
analysis: dict[str, t.Any],
|
|
88
|
+
analyzer_result: dict[str, t.Any],
|
|
89
|
+
) -> None:
|
|
90
|
+
"""Process unused functions.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
analysis: Analysis dict
|
|
94
|
+
analyzer_result: Analyzer result
|
|
95
|
+
"""
|
|
96
|
+
all_unused_functions: list[dict[str, t.Any]] = analyzer_result[
|
|
97
|
+
"unused_functions"
|
|
98
|
+
]
|
|
99
|
+
unused_functions = [
|
|
100
|
+
func
|
|
101
|
+
for func in all_unused_functions
|
|
102
|
+
if func["name"] not in analyzer_result["used_names"]
|
|
103
|
+
]
|
|
104
|
+
analysis["unused_functions"] = unused_functions
|
|
105
|
+
for func in unused_functions:
|
|
106
|
+
analysis["removable_items"].append(f"unused function: {func['name']}")
|
|
107
|
+
|
|
108
|
+
@staticmethod
|
|
109
|
+
def _process_unused_classes(
|
|
110
|
+
analysis: dict[str, t.Any], analyzer_result: dict[str, t.Any]
|
|
111
|
+
) -> None:
|
|
112
|
+
"""Process unused classes.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
analysis: Analysis dict
|
|
116
|
+
analyzer_result: Analyzer result
|
|
117
|
+
"""
|
|
118
|
+
if "unused_classes" not in analyzer_result:
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
unused_classes = [
|
|
122
|
+
cls
|
|
123
|
+
for cls in analyzer_result["unused_classes"]
|
|
124
|
+
if cls["name"] not in analyzer_result["used_names"]
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
analysis["unused_classes"] = unused_classes
|
|
128
|
+
for cls in unused_classes:
|
|
129
|
+
analysis["removable_items"].append(f"unused class: {cls['name']}")
|
|
130
|
+
|
|
131
|
+
@staticmethod
|
|
132
|
+
def _detect_unreachable_code(
|
|
133
|
+
analysis: dict[str, t.Any], tree: ast.AST, content: str
|
|
134
|
+
) -> None:
|
|
135
|
+
"""Detect unreachable code.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
analysis: Analysis dict
|
|
139
|
+
tree: AST tree
|
|
140
|
+
content: File content
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
class UnreachableCodeDetector(ast.NodeVisitor):
|
|
144
|
+
def __init__(self) -> None:
|
|
145
|
+
self.unreachable_blocks: list[dict[str, t.Any]] = []
|
|
146
|
+
|
|
147
|
+
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
|
|
148
|
+
self._check_unreachable_in_function(node)
|
|
149
|
+
self.generic_visit(node)
|
|
150
|
+
|
|
151
|
+
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
|
|
152
|
+
self._check_unreachable_in_function(node)
|
|
153
|
+
self.generic_visit(node)
|
|
154
|
+
|
|
155
|
+
def _check_unreachable_in_function(
|
|
156
|
+
self, node: ast.FunctionDef | ast.AsyncFunctionDef
|
|
157
|
+
) -> None:
|
|
158
|
+
for i, stmt in enumerate(node.body):
|
|
159
|
+
if isinstance(stmt, ast.Return | ast.Raise):
|
|
160
|
+
if i + 1 < len(node.body):
|
|
161
|
+
next_stmt = node.body[i + 1]
|
|
162
|
+
self.unreachable_blocks.append(
|
|
163
|
+
{
|
|
164
|
+
"type": "unreachable_after_return",
|
|
165
|
+
"line": next_stmt.lineno,
|
|
166
|
+
"function": node.name,
|
|
167
|
+
}
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
detector = UnreachableCodeDetector()
|
|
171
|
+
detector.visit(tree)
|
|
172
|
+
|
|
173
|
+
analysis["unreachable_code"] = detector.unreachable_blocks
|
|
174
|
+
for block in detector.unreachable_blocks:
|
|
175
|
+
analysis["removable_items"].append(
|
|
176
|
+
f"unreachable code after line {block['line']} in {block['function']}"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
@staticmethod
|
|
180
|
+
def _detect_redundant_code(
|
|
181
|
+
analysis: dict[str, t.Any], tree: ast.AST, content: str
|
|
182
|
+
) -> None:
|
|
183
|
+
"""Detect redundant code patterns.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
analysis: Analysis dict
|
|
187
|
+
tree: AST tree
|
|
188
|
+
content: File content
|
|
189
|
+
"""
|
|
190
|
+
lines = content.split("\n")
|
|
191
|
+
|
|
192
|
+
line_hashes = {}
|
|
193
|
+
for i, line in enumerate(lines):
|
|
194
|
+
if line.strip() and not line.strip().startswith("#"):
|
|
195
|
+
line_hash = hash(line.strip())
|
|
196
|
+
if line_hash in line_hashes:
|
|
197
|
+
analysis["removable_items"].append(
|
|
198
|
+
f"potential duplicate code at line {i + 1}"
|
|
199
|
+
)
|
|
200
|
+
line_hashes[line_hash] = i
|
|
201
|
+
|
|
202
|
+
class RedundantPatternDetector(ast.NodeVisitor):
|
|
203
|
+
def __init__(self) -> None:
|
|
204
|
+
self.redundant_items: list[dict[str, t.Any]] = []
|
|
205
|
+
|
|
206
|
+
def visit_ExceptHandler(self, node: ast.ExceptHandler) -> None:
|
|
207
|
+
if len(node.body) == 1 and isinstance(node.body[0], ast.Pass):
|
|
208
|
+
self.redundant_items.append(
|
|
209
|
+
{"type": "empty_except", "line": node.lineno}
|
|
210
|
+
)
|
|
211
|
+
self.generic_visit(node)
|
|
212
|
+
|
|
213
|
+
def visit_If(self, node: ast.If) -> None:
|
|
214
|
+
if isinstance(node.test, ast.Constant):
|
|
215
|
+
if node.test.value is True:
|
|
216
|
+
self.redundant_items.append(
|
|
217
|
+
{"type": "if_true", "line": node.lineno}
|
|
218
|
+
)
|
|
219
|
+
elif node.test.value is False:
|
|
220
|
+
self.redundant_items.append(
|
|
221
|
+
{"type": "if_false", "line": node.lineno}
|
|
222
|
+
)
|
|
223
|
+
self.generic_visit(node)
|
|
224
|
+
|
|
225
|
+
detector = RedundantPatternDetector()
|
|
226
|
+
detector.visit(tree)
|
|
227
|
+
|
|
228
|
+
for item in detector.redundant_items:
|
|
229
|
+
analysis["removable_items"].append(
|
|
230
|
+
f"redundant {item['type']} at line {item['line']}"
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
def find_lines_to_remove(
|
|
234
|
+
self, lines: list[str], analysis: dict[str, t.Any]
|
|
235
|
+
) -> set[int]:
|
|
236
|
+
"""Find lines to remove.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
lines: File lines
|
|
240
|
+
analysis: Analysis results
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
Set of line indices to remove
|
|
244
|
+
"""
|
|
245
|
+
lines_to_remove: set[int] = set()
|
|
246
|
+
|
|
247
|
+
for unused_import in analysis["unused_imports"]:
|
|
248
|
+
line_idx = unused_import["line"] - 1
|
|
249
|
+
if 0 <= line_idx < len(lines):
|
|
250
|
+
line = lines[line_idx]
|
|
251
|
+
if self._should_remove_import_line(line, unused_import):
|
|
252
|
+
lines_to_remove.add(line_idx)
|
|
253
|
+
|
|
254
|
+
return lines_to_remove
|
|
255
|
+
|
|
256
|
+
@staticmethod
|
|
257
|
+
def _should_remove_import_line(line: str, unused_import: dict[str, str]) -> bool:
|
|
258
|
+
"""Check if import line should be removed.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
line: Code line
|
|
262
|
+
unused_import: Unused import info
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
True if should remove
|
|
266
|
+
"""
|
|
267
|
+
if unused_import["type"] == "import":
|
|
268
|
+
return f"import {unused_import['name']}" in line
|
|
269
|
+
elif unused_import["type"] == "from_import":
|
|
270
|
+
return (
|
|
271
|
+
"from " in line
|
|
272
|
+
and unused_import["name"] in line
|
|
273
|
+
and line.strip().endswith(unused_import["name"])
|
|
274
|
+
)
|
|
275
|
+
return False
|
|
276
|
+
|
|
277
|
+
@staticmethod
|
|
278
|
+
def _find_unreachable_lines(
|
|
279
|
+
lines: list[str], analysis: dict[str, t.Any]
|
|
280
|
+
) -> set[int]:
|
|
281
|
+
"""Find unreachable code lines.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
lines: File lines
|
|
285
|
+
analysis: Analysis results
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
Set of line indices
|
|
289
|
+
"""
|
|
290
|
+
lines_to_remove: set[int] = set()
|
|
291
|
+
|
|
292
|
+
for item in analysis.get("unreachable_code", []):
|
|
293
|
+
if "line" in item:
|
|
294
|
+
line_idx = item["line"] - 1
|
|
295
|
+
if 0 <= line_idx < len(lines):
|
|
296
|
+
lines_to_remove.add(line_idx)
|
|
297
|
+
|
|
298
|
+
return lines_to_remove
|
|
299
|
+
|
|
300
|
+
@staticmethod
|
|
301
|
+
def _find_redundant_lines(lines: list[str], analysis: dict[str, t.Any]) -> set[int]:
|
|
302
|
+
"""Find redundant code lines.
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
lines: File lines
|
|
306
|
+
analysis: Analysis results
|
|
307
|
+
|
|
308
|
+
Returns:
|
|
309
|
+
Set of line indices
|
|
310
|
+
"""
|
|
311
|
+
lines_to_remove: set[int] = set()
|
|
312
|
+
|
|
313
|
+
for i in range(len(lines)):
|
|
314
|
+
if DeadCodeDetector._is_empty_except_block(lines, i):
|
|
315
|
+
empty_pass_idx = DeadCodeDetector._find_empty_pass_line(lines, i)
|
|
316
|
+
if empty_pass_idx is not None:
|
|
317
|
+
lines_to_remove.add(empty_pass_idx)
|
|
318
|
+
|
|
319
|
+
return lines_to_remove
|
|
320
|
+
|
|
321
|
+
@staticmethod
|
|
322
|
+
def _is_empty_except_block(lines: list[str], line_idx: int) -> bool:
|
|
323
|
+
"""Check if except block is empty.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
lines: File lines
|
|
327
|
+
line_idx: Line index
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
True if empty except block
|
|
331
|
+
"""
|
|
332
|
+
stripped = lines[line_idx].strip()
|
|
333
|
+
return stripped == "except: " or stripped.startswith("except ")
|
|
334
|
+
|
|
335
|
+
@staticmethod
|
|
336
|
+
def _find_empty_pass_line(lines: list[str], except_idx: int) -> int | None:
|
|
337
|
+
"""Find empty pass line in except block.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
lines: File lines
|
|
341
|
+
except_idx: Except line index
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
Pass line index or None
|
|
345
|
+
"""
|
|
346
|
+
for j in range(except_idx + 1, min(except_idx + 5, len(lines))):
|
|
347
|
+
next_line = lines[j].strip()
|
|
348
|
+
if not next_line:
|
|
349
|
+
continue
|
|
350
|
+
if next_line == "pass":
|
|
351
|
+
return j
|
|
352
|
+
break
|
|
353
|
+
return None
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
class UsageDataCollector:
|
|
357
|
+
"""Collects usage data from AST traversal."""
|
|
358
|
+
|
|
359
|
+
def __init__(self) -> None:
|
|
360
|
+
"""Initialize collector."""
|
|
361
|
+
self.imports: dict[str, str] = {}
|
|
362
|
+
self.import_lines: list[tuple[int, str, str]] = []
|
|
363
|
+
self.functions: list[dict[str, t.Any]] = []
|
|
364
|
+
self.classes: list[dict[str, t.Any]] = []
|
|
365
|
+
self.usages: set[str] = set()
|
|
366
|
+
|
|
367
|
+
def get_results(self, analyzer: "EnhancedUsageAnalyzer") -> dict[str, t.Any]:
|
|
368
|
+
"""Get collection results.
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
analyzer: Enhanced usage analyzer
|
|
372
|
+
|
|
373
|
+
Returns:
|
|
374
|
+
Results dict
|
|
375
|
+
"""
|
|
376
|
+
return {
|
|
377
|
+
"import_lines": self.import_lines,
|
|
378
|
+
"used_names": analyzer.used_names,
|
|
379
|
+
"unused_functions": self.functions,
|
|
380
|
+
"unused_classes": self.classes,
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
class EnhancedUsageAnalyzer(ast.NodeVisitor):
|
|
385
|
+
"""Enhanced analyzer for tracking usage."""
|
|
386
|
+
|
|
387
|
+
def __init__(self, collector: UsageDataCollector) -> None:
|
|
388
|
+
"""Initialize analyzer.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
collector: Usage data collector
|
|
392
|
+
"""
|
|
393
|
+
self.collector = collector
|
|
394
|
+
self.used_names: set[str] = set()
|
|
395
|
+
self.line_counter = 0
|
|
396
|
+
|
|
397
|
+
def visit_Import(self, node: ast.Import) -> None:
|
|
398
|
+
"""Visit import statement."""
|
|
399
|
+
for alias in node.names:
|
|
400
|
+
name = alias.asname or alias.name
|
|
401
|
+
self.collector.import_lines.append((node.lineno, name, "import"))
|
|
402
|
+
self.generic_visit(node)
|
|
403
|
+
|
|
404
|
+
def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
|
|
405
|
+
"""Visit from import statement."""
|
|
406
|
+
for alias in node.names:
|
|
407
|
+
name = alias.asname or alias.name
|
|
408
|
+
self.collector.import_lines.append((node.lineno, name, "from_import"))
|
|
409
|
+
self.generic_visit(node)
|
|
410
|
+
|
|
411
|
+
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
|
|
412
|
+
"""Visit function definition."""
|
|
413
|
+
self.collector.functions.append({"name": node.name, "line": node.lineno})
|
|
414
|
+
self.used_names.add(node.name)
|
|
415
|
+
self.generic_visit(node)
|
|
416
|
+
|
|
417
|
+
def visit_ClassDef(self, node: ast.ClassDef) -> None:
|
|
418
|
+
"""Visit class definition."""
|
|
419
|
+
self.collector.classes.append({"name": node.name, "line": node.lineno})
|
|
420
|
+
self.used_names.add(node.name)
|
|
421
|
+
self.generic_visit(node)
|
|
422
|
+
|
|
423
|
+
def visit_Name(self, node: ast.Name) -> None:
|
|
424
|
+
"""Visit name reference."""
|
|
425
|
+
self.used_names.add(node.id)
|
|
426
|
+
self.generic_visit(node)
|
|
427
|
+
|
|
428
|
+
def visit_Attribute(self, node: ast.Attribute) -> None:
|
|
429
|
+
"""Visit attribute access."""
|
|
430
|
+
self.used_names.add(node.attr)
|
|
431
|
+
self.generic_visit(node)
|
|
432
|
+
|
|
433
|
+
def visit_Call(self, node: ast.Call) -> None:
|
|
434
|
+
"""Visit function call."""
|
|
435
|
+
if isinstance(node.func, ast.Name):
|
|
436
|
+
self.used_names.add(node.func.id)
|
|
437
|
+
self.generic_visit(node)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Test creation helper modules.
|
|
2
|
+
|
|
3
|
+
This package provides modular helpers for test creation:
|
|
4
|
+
- TestASTAnalyzer: AST parsing and code structure extraction
|
|
5
|
+
- TestTemplateGenerator: Test template generation
|
|
6
|
+
- TestCoverageAnalyzer: Coverage analysis and gap detection
|
|
7
|
+
|
|
8
|
+
All helpers use AgentContext pattern (legacy, intentional).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from .test_ast_analyzer import TestASTAnalyzer
|
|
12
|
+
from .test_coverage_analyzer import TestCoverageAnalyzer
|
|
13
|
+
from .test_template_generator import TestTemplateGenerator
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"TestASTAnalyzer",
|
|
17
|
+
"TestCoverageAnalyzer",
|
|
18
|
+
"TestTemplateGenerator",
|
|
19
|
+
]
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""AST parsing helper for test creation.
|
|
2
|
+
|
|
3
|
+
This module provides AST analysis capabilities for extracting code structure
|
|
4
|
+
from Python source files. Uses AgentContext pattern (legacy, intentional).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import ast
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from crackerjack.agents.base import AgentContext
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TestASTAnalyzer:
|
|
15
|
+
"""AST parsing helper for test creation.
|
|
16
|
+
|
|
17
|
+
Uses AgentContext pattern (legacy, intentional).
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, context: AgentContext) -> None:
|
|
21
|
+
self.context = context
|
|
22
|
+
|
|
23
|
+
async def extract_functions_from_file(
|
|
24
|
+
self,
|
|
25
|
+
file_path: Path,
|
|
26
|
+
) -> list[dict[str, Any]]:
|
|
27
|
+
"""Extract all functions from a Python file."""
|
|
28
|
+
functions: list[dict[str, Any]] = []
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
content = self.context.get_file_content(file_path)
|
|
32
|
+
if not content:
|
|
33
|
+
return functions
|
|
34
|
+
|
|
35
|
+
tree = ast.parse(content)
|
|
36
|
+
functions = self._parse_function_nodes(tree)
|
|
37
|
+
|
|
38
|
+
except Exception as e:
|
|
39
|
+
self._log(f"Error parsing file {file_path}: {e}", "WARN")
|
|
40
|
+
|
|
41
|
+
return functions
|
|
42
|
+
|
|
43
|
+
def _parse_function_nodes(self, tree: ast.AST) -> list[dict[str, Any]]:
|
|
44
|
+
"""Parse function nodes from AST."""
|
|
45
|
+
functions: list[dict[str, Any]] = []
|
|
46
|
+
|
|
47
|
+
for node in ast.walk(tree):
|
|
48
|
+
if isinstance(
|
|
49
|
+
node, ast.FunctionDef | ast.AsyncFunctionDef
|
|
50
|
+
) and self._is_valid_function_node(node):
|
|
51
|
+
function_info = self._create_function_info(node)
|
|
52
|
+
|
|
53
|
+
function_info["is_async"] = isinstance(node, ast.AsyncFunctionDef)
|
|
54
|
+
functions.append(function_info)
|
|
55
|
+
|
|
56
|
+
return functions
|
|
57
|
+
|
|
58
|
+
def _is_valid_function_node(
|
|
59
|
+
self, node: ast.FunctionDef | ast.AsyncFunctionDef
|
|
60
|
+
) -> bool:
|
|
61
|
+
"""Check if function node should be included."""
|
|
62
|
+
return not node.name.startswith(("_", "test_"))
|
|
63
|
+
|
|
64
|
+
def _create_function_info(
|
|
65
|
+
self, node: ast.FunctionDef | ast.AsyncFunctionDef
|
|
66
|
+
) -> dict[str, Any]:
|
|
67
|
+
"""Create function info dict from AST node."""
|
|
68
|
+
return {
|
|
69
|
+
"name": node.name,
|
|
70
|
+
"line": node.lineno,
|
|
71
|
+
"signature": self._get_function_signature(node),
|
|
72
|
+
"args": [arg.arg for arg in node.args.args],
|
|
73
|
+
"returns": self._get_return_annotation(node),
|
|
74
|
+
"is_async": isinstance(node, ast.AsyncFunctionDef),
|
|
75
|
+
"docstring": ast.get_docstring(node) or "",
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async def extract_classes_from_file(self, file_path: Path) -> list[dict[str, Any]]:
|
|
79
|
+
"""Extract all classes from a Python file."""
|
|
80
|
+
classes: list[dict[str, Any]] = []
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
content = self.context.get_file_content(file_path)
|
|
84
|
+
if not content:
|
|
85
|
+
return classes
|
|
86
|
+
|
|
87
|
+
tree = ast.parse(content)
|
|
88
|
+
classes = self._process_ast_nodes_for_classes(tree)
|
|
89
|
+
|
|
90
|
+
except Exception as e:
|
|
91
|
+
self._log(f"Error parsing classes from {file_path}: {e}", "WARN")
|
|
92
|
+
|
|
93
|
+
return classes
|
|
94
|
+
|
|
95
|
+
def _process_ast_nodes_for_classes(self, tree: ast.AST) -> list[dict[str, Any]]:
|
|
96
|
+
"""Process AST nodes to extract class information."""
|
|
97
|
+
classes: list[dict[str, Any]] = []
|
|
98
|
+
|
|
99
|
+
for node in ast.walk(tree):
|
|
100
|
+
if isinstance(node, ast.ClassDef) and self._should_include_class(node):
|
|
101
|
+
class_info = self._create_class_info(node)
|
|
102
|
+
classes.append(class_info)
|
|
103
|
+
|
|
104
|
+
return classes
|
|
105
|
+
|
|
106
|
+
def _should_include_class(self, node: ast.ClassDef) -> bool:
|
|
107
|
+
"""Check if class should be included in analysis."""
|
|
108
|
+
return not node.name.startswith("_")
|
|
109
|
+
|
|
110
|
+
def _create_class_info(self, node: ast.ClassDef) -> dict[str, Any]:
|
|
111
|
+
"""Create class info dict from AST node."""
|
|
112
|
+
methods = self._extract_public_methods_from_class(node)
|
|
113
|
+
return {"name": node.name, "line": node.lineno, "methods": methods}
|
|
114
|
+
|
|
115
|
+
def _extract_public_methods_from_class(self, node: ast.ClassDef) -> list[str]:
|
|
116
|
+
"""Extract public method names from class node."""
|
|
117
|
+
return [
|
|
118
|
+
item.name
|
|
119
|
+
for item in node.body
|
|
120
|
+
if isinstance(item, ast.FunctionDef) and not item.name.startswith("_")
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
def _get_function_signature(
|
|
124
|
+
self, node: ast.FunctionDef | ast.AsyncFunctionDef
|
|
125
|
+
) -> str:
|
|
126
|
+
"""Get function signature string."""
|
|
127
|
+
args = [arg.arg for arg in node.args.args]
|
|
128
|
+
prefix = "async " if isinstance(node, ast.AsyncFunctionDef) else ""
|
|
129
|
+
return f"{prefix}{node.name}({', '.join(args)})"
|
|
130
|
+
|
|
131
|
+
def _get_return_annotation(
|
|
132
|
+
self, node: ast.FunctionDef | ast.AsyncFunctionDef
|
|
133
|
+
) -> str:
|
|
134
|
+
"""Get return type annotation string."""
|
|
135
|
+
if node.returns:
|
|
136
|
+
return ast.unparse(node.returns) if (hasattr(ast, "unparse")) else "Any"
|
|
137
|
+
return "Any"
|
|
138
|
+
|
|
139
|
+
async def function_has_test(
|
|
140
|
+
self,
|
|
141
|
+
func_info: dict[str, Any],
|
|
142
|
+
file_path: Path,
|
|
143
|
+
) -> bool:
|
|
144
|
+
"""Check if a function has corresponding test."""
|
|
145
|
+
test_file_path = await self.generate_test_file_path(file_path)
|
|
146
|
+
|
|
147
|
+
if not test_file_path.exists():
|
|
148
|
+
return False
|
|
149
|
+
|
|
150
|
+
test_content = self.context.get_file_content(test_file_path)
|
|
151
|
+
if not test_content:
|
|
152
|
+
return False
|
|
153
|
+
|
|
154
|
+
test_patterns = [
|
|
155
|
+
f"test_{func_info['name']}",
|
|
156
|
+
f"test_{func_info['name']}_",
|
|
157
|
+
f"def test_{func_info['name']}",
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
return any(pattern in test_content for pattern in test_patterns)
|
|
161
|
+
|
|
162
|
+
async def generate_test_file_path(self, source_file: Path) -> Path:
|
|
163
|
+
"""Generate path for test file corresponding to source file."""
|
|
164
|
+
tests_dir = self.context.project_path / "tests"
|
|
165
|
+
tests_dir.mkdir(exist_ok=True)
|
|
166
|
+
|
|
167
|
+
relative_path = source_file.relative_to(
|
|
168
|
+
self.context.project_path / "crackerjack",
|
|
169
|
+
)
|
|
170
|
+
test_name = f"test_{relative_path.stem}.py"
|
|
171
|
+
|
|
172
|
+
return tests_dir / test_name
|
|
173
|
+
|
|
174
|
+
def get_module_import_path(self, file_path: Path) -> str:
|
|
175
|
+
"""Get module import path from file path."""
|
|
176
|
+
try:
|
|
177
|
+
relative_path = file_path.relative_to(self.context.project_path)
|
|
178
|
+
parts = (*relative_path.parts[:-1], relative_path.stem)
|
|
179
|
+
return ".".join(parts)
|
|
180
|
+
except ValueError:
|
|
181
|
+
return file_path.stem
|
|
182
|
+
|
|
183
|
+
def should_skip_module_for_coverage(self, py_file: Path) -> bool:
|
|
184
|
+
"""Check if module should be skipped for coverage analysis."""
|
|
185
|
+
return py_file.name.startswith("test_") or py_file.name == "__init__.py"
|
|
186
|
+
|
|
187
|
+
def should_skip_file_for_testing(self, py_file: Path) -> bool:
|
|
188
|
+
"""Check if file should be skipped for testing."""
|
|
189
|
+
return py_file.name.startswith("test_")
|
|
190
|
+
|
|
191
|
+
def has_corresponding_test(self, file_path: str) -> bool:
|
|
192
|
+
"""Check if source file has corresponding test file."""
|
|
193
|
+
path = Path(file_path)
|
|
194
|
+
|
|
195
|
+
test_patterns = [
|
|
196
|
+
f"test_{path.stem}.py",
|
|
197
|
+
f"{path.stem}_test.py",
|
|
198
|
+
f"test_{path.stem}_*.py",
|
|
199
|
+
]
|
|
200
|
+
|
|
201
|
+
tests_dir = self.context.project_path / "tests"
|
|
202
|
+
if tests_dir.exists():
|
|
203
|
+
for pattern in test_patterns:
|
|
204
|
+
if list(tests_dir.glob(pattern)):
|
|
205
|
+
return True
|
|
206
|
+
|
|
207
|
+
return False
|
|
208
|
+
|
|
209
|
+
def get_relative_module_path(self, py_file: Path) -> str:
|
|
210
|
+
"""Get relative module path from project root."""
|
|
211
|
+
return str(py_file.relative_to(self.context.project_path))
|
|
212
|
+
|
|
213
|
+
def _log(self, message: str, level: str = "INFO") -> None:
|
|
214
|
+
"""Log message through context."""
|
|
215
|
+
# This is a helper - logging would typically go through the agent
|
|
216
|
+
pass
|