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,497 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import hashlib
|
|
5
|
+
import time
|
|
6
|
+
from collections.abc import Iterator
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import aiofiles
|
|
11
|
+
from acb.depends import Inject, depends
|
|
12
|
+
from acb.logger import Logger
|
|
13
|
+
|
|
14
|
+
from crackerjack.errors import FileError
|
|
15
|
+
from crackerjack.models.protocols import (
|
|
16
|
+
EnhancedFileSystemServiceProtocol,
|
|
17
|
+
ServiceProtocol,
|
|
18
|
+
)
|
|
19
|
+
from crackerjack.services.logging import LoggingContext
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class FileCache:
|
|
23
|
+
@depends.inject
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
logger: Inject[Logger],
|
|
27
|
+
max_size: int = 1000,
|
|
28
|
+
default_ttl: float = 300.0,
|
|
29
|
+
) -> None:
|
|
30
|
+
self.max_size = max_size
|
|
31
|
+
self.default_ttl = default_ttl
|
|
32
|
+
self._cache: dict[str, dict[str, Any]] = {}
|
|
33
|
+
self._access_times: dict[str, float] = {}
|
|
34
|
+
self.logger = logger
|
|
35
|
+
|
|
36
|
+
def get(self, key: str) -> str | None:
|
|
37
|
+
if key not in self._cache:
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
cache_entry = self._cache[key]
|
|
41
|
+
now = time.time()
|
|
42
|
+
|
|
43
|
+
if now - cache_entry["timestamp"] > cache_entry["ttl"]:
|
|
44
|
+
self._evict(key)
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
self._access_times[key] = now
|
|
48
|
+
self.logger.debug("Cache hit", key=key)
|
|
49
|
+
content: str | None = cache_entry["content"]
|
|
50
|
+
return content
|
|
51
|
+
|
|
52
|
+
def put(self, key: str, content: str, ttl: float | None = None) -> None:
|
|
53
|
+
if len(self._cache) >= self.max_size:
|
|
54
|
+
self._evict_lru()
|
|
55
|
+
|
|
56
|
+
now = time.time()
|
|
57
|
+
self._cache[key] = {
|
|
58
|
+
"content": content,
|
|
59
|
+
"timestamp": now,
|
|
60
|
+
"ttl": ttl or self.default_ttl,
|
|
61
|
+
"size": len(content),
|
|
62
|
+
}
|
|
63
|
+
self._access_times[key] = now
|
|
64
|
+
self.logger.debug("Cache put", key=key, size=len(content))
|
|
65
|
+
|
|
66
|
+
def _evict(self, key: str) -> None:
|
|
67
|
+
self._cache.pop(key, None)
|
|
68
|
+
self._access_times.pop(key, None)
|
|
69
|
+
|
|
70
|
+
def _evict_lru(self) -> None:
|
|
71
|
+
if not self._access_times:
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
lru_key = min(self._access_times, key=lambda k: self._access_times.get(k, 0.0))
|
|
75
|
+
self._evict(lru_key)
|
|
76
|
+
self.logger.debug("Cache LRU eviction", key=lru_key)
|
|
77
|
+
|
|
78
|
+
def clear(self) -> None:
|
|
79
|
+
self._cache.clear()
|
|
80
|
+
self._access_times.clear()
|
|
81
|
+
self.logger.debug("Cache cleared")
|
|
82
|
+
|
|
83
|
+
def get_stats(self) -> dict[str, Any]:
|
|
84
|
+
total_size = sum(entry["size"] for entry in self._cache.values())
|
|
85
|
+
return {
|
|
86
|
+
"entries": len(self._cache),
|
|
87
|
+
"max_size": self.max_size,
|
|
88
|
+
"total_content_size": total_size,
|
|
89
|
+
"memory_usage_mb": total_size / (1024 * 1024),
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class BatchFileOperations:
|
|
94
|
+
@depends.inject
|
|
95
|
+
def __init__(
|
|
96
|
+
self,
|
|
97
|
+
logger: Inject[Logger],
|
|
98
|
+
batch_size: int = 10,
|
|
99
|
+
) -> None:
|
|
100
|
+
self.batch_size = batch_size
|
|
101
|
+
self.read_queue: list[tuple[Path, asyncio.Future[str]]] = []
|
|
102
|
+
self.write_queue: list[tuple[Path, str, asyncio.Future[None]]] = []
|
|
103
|
+
self.logger = logger
|
|
104
|
+
|
|
105
|
+
async def queue_read(self, path: Path) -> str:
|
|
106
|
+
future: asyncio.Future[str] = asyncio.Future()
|
|
107
|
+
self.read_queue.append((path, future))
|
|
108
|
+
|
|
109
|
+
if len(self.read_queue) >= self.batch_size:
|
|
110
|
+
await self._flush_reads()
|
|
111
|
+
|
|
112
|
+
return await future
|
|
113
|
+
|
|
114
|
+
async def queue_write(self, path: Path, content: str) -> None:
|
|
115
|
+
future: asyncio.Future[None] = asyncio.Future[None]()
|
|
116
|
+
self.write_queue.append((path, content, future))
|
|
117
|
+
|
|
118
|
+
if len(self.write_queue) >= self.batch_size:
|
|
119
|
+
await self._flush_writes()
|
|
120
|
+
|
|
121
|
+
await future
|
|
122
|
+
|
|
123
|
+
async def flush_all(self) -> None:
|
|
124
|
+
await asyncio.gather(
|
|
125
|
+
self._flush_reads(),
|
|
126
|
+
self._flush_writes(),
|
|
127
|
+
return_exceptions=True,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
async def _flush_reads(self) -> None:
|
|
131
|
+
if not self.read_queue:
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
with LoggingContext("batch_file_reads", count=len(self.read_queue)):
|
|
135
|
+
batch = self.read_queue.copy()
|
|
136
|
+
self.read_queue.clear()
|
|
137
|
+
|
|
138
|
+
from itertools import starmap
|
|
139
|
+
|
|
140
|
+
tasks = list(starmap(self._read_single_async, batch))
|
|
141
|
+
|
|
142
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
143
|
+
|
|
144
|
+
async def _flush_writes(self) -> None:
|
|
145
|
+
if not self.write_queue:
|
|
146
|
+
return
|
|
147
|
+
|
|
148
|
+
with LoggingContext("batch_file_writes", count=len(self.write_queue)):
|
|
149
|
+
batch = self.write_queue.copy()
|
|
150
|
+
self.write_queue.clear()
|
|
151
|
+
|
|
152
|
+
from itertools import starmap
|
|
153
|
+
|
|
154
|
+
tasks = list(starmap(self._write_single_async, batch))
|
|
155
|
+
|
|
156
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
async def _read_single_async(path: Path, future: asyncio.Future[str]) -> None:
|
|
160
|
+
try:
|
|
161
|
+
async with aiofiles.open(path, encoding="utf-8") as f:
|
|
162
|
+
content = await f.read()
|
|
163
|
+
future.set_result(content)
|
|
164
|
+
except Exception as e:
|
|
165
|
+
future.set_exception(e)
|
|
166
|
+
|
|
167
|
+
@staticmethod
|
|
168
|
+
async def _write_single_async(
|
|
169
|
+
path: Path,
|
|
170
|
+
content: str,
|
|
171
|
+
future: asyncio.Future[None],
|
|
172
|
+
) -> None:
|
|
173
|
+
try:
|
|
174
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
175
|
+
async with aiofiles.open(path, "w", encoding="utf-8") as f:
|
|
176
|
+
await f.write(content)
|
|
177
|
+
future.set_result(None)
|
|
178
|
+
except Exception as e:
|
|
179
|
+
future.set_exception(e)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class EnhancedFileSystemService(EnhancedFileSystemServiceProtocol, ServiceProtocol):
|
|
183
|
+
@depends.inject
|
|
184
|
+
def __init__(
|
|
185
|
+
self,
|
|
186
|
+
logger: Inject[Logger],
|
|
187
|
+
cache_size: int = 1000,
|
|
188
|
+
cache_ttl: float = 300.0,
|
|
189
|
+
batch_size: int = 10,
|
|
190
|
+
enable_async: bool = True,
|
|
191
|
+
) -> None:
|
|
192
|
+
# Use keyword args to avoid DI/positional ambiguity
|
|
193
|
+
self.cache = FileCache(max_size=cache_size, default_ttl=cache_ttl)
|
|
194
|
+
self.batch_ops = BatchFileOperations(batch_size) if enable_async else None
|
|
195
|
+
self.enable_async = enable_async
|
|
196
|
+
self.logger = logger
|
|
197
|
+
|
|
198
|
+
self._file_timestamps: dict[str, float] = {}
|
|
199
|
+
|
|
200
|
+
def read_file(self, path: str | Path) -> str:
|
|
201
|
+
path_obj = Path(path) if isinstance(path, str) else path
|
|
202
|
+
|
|
203
|
+
with LoggingContext("read_file", path=str(path_obj)):
|
|
204
|
+
cache_key = self._get_cache_key(path_obj)
|
|
205
|
+
cached_content = self._get_from_cache(cache_key, path_obj)
|
|
206
|
+
|
|
207
|
+
if cached_content is not None:
|
|
208
|
+
return cached_content
|
|
209
|
+
|
|
210
|
+
content = self._read_file_direct(path_obj)
|
|
211
|
+
|
|
212
|
+
self.cache.put(cache_key, content)
|
|
213
|
+
self._file_timestamps[str(path_obj)] = path_obj.stat().st_mtime
|
|
214
|
+
|
|
215
|
+
return content
|
|
216
|
+
|
|
217
|
+
def write_file(self, path: str | Path, content: str) -> None:
|
|
218
|
+
path_obj = Path(path) if isinstance(path, str) else path
|
|
219
|
+
# Validate content type before logging/length computation
|
|
220
|
+
if not isinstance(content, str):
|
|
221
|
+
raise TypeError("Content must be a string")
|
|
222
|
+
|
|
223
|
+
with LoggingContext("write_file", path=str(path_obj), size=len(content)):
|
|
224
|
+
self._write_file_direct(path_obj, content)
|
|
225
|
+
|
|
226
|
+
cache_key = self._get_cache_key(path_obj)
|
|
227
|
+
self.cache._evict(cache_key)
|
|
228
|
+
self._file_timestamps[str(path_obj)] = time.time()
|
|
229
|
+
|
|
230
|
+
async def read_file_async(self, path: Path) -> str:
|
|
231
|
+
if not self.enable_async or not self.batch_ops:
|
|
232
|
+
return self.read_file(path)
|
|
233
|
+
|
|
234
|
+
cache_key = self._get_cache_key(path)
|
|
235
|
+
cached_content = self._get_from_cache(cache_key, path)
|
|
236
|
+
|
|
237
|
+
if cached_content is not None:
|
|
238
|
+
return cached_content
|
|
239
|
+
|
|
240
|
+
content = await self.batch_ops.queue_read(path)
|
|
241
|
+
|
|
242
|
+
self.cache.put(cache_key, content)
|
|
243
|
+
self._file_timestamps[str(path)] = path.stat().st_mtime
|
|
244
|
+
|
|
245
|
+
return content
|
|
246
|
+
|
|
247
|
+
async def write_file_async(self, path: Path, content: str) -> None:
|
|
248
|
+
if not self.enable_async or not self.batch_ops:
|
|
249
|
+
self.write_file(path, content)
|
|
250
|
+
return
|
|
251
|
+
|
|
252
|
+
await self.batch_ops.queue_write(path, content)
|
|
253
|
+
|
|
254
|
+
cache_key = self._get_cache_key(path)
|
|
255
|
+
self.cache._evict(cache_key)
|
|
256
|
+
self._file_timestamps[str(path)] = time.time()
|
|
257
|
+
|
|
258
|
+
async def read_multiple_files(self, paths: list[Path]) -> dict[Path, str]:
|
|
259
|
+
results = {}
|
|
260
|
+
|
|
261
|
+
if not self.enable_async or not self.batch_ops:
|
|
262
|
+
for path in paths:
|
|
263
|
+
try:
|
|
264
|
+
results[path] = self.read_file(path)
|
|
265
|
+
except Exception as e:
|
|
266
|
+
self.logger.exception(
|
|
267
|
+
"Failed to read file",
|
|
268
|
+
path=str(path),
|
|
269
|
+
error=str(e),
|
|
270
|
+
)
|
|
271
|
+
results[path] = ""
|
|
272
|
+
return results
|
|
273
|
+
|
|
274
|
+
with LoggingContext("read_multiple_files", count=len(paths)):
|
|
275
|
+
tasks = [self.read_file_async(path) for path in paths]
|
|
276
|
+
|
|
277
|
+
results_list = await asyncio.gather(*tasks, return_exceptions=True)
|
|
278
|
+
|
|
279
|
+
for path, result in zip(paths, results_list, strict=False):
|
|
280
|
+
if isinstance(result, Exception):
|
|
281
|
+
self.logger.error(
|
|
282
|
+
"Failed to read file",
|
|
283
|
+
path=str(path),
|
|
284
|
+
error=str(result),
|
|
285
|
+
)
|
|
286
|
+
results[path] = ""
|
|
287
|
+
elif isinstance(result, str): # Explicit check for mypy
|
|
288
|
+
results[path] = result
|
|
289
|
+
|
|
290
|
+
return results
|
|
291
|
+
|
|
292
|
+
async def write_multiple_files(self, file_data: dict[Path, str]) -> None:
|
|
293
|
+
if not self.enable_async or not self.batch_ops:
|
|
294
|
+
for path, content in file_data.items():
|
|
295
|
+
try:
|
|
296
|
+
self.write_file(path, content)
|
|
297
|
+
except Exception as e:
|
|
298
|
+
self.logger.exception(
|
|
299
|
+
"Failed to write file",
|
|
300
|
+
path=str(path),
|
|
301
|
+
error=str(e),
|
|
302
|
+
)
|
|
303
|
+
return
|
|
304
|
+
|
|
305
|
+
with LoggingContext("write_multiple_files", count=len(file_data)):
|
|
306
|
+
from itertools import starmap
|
|
307
|
+
|
|
308
|
+
tasks = list(starmap(self.write_file_async, file_data.items()))
|
|
309
|
+
|
|
310
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
311
|
+
|
|
312
|
+
@staticmethod
|
|
313
|
+
def _get_cache_key(path: Path) -> str:
|
|
314
|
+
path_str = str(path.resolve())
|
|
315
|
+
return hashlib.md5(path_str.encode(), usedforsecurity=False).hexdigest()
|
|
316
|
+
|
|
317
|
+
def _get_from_cache(self, cache_key: str, path: Path) -> str | None:
|
|
318
|
+
if not path.exists():
|
|
319
|
+
return None
|
|
320
|
+
|
|
321
|
+
path_str = str(path)
|
|
322
|
+
if path_str in self._file_timestamps:
|
|
323
|
+
current_mtime = path.stat().st_mtime
|
|
324
|
+
cached_mtime = self._file_timestamps[path_str]
|
|
325
|
+
|
|
326
|
+
if current_mtime > cached_mtime:
|
|
327
|
+
self.cache._evict(cache_key)
|
|
328
|
+
del self._file_timestamps[path_str]
|
|
329
|
+
return None
|
|
330
|
+
|
|
331
|
+
return self.cache.get(cache_key)
|
|
332
|
+
|
|
333
|
+
@staticmethod
|
|
334
|
+
def _validate_file_exists(path: Path) -> None:
|
|
335
|
+
"""Validate that a file exists."""
|
|
336
|
+
if not path.exists():
|
|
337
|
+
raise FileError(
|
|
338
|
+
message=f"File does not exist: {path}",
|
|
339
|
+
details=f"Attempted to read file at {path.absolute()}",
|
|
340
|
+
recovery="Check file path and ensure file exists",
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
@staticmethod
|
|
344
|
+
def _handle_read_error(error: Exception, path: Path) -> None:
|
|
345
|
+
"""Handle file read errors."""
|
|
346
|
+
if isinstance(error, PermissionError):
|
|
347
|
+
raise FileError(
|
|
348
|
+
message=f"Permission denied reading file: {path}",
|
|
349
|
+
details=str(error),
|
|
350
|
+
recovery="Check file permissions and user access rights",
|
|
351
|
+
) from error
|
|
352
|
+
elif isinstance(error, UnicodeDecodeError):
|
|
353
|
+
raise FileError(
|
|
354
|
+
message=f"Unable to decode file as UTF-8: {path}",
|
|
355
|
+
details=str(error),
|
|
356
|
+
recovery="Ensure file is text - based and UTF-8 encoded",
|
|
357
|
+
) from error
|
|
358
|
+
elif isinstance(error, OSError):
|
|
359
|
+
raise FileError(
|
|
360
|
+
message=f"System error reading file: {path}",
|
|
361
|
+
details=str(error),
|
|
362
|
+
recovery="Check disk space and file system integrity",
|
|
363
|
+
) from error
|
|
364
|
+
|
|
365
|
+
@staticmethod
|
|
366
|
+
def _read_file_direct(path: Path) -> str:
|
|
367
|
+
try:
|
|
368
|
+
EnhancedFileSystemService._validate_file_exists(path)
|
|
369
|
+
return path.read_text(encoding="utf-8")
|
|
370
|
+
except (PermissionError, UnicodeDecodeError, OSError) as e:
|
|
371
|
+
EnhancedFileSystemService._handle_read_error(e, path)
|
|
372
|
+
raise # Ensure type checker knows this doesn't return
|
|
373
|
+
|
|
374
|
+
@staticmethod
|
|
375
|
+
def _write_file_direct(path: Path, content: str) -> None:
|
|
376
|
+
try:
|
|
377
|
+
try:
|
|
378
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
379
|
+
except OSError as e:
|
|
380
|
+
raise FileError(
|
|
381
|
+
message=f"Cannot create parent directory: {path.parent}",
|
|
382
|
+
details=str(e),
|
|
383
|
+
recovery="Check directory permissions and disk space",
|
|
384
|
+
) from e
|
|
385
|
+
|
|
386
|
+
path.write_text(content, encoding="utf-8")
|
|
387
|
+
|
|
388
|
+
except PermissionError as e:
|
|
389
|
+
raise FileError(
|
|
390
|
+
message=f"Permission denied writing file: {path}",
|
|
391
|
+
details=str(e),
|
|
392
|
+
recovery="Check file and directory permissions",
|
|
393
|
+
) from e
|
|
394
|
+
except OSError as e:
|
|
395
|
+
raise FileError(
|
|
396
|
+
message=f"System error writing file: {path}",
|
|
397
|
+
details=str(e),
|
|
398
|
+
recovery="Check disk space and file system integrity",
|
|
399
|
+
) from e
|
|
400
|
+
|
|
401
|
+
@staticmethod
|
|
402
|
+
def file_exists(path: str | Path) -> bool:
|
|
403
|
+
return (Path(path) if isinstance(path, str) else path).exists()
|
|
404
|
+
|
|
405
|
+
def create_directory(self, path: str | Path) -> None:
|
|
406
|
+
path_obj = Path(path) if isinstance(path, str) else path
|
|
407
|
+
try:
|
|
408
|
+
path_obj.mkdir(parents=True, exist_ok=True)
|
|
409
|
+
self.logger.debug("Directory created", path=str(path_obj))
|
|
410
|
+
except OSError as e:
|
|
411
|
+
raise FileError(
|
|
412
|
+
message=f"Cannot create directory: {path_obj}",
|
|
413
|
+
details=str(e),
|
|
414
|
+
recovery="Check parent directory permissions and disk space",
|
|
415
|
+
) from e
|
|
416
|
+
|
|
417
|
+
def delete_file(self, path: str | Path) -> None:
|
|
418
|
+
path_obj = Path(path) if isinstance(path, str) else path
|
|
419
|
+
|
|
420
|
+
try:
|
|
421
|
+
if path_obj.exists():
|
|
422
|
+
path_obj.unlink()
|
|
423
|
+
|
|
424
|
+
cache_key = self._get_cache_key(path_obj)
|
|
425
|
+
self.cache._evict(cache_key)
|
|
426
|
+
self._file_timestamps.pop(str(path_obj), None)
|
|
427
|
+
|
|
428
|
+
self.logger.debug("File deleted", path=str(path_obj))
|
|
429
|
+
except OSError as e:
|
|
430
|
+
raise FileError(
|
|
431
|
+
message=f"Cannot delete file: {path_obj}",
|
|
432
|
+
details=str(e),
|
|
433
|
+
recovery="Check file permissions",
|
|
434
|
+
) from e
|
|
435
|
+
|
|
436
|
+
@staticmethod
|
|
437
|
+
def list_files(path: str | Path, pattern: str = "*") -> Iterator[Path]:
|
|
438
|
+
path_obj = Path(path) if isinstance(path, str) else path
|
|
439
|
+
|
|
440
|
+
if not path_obj.is_dir():
|
|
441
|
+
raise FileError(
|
|
442
|
+
message=f"Path is not a directory: {path_obj}",
|
|
443
|
+
details=f"Cannot list[t.Any] files in {path_obj}",
|
|
444
|
+
recovery="Ensure path points to a valid directory",
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
try:
|
|
448
|
+
yield from path_obj.glob(pattern)
|
|
449
|
+
except OSError as e:
|
|
450
|
+
raise FileError(
|
|
451
|
+
message=f"Cannot list[t.Any] files in directory: {path_obj}",
|
|
452
|
+
details=str(e),
|
|
453
|
+
recovery="Check directory permissions",
|
|
454
|
+
) from e
|
|
455
|
+
|
|
456
|
+
async def flush_operations(self) -> None:
|
|
457
|
+
if self.batch_ops:
|
|
458
|
+
await self.batch_ops.flush_all()
|
|
459
|
+
|
|
460
|
+
def get_cache_stats(self) -> dict[str, Any]:
|
|
461
|
+
return self.cache.get_stats()
|
|
462
|
+
|
|
463
|
+
def clear_cache(self) -> None:
|
|
464
|
+
self.cache.clear()
|
|
465
|
+
self._file_timestamps.clear()
|
|
466
|
+
|
|
467
|
+
def exists(self, path: str | Path) -> bool:
|
|
468
|
+
return (Path(path) if isinstance(path, str) else path).exists()
|
|
469
|
+
|
|
470
|
+
def mkdir(self, path: str | Path, parents: bool = False) -> None:
|
|
471
|
+
path_obj = Path(path) if isinstance(path, str) else path
|
|
472
|
+
try:
|
|
473
|
+
path_obj.mkdir(parents=parents, exist_ok=True)
|
|
474
|
+
except OSError as e:
|
|
475
|
+
raise FileError(
|
|
476
|
+
message=f"Cannot create directory: {path_obj}",
|
|
477
|
+
details=str(e),
|
|
478
|
+
recovery="Check parent directory permissions",
|
|
479
|
+
) from e
|
|
480
|
+
|
|
481
|
+
async def _on_start(self) -> None:
|
|
482
|
+
"""
|
|
483
|
+
Lifecycle method called when the service is started.
|
|
484
|
+
"""
|
|
485
|
+
self.logger.debug("EnhancedFileSystemService started")
|
|
486
|
+
|
|
487
|
+
async def _on_stop(self) -> None:
|
|
488
|
+
"""
|
|
489
|
+
Lifecycle method called when the service is stopped.
|
|
490
|
+
"""
|
|
491
|
+
self.logger.debug("EnhancedFileSystemService stopped")
|
|
492
|
+
|
|
493
|
+
async def _on_reload(self) -> None:
|
|
494
|
+
"""
|
|
495
|
+
Lifecycle method called when the service is reloaded.
|
|
496
|
+
"""
|
|
497
|
+
self.logger.debug("EnhancedFileSystemService reloaded")
|