aiecs 1.0.1__py3-none-any.whl → 1.7.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of aiecs might be problematic. Click here for more details.
- aiecs/__init__.py +13 -16
- aiecs/__main__.py +7 -7
- aiecs/aiecs_client.py +269 -75
- aiecs/application/executors/operation_executor.py +79 -54
- aiecs/application/knowledge_graph/__init__.py +7 -0
- aiecs/application/knowledge_graph/builder/__init__.py +37 -0
- aiecs/application/knowledge_graph/builder/data_quality.py +302 -0
- aiecs/application/knowledge_graph/builder/data_reshaping.py +293 -0
- aiecs/application/knowledge_graph/builder/document_builder.py +369 -0
- aiecs/application/knowledge_graph/builder/graph_builder.py +490 -0
- aiecs/application/knowledge_graph/builder/import_optimizer.py +396 -0
- aiecs/application/knowledge_graph/builder/schema_inference.py +462 -0
- aiecs/application/knowledge_graph/builder/schema_mapping.py +563 -0
- aiecs/application/knowledge_graph/builder/structured_pipeline.py +1384 -0
- aiecs/application/knowledge_graph/builder/text_chunker.py +317 -0
- aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
- aiecs/application/knowledge_graph/extractors/base.py +98 -0
- aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +422 -0
- aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +347 -0
- aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +241 -0
- aiecs/application/knowledge_graph/fusion/__init__.py +78 -0
- aiecs/application/knowledge_graph/fusion/ab_testing.py +395 -0
- aiecs/application/knowledge_graph/fusion/abbreviation_expander.py +327 -0
- aiecs/application/knowledge_graph/fusion/alias_index.py +597 -0
- aiecs/application/knowledge_graph/fusion/alias_matcher.py +384 -0
- aiecs/application/knowledge_graph/fusion/cache_coordinator.py +343 -0
- aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +433 -0
- aiecs/application/knowledge_graph/fusion/entity_linker.py +511 -0
- aiecs/application/knowledge_graph/fusion/evaluation_dataset.py +240 -0
- aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +632 -0
- aiecs/application/knowledge_graph/fusion/matching_config.py +489 -0
- aiecs/application/knowledge_graph/fusion/name_normalizer.py +352 -0
- aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +183 -0
- aiecs/application/knowledge_graph/fusion/semantic_name_matcher.py +464 -0
- aiecs/application/knowledge_graph/fusion/similarity_pipeline.py +534 -0
- aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
- aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +342 -0
- aiecs/application/knowledge_graph/pattern_matching/query_executor.py +366 -0
- aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
- aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +195 -0
- aiecs/application/knowledge_graph/profiling/query_profiler.py +223 -0
- aiecs/application/knowledge_graph/reasoning/__init__.py +27 -0
- aiecs/application/knowledge_graph/reasoning/evidence_synthesis.py +341 -0
- aiecs/application/knowledge_graph/reasoning/inference_engine.py +500 -0
- aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +163 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/__init__.py +79 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_builder.py +513 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_nodes.py +913 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +866 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +475 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +396 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +208 -0
- aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +170 -0
- aiecs/application/knowledge_graph/reasoning/query_planner.py +855 -0
- aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +518 -0
- aiecs/application/knowledge_graph/retrieval/__init__.py +27 -0
- aiecs/application/knowledge_graph/retrieval/query_intent_classifier.py +211 -0
- aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +592 -0
- aiecs/application/knowledge_graph/retrieval/strategy_types.py +23 -0
- aiecs/application/knowledge_graph/search/__init__.py +59 -0
- aiecs/application/knowledge_graph/search/hybrid_search.py +457 -0
- aiecs/application/knowledge_graph/search/reranker.py +293 -0
- aiecs/application/knowledge_graph/search/reranker_strategies.py +535 -0
- aiecs/application/knowledge_graph/search/text_similarity.py +392 -0
- aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
- aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +305 -0
- aiecs/application/knowledge_graph/traversal/path_scorer.py +271 -0
- aiecs/application/knowledge_graph/validators/__init__.py +13 -0
- aiecs/application/knowledge_graph/validators/relation_validator.py +239 -0
- aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
- aiecs/application/knowledge_graph/visualization/graph_visualizer.py +313 -0
- aiecs/common/__init__.py +9 -0
- aiecs/common/knowledge_graph/__init__.py +17 -0
- aiecs/common/knowledge_graph/runnable.py +471 -0
- aiecs/config/__init__.py +20 -5
- aiecs/config/config.py +762 -31
- aiecs/config/graph_config.py +131 -0
- aiecs/config/tool_config.py +399 -0
- aiecs/core/__init__.py +29 -13
- aiecs/core/interface/__init__.py +2 -2
- aiecs/core/interface/execution_interface.py +22 -22
- aiecs/core/interface/storage_interface.py +37 -88
- aiecs/core/registry/__init__.py +31 -0
- aiecs/core/registry/service_registry.py +92 -0
- aiecs/domain/__init__.py +270 -1
- aiecs/domain/agent/__init__.py +191 -0
- aiecs/domain/agent/base_agent.py +3870 -0
- aiecs/domain/agent/exceptions.py +99 -0
- aiecs/domain/agent/graph_aware_mixin.py +569 -0
- aiecs/domain/agent/hybrid_agent.py +1435 -0
- aiecs/domain/agent/integration/__init__.py +29 -0
- aiecs/domain/agent/integration/context_compressor.py +216 -0
- aiecs/domain/agent/integration/context_engine_adapter.py +587 -0
- aiecs/domain/agent/integration/protocols.py +281 -0
- aiecs/domain/agent/integration/retry_policy.py +218 -0
- aiecs/domain/agent/integration/role_config.py +213 -0
- aiecs/domain/agent/knowledge_aware_agent.py +1892 -0
- aiecs/domain/agent/lifecycle.py +291 -0
- aiecs/domain/agent/llm_agent.py +692 -0
- aiecs/domain/agent/memory/__init__.py +12 -0
- aiecs/domain/agent/memory/conversation.py +1124 -0
- aiecs/domain/agent/migration/__init__.py +14 -0
- aiecs/domain/agent/migration/conversion.py +163 -0
- aiecs/domain/agent/migration/legacy_wrapper.py +86 -0
- aiecs/domain/agent/models.py +884 -0
- aiecs/domain/agent/observability.py +479 -0
- aiecs/domain/agent/persistence.py +449 -0
- aiecs/domain/agent/prompts/__init__.py +29 -0
- aiecs/domain/agent/prompts/builder.py +159 -0
- aiecs/domain/agent/prompts/formatters.py +187 -0
- aiecs/domain/agent/prompts/template.py +255 -0
- aiecs/domain/agent/registry.py +253 -0
- aiecs/domain/agent/tool_agent.py +444 -0
- aiecs/domain/agent/tools/__init__.py +15 -0
- aiecs/domain/agent/tools/schema_generator.py +364 -0
- aiecs/domain/community/__init__.py +155 -0
- aiecs/domain/community/agent_adapter.py +469 -0
- aiecs/domain/community/analytics.py +432 -0
- aiecs/domain/community/collaborative_workflow.py +648 -0
- aiecs/domain/community/communication_hub.py +634 -0
- aiecs/domain/community/community_builder.py +320 -0
- aiecs/domain/community/community_integration.py +796 -0
- aiecs/domain/community/community_manager.py +803 -0
- aiecs/domain/community/decision_engine.py +849 -0
- aiecs/domain/community/exceptions.py +231 -0
- aiecs/domain/community/models/__init__.py +33 -0
- aiecs/domain/community/models/community_models.py +234 -0
- aiecs/domain/community/resource_manager.py +461 -0
- aiecs/domain/community/shared_context_manager.py +589 -0
- aiecs/domain/context/__init__.py +40 -10
- aiecs/domain/context/context_engine.py +1910 -0
- aiecs/domain/context/conversation_models.py +87 -53
- aiecs/domain/context/graph_memory.py +582 -0
- aiecs/domain/execution/model.py +12 -4
- aiecs/domain/knowledge_graph/__init__.py +19 -0
- aiecs/domain/knowledge_graph/models/__init__.py +52 -0
- aiecs/domain/knowledge_graph/models/entity.py +148 -0
- aiecs/domain/knowledge_graph/models/evidence.py +178 -0
- aiecs/domain/knowledge_graph/models/inference_rule.py +184 -0
- aiecs/domain/knowledge_graph/models/path.py +171 -0
- aiecs/domain/knowledge_graph/models/path_pattern.py +171 -0
- aiecs/domain/knowledge_graph/models/query.py +261 -0
- aiecs/domain/knowledge_graph/models/query_plan.py +181 -0
- aiecs/domain/knowledge_graph/models/relation.py +202 -0
- aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
- aiecs/domain/knowledge_graph/schema/entity_type.py +131 -0
- aiecs/domain/knowledge_graph/schema/graph_schema.py +253 -0
- aiecs/domain/knowledge_graph/schema/property_schema.py +143 -0
- aiecs/domain/knowledge_graph/schema/relation_type.py +163 -0
- aiecs/domain/knowledge_graph/schema/schema_manager.py +691 -0
- aiecs/domain/knowledge_graph/schema/type_enums.py +209 -0
- aiecs/domain/task/dsl_processor.py +172 -56
- aiecs/domain/task/model.py +20 -8
- aiecs/domain/task/task_context.py +27 -24
- aiecs/infrastructure/__init__.py +0 -2
- aiecs/infrastructure/graph_storage/__init__.py +11 -0
- aiecs/infrastructure/graph_storage/base.py +837 -0
- aiecs/infrastructure/graph_storage/batch_operations.py +458 -0
- aiecs/infrastructure/graph_storage/cache.py +424 -0
- aiecs/infrastructure/graph_storage/distributed.py +223 -0
- aiecs/infrastructure/graph_storage/error_handling.py +380 -0
- aiecs/infrastructure/graph_storage/graceful_degradation.py +294 -0
- aiecs/infrastructure/graph_storage/health_checks.py +378 -0
- aiecs/infrastructure/graph_storage/in_memory.py +1197 -0
- aiecs/infrastructure/graph_storage/index_optimization.py +446 -0
- aiecs/infrastructure/graph_storage/lazy_loading.py +431 -0
- aiecs/infrastructure/graph_storage/metrics.py +344 -0
- aiecs/infrastructure/graph_storage/migration.py +400 -0
- aiecs/infrastructure/graph_storage/pagination.py +483 -0
- aiecs/infrastructure/graph_storage/performance_monitoring.py +456 -0
- aiecs/infrastructure/graph_storage/postgres.py +1563 -0
- aiecs/infrastructure/graph_storage/property_storage.py +353 -0
- aiecs/infrastructure/graph_storage/protocols.py +76 -0
- aiecs/infrastructure/graph_storage/query_optimizer.py +642 -0
- aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
- aiecs/infrastructure/graph_storage/sqlite.py +1373 -0
- aiecs/infrastructure/graph_storage/streaming.py +487 -0
- aiecs/infrastructure/graph_storage/tenant.py +412 -0
- aiecs/infrastructure/messaging/celery_task_manager.py +92 -54
- aiecs/infrastructure/messaging/websocket_manager.py +51 -35
- aiecs/infrastructure/monitoring/__init__.py +22 -0
- aiecs/infrastructure/monitoring/executor_metrics.py +45 -11
- aiecs/infrastructure/monitoring/global_metrics_manager.py +212 -0
- aiecs/infrastructure/monitoring/structured_logger.py +3 -7
- aiecs/infrastructure/monitoring/tracing_manager.py +63 -35
- aiecs/infrastructure/persistence/__init__.py +14 -1
- aiecs/infrastructure/persistence/context_engine_client.py +184 -0
- aiecs/infrastructure/persistence/database_manager.py +67 -43
- aiecs/infrastructure/persistence/file_storage.py +180 -103
- aiecs/infrastructure/persistence/redis_client.py +74 -21
- aiecs/llm/__init__.py +73 -25
- aiecs/llm/callbacks/__init__.py +11 -0
- aiecs/llm/{custom_callbacks.py → callbacks/custom_callbacks.py} +26 -19
- aiecs/llm/client_factory.py +224 -36
- aiecs/llm/client_resolver.py +155 -0
- aiecs/llm/clients/__init__.py +38 -0
- aiecs/llm/clients/base_client.py +324 -0
- aiecs/llm/clients/google_function_calling_mixin.py +457 -0
- aiecs/llm/clients/googleai_client.py +241 -0
- aiecs/llm/clients/openai_client.py +158 -0
- aiecs/llm/clients/openai_compatible_mixin.py +367 -0
- aiecs/llm/clients/vertex_client.py +897 -0
- aiecs/llm/clients/xai_client.py +201 -0
- aiecs/llm/config/__init__.py +51 -0
- aiecs/llm/config/config_loader.py +272 -0
- aiecs/llm/config/config_validator.py +206 -0
- aiecs/llm/config/model_config.py +143 -0
- aiecs/llm/protocols.py +149 -0
- aiecs/llm/utils/__init__.py +10 -0
- aiecs/llm/utils/validate_config.py +89 -0
- aiecs/main.py +140 -121
- aiecs/scripts/aid/VERSION_MANAGEMENT.md +138 -0
- aiecs/scripts/aid/__init__.py +19 -0
- aiecs/scripts/aid/module_checker.py +499 -0
- aiecs/scripts/aid/version_manager.py +235 -0
- aiecs/scripts/{DEPENDENCY_SYSTEM_SUMMARY.md → dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md} +1 -0
- aiecs/scripts/{README_DEPENDENCY_CHECKER.md → dependance_check/README_DEPENDENCY_CHECKER.md} +1 -0
- aiecs/scripts/dependance_check/__init__.py +15 -0
- aiecs/scripts/dependance_check/dependency_checker.py +1835 -0
- aiecs/scripts/{dependency_fixer.py → dependance_check/dependency_fixer.py} +192 -90
- aiecs/scripts/{download_nlp_data.py → dependance_check/download_nlp_data.py} +203 -71
- aiecs/scripts/dependance_patch/__init__.py +7 -0
- aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
- aiecs/scripts/{fix_weasel_validator.py → dependance_patch/fix_weasel/fix_weasel_validator.py} +21 -14
- aiecs/scripts/{patch_weasel_library.sh → dependance_patch/fix_weasel/patch_weasel_library.sh} +1 -1
- aiecs/scripts/knowledge_graph/__init__.py +3 -0
- aiecs/scripts/knowledge_graph/run_threshold_experiments.py +212 -0
- aiecs/scripts/migrations/multi_tenancy/README.md +142 -0
- aiecs/scripts/tools_develop/README.md +671 -0
- aiecs/scripts/tools_develop/README_CONFIG_CHECKER.md +273 -0
- aiecs/scripts/tools_develop/TOOLS_CONFIG_GUIDE.md +1287 -0
- aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
- aiecs/scripts/tools_develop/__init__.py +21 -0
- aiecs/scripts/tools_develop/check_all_tools_config.py +548 -0
- aiecs/scripts/tools_develop/check_type_annotations.py +257 -0
- aiecs/scripts/tools_develop/pre-commit-schema-coverage.sh +66 -0
- aiecs/scripts/tools_develop/schema_coverage.py +511 -0
- aiecs/scripts/tools_develop/validate_tool_schemas.py +475 -0
- aiecs/scripts/tools_develop/verify_executor_config_fix.py +98 -0
- aiecs/scripts/tools_develop/verify_tools.py +352 -0
- aiecs/tasks/__init__.py +0 -1
- aiecs/tasks/worker.py +115 -47
- aiecs/tools/__init__.py +194 -72
- aiecs/tools/apisource/__init__.py +99 -0
- aiecs/tools/apisource/intelligence/__init__.py +19 -0
- aiecs/tools/apisource/intelligence/data_fusion.py +632 -0
- aiecs/tools/apisource/intelligence/query_analyzer.py +417 -0
- aiecs/tools/apisource/intelligence/search_enhancer.py +385 -0
- aiecs/tools/apisource/monitoring/__init__.py +9 -0
- aiecs/tools/apisource/monitoring/metrics.py +330 -0
- aiecs/tools/apisource/providers/__init__.py +112 -0
- aiecs/tools/apisource/providers/base.py +671 -0
- aiecs/tools/apisource/providers/census.py +397 -0
- aiecs/tools/apisource/providers/fred.py +535 -0
- aiecs/tools/apisource/providers/newsapi.py +409 -0
- aiecs/tools/apisource/providers/worldbank.py +352 -0
- aiecs/tools/apisource/reliability/__init__.py +12 -0
- aiecs/tools/apisource/reliability/error_handler.py +363 -0
- aiecs/tools/apisource/reliability/fallback_strategy.py +376 -0
- aiecs/tools/apisource/tool.py +832 -0
- aiecs/tools/apisource/utils/__init__.py +9 -0
- aiecs/tools/apisource/utils/validators.py +334 -0
- aiecs/tools/base_tool.py +415 -21
- aiecs/tools/docs/__init__.py +121 -0
- aiecs/tools/docs/ai_document_orchestrator.py +607 -0
- aiecs/tools/docs/ai_document_writer_orchestrator.py +2350 -0
- aiecs/tools/docs/content_insertion_tool.py +1320 -0
- aiecs/tools/docs/document_creator_tool.py +1323 -0
- aiecs/tools/docs/document_layout_tool.py +1160 -0
- aiecs/tools/docs/document_parser_tool.py +1011 -0
- aiecs/tools/docs/document_writer_tool.py +1829 -0
- aiecs/tools/knowledge_graph/__init__.py +17 -0
- aiecs/tools/knowledge_graph/graph_reasoning_tool.py +807 -0
- aiecs/tools/knowledge_graph/graph_search_tool.py +944 -0
- aiecs/tools/knowledge_graph/kg_builder_tool.py +524 -0
- aiecs/tools/langchain_adapter.py +300 -138
- aiecs/tools/schema_generator.py +455 -0
- aiecs/tools/search_tool/__init__.py +100 -0
- aiecs/tools/search_tool/analyzers.py +581 -0
- aiecs/tools/search_tool/cache.py +264 -0
- aiecs/tools/search_tool/constants.py +128 -0
- aiecs/tools/search_tool/context.py +224 -0
- aiecs/tools/search_tool/core.py +778 -0
- aiecs/tools/search_tool/deduplicator.py +119 -0
- aiecs/tools/search_tool/error_handler.py +242 -0
- aiecs/tools/search_tool/metrics.py +343 -0
- aiecs/tools/search_tool/rate_limiter.py +172 -0
- aiecs/tools/search_tool/schemas.py +275 -0
- aiecs/tools/statistics/__init__.py +80 -0
- aiecs/tools/statistics/ai_data_analysis_orchestrator.py +646 -0
- aiecs/tools/statistics/ai_insight_generator_tool.py +508 -0
- aiecs/tools/statistics/ai_report_orchestrator_tool.py +684 -0
- aiecs/tools/statistics/data_loader_tool.py +555 -0
- aiecs/tools/statistics/data_profiler_tool.py +638 -0
- aiecs/tools/statistics/data_transformer_tool.py +580 -0
- aiecs/tools/statistics/data_visualizer_tool.py +498 -0
- aiecs/tools/statistics/model_trainer_tool.py +507 -0
- aiecs/tools/statistics/statistical_analyzer_tool.py +472 -0
- aiecs/tools/task_tools/__init__.py +49 -36
- aiecs/tools/task_tools/chart_tool.py +200 -184
- aiecs/tools/task_tools/classfire_tool.py +268 -267
- aiecs/tools/task_tools/image_tool.py +175 -131
- aiecs/tools/task_tools/office_tool.py +226 -146
- aiecs/tools/task_tools/pandas_tool.py +477 -121
- aiecs/tools/task_tools/report_tool.py +390 -142
- aiecs/tools/task_tools/research_tool.py +149 -79
- aiecs/tools/task_tools/scraper_tool.py +339 -145
- aiecs/tools/task_tools/stats_tool.py +448 -209
- aiecs/tools/temp_file_manager.py +26 -24
- aiecs/tools/tool_executor/__init__.py +18 -16
- aiecs/tools/tool_executor/tool_executor.py +364 -52
- aiecs/utils/LLM_output_structor.py +74 -48
- aiecs/utils/__init__.py +14 -3
- aiecs/utils/base_callback.py +0 -3
- aiecs/utils/cache_provider.py +696 -0
- aiecs/utils/execution_utils.py +50 -31
- aiecs/utils/prompt_loader.py +1 -0
- aiecs/utils/token_usage_repository.py +37 -11
- aiecs/ws/socket_server.py +14 -4
- {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/METADATA +52 -15
- aiecs-1.7.6.dist-info/RECORD +337 -0
- aiecs-1.7.6.dist-info/entry_points.txt +13 -0
- aiecs/config/registry.py +0 -19
- aiecs/domain/context/content_engine.py +0 -982
- aiecs/llm/base_client.py +0 -99
- aiecs/llm/openai_client.py +0 -125
- aiecs/llm/vertex_client.py +0 -186
- aiecs/llm/xai_client.py +0 -184
- aiecs/scripts/dependency_checker.py +0 -857
- aiecs/scripts/quick_dependency_check.py +0 -269
- aiecs/tools/task_tools/search_api.py +0 -7
- aiecs-1.0.1.dist-info/RECORD +0 -90
- aiecs-1.0.1.dist-info/entry_points.txt +0 -7
- /aiecs/scripts/{setup_nlp_data.sh → dependance_check/setup_nlp_data.sh} +0 -0
- /aiecs/scripts/{README_WEASEL_PATCH.md → dependance_patch/fix_weasel/README_WEASEL_PATCH.md} +0 -0
- /aiecs/scripts/{fix_weasel_validator.sh → dependance_patch/fix_weasel/fix_weasel_validator.sh} +0 -0
- /aiecs/scripts/{run_weasel_patch.sh → dependance_patch/fix_weasel/run_weasel_patch.sh} +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/WHEEL +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/licenses/LICENSE +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
AIECS Module Import Checker
|
|
4
|
+
|
|
5
|
+
Tests all module imports in the aiecs package to ensure there are no import errors
|
|
6
|
+
that would occur when installing from PyPI. This simulates the installation process
|
|
7
|
+
and catches "cannot find module" errors before publishing.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
python -m aiecs.scripts.aid.module_checker
|
|
11
|
+
aiecs-check-modules [--verbose] [--module MODULE]
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import ast
|
|
16
|
+
import importlib
|
|
17
|
+
import importlib.util
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Dict, List, Set, Tuple, Optional
|
|
21
|
+
import traceback
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ModuleImportChecker:
|
|
25
|
+
"""Checks all module imports to catch errors before publishing"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, project_root: Optional[Path] = None, verbose: bool = False):
|
|
28
|
+
"""Initialize the module checker"""
|
|
29
|
+
if project_root is None:
|
|
30
|
+
# Find project root by looking for pyproject.toml
|
|
31
|
+
current = Path(__file__).parent
|
|
32
|
+
while current != current.parent:
|
|
33
|
+
if (current / "pyproject.toml").exists():
|
|
34
|
+
project_root = current
|
|
35
|
+
break
|
|
36
|
+
current = current.parent
|
|
37
|
+
|
|
38
|
+
if project_root is None:
|
|
39
|
+
raise RuntimeError("Could not find project root (pyproject.toml)")
|
|
40
|
+
|
|
41
|
+
self.project_root = project_root
|
|
42
|
+
self.aiecs_root = project_root / "aiecs"
|
|
43
|
+
self.verbose = verbose
|
|
44
|
+
|
|
45
|
+
# Add project root to sys.path so we can import aiecs modules
|
|
46
|
+
project_root_str = str(self.project_root)
|
|
47
|
+
if project_root_str not in sys.path:
|
|
48
|
+
sys.path.insert(0, project_root_str)
|
|
49
|
+
|
|
50
|
+
# Results storage
|
|
51
|
+
self.import_errors: List[Tuple[str, str, Exception]] = [] # (module, import_name, error)
|
|
52
|
+
self.failed_modules: List[Tuple[str, Exception]] = [] # (module, error)
|
|
53
|
+
self.successful_modules: Set[str] = set()
|
|
54
|
+
|
|
55
|
+
# Track which modules we've tried to import
|
|
56
|
+
self.imported_modules: Set[str] = set()
|
|
57
|
+
self.module_files: Dict[str, Path] = {}
|
|
58
|
+
|
|
59
|
+
def find_all_modules(self) -> List[str]:
|
|
60
|
+
"""Find all Python modules in the aiecs package"""
|
|
61
|
+
modules = []
|
|
62
|
+
|
|
63
|
+
if not self.aiecs_root.exists():
|
|
64
|
+
return modules
|
|
65
|
+
|
|
66
|
+
for py_file in self.aiecs_root.rglob("*.py"):
|
|
67
|
+
# Skip __pycache__ and test files (test_*.py or *_test.py patterns)
|
|
68
|
+
filename = py_file.name.lower()
|
|
69
|
+
if "__pycache__" in str(py_file) or filename.startswith("test_") or filename.endswith("_test.py"):
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
# Convert file path to module name
|
|
73
|
+
relative_path = py_file.relative_to(self.project_root)
|
|
74
|
+
module_parts = relative_path.parts[:-1] + (relative_path.stem,)
|
|
75
|
+
module_name = ".".join(module_parts)
|
|
76
|
+
|
|
77
|
+
modules.append(module_name)
|
|
78
|
+
self.module_files[module_name] = py_file
|
|
79
|
+
|
|
80
|
+
return sorted(modules)
|
|
81
|
+
|
|
82
|
+
def resolve_relative_import(self, module_name: Optional[str], level: int, file_path: Path) -> Optional[str]:
|
|
83
|
+
"""
|
|
84
|
+
Resolve a relative import to an absolute module name.
|
|
85
|
+
Returns None if it's not a relative import or not an aiecs module.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
module_name: The module name from AST (without leading dots)
|
|
89
|
+
level: The number of dots (0 = absolute, 1 = ., 2 = .., etc.)
|
|
90
|
+
file_path: Path to the file containing the import
|
|
91
|
+
"""
|
|
92
|
+
if level == 0:
|
|
93
|
+
# Absolute import
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
# Get the module name for the current file
|
|
97
|
+
relative_path = file_path.relative_to(self.project_root)
|
|
98
|
+
current_module_parts = relative_path.parts[:-1] + (relative_path.stem,)
|
|
99
|
+
current_module = ".".join(current_module_parts)
|
|
100
|
+
|
|
101
|
+
if not self.is_aiecs_module(current_module):
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
# Resolve relative import based on level
|
|
105
|
+
if level == 1:
|
|
106
|
+
# from .module import something or from . import something
|
|
107
|
+
if module_name:
|
|
108
|
+
# from .module import
|
|
109
|
+
if '.' in current_module:
|
|
110
|
+
parent = current_module.rsplit('.', 1)[0]
|
|
111
|
+
return f"{parent}.{module_name}"
|
|
112
|
+
else:
|
|
113
|
+
return module_name
|
|
114
|
+
else:
|
|
115
|
+
# from . import something
|
|
116
|
+
return current_module.rsplit('.', 1)[0] if '.' in current_module else None
|
|
117
|
+
elif level == 2:
|
|
118
|
+
# from ..module import something
|
|
119
|
+
if '.' in current_module:
|
|
120
|
+
parent = '.'.join(current_module.split('.')[:-2])
|
|
121
|
+
if module_name:
|
|
122
|
+
return f"{parent}.{module_name}"
|
|
123
|
+
else:
|
|
124
|
+
return parent
|
|
125
|
+
elif level > 2:
|
|
126
|
+
# from ...module or deeper
|
|
127
|
+
if '.' in current_module:
|
|
128
|
+
parts = current_module.split('.')
|
|
129
|
+
if len(parts) >= level:
|
|
130
|
+
parent = '.'.join(parts[:-(level-1)])
|
|
131
|
+
if module_name:
|
|
132
|
+
return f"{parent}.{module_name}"
|
|
133
|
+
else:
|
|
134
|
+
return parent
|
|
135
|
+
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
|
+
def extract_imports_from_file(self, file_path: Path) -> List[Tuple[str, Optional[str]]]:
|
|
139
|
+
"""
|
|
140
|
+
Extract all imports from a Python file.
|
|
141
|
+
Returns list of (module_name, imported_item) tuples.
|
|
142
|
+
imported_item is None for 'import module' statements.
|
|
143
|
+
"""
|
|
144
|
+
imports = []
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
content = file_path.read_text(encoding="utf-8")
|
|
148
|
+
tree = ast.parse(content, filename=str(file_path))
|
|
149
|
+
|
|
150
|
+
for node in ast.walk(tree):
|
|
151
|
+
if isinstance(node, ast.Import):
|
|
152
|
+
for alias in node.names:
|
|
153
|
+
imports.append((alias.name, None))
|
|
154
|
+
elif isinstance(node, ast.ImportFrom):
|
|
155
|
+
# Check if it's a relative import (level > 0)
|
|
156
|
+
if node.level > 0:
|
|
157
|
+
# Relative import - resolve it
|
|
158
|
+
resolved_module = self.resolve_relative_import(node.module, node.level, file_path)
|
|
159
|
+
if resolved_module:
|
|
160
|
+
# Use resolved absolute module name
|
|
161
|
+
for alias in node.names:
|
|
162
|
+
imports.append((resolved_module, alias.name))
|
|
163
|
+
# If resolution failed, skip it (might be external or invalid)
|
|
164
|
+
elif node.module:
|
|
165
|
+
# Absolute import
|
|
166
|
+
for alias in node.names:
|
|
167
|
+
imports.append((node.module, alias.name))
|
|
168
|
+
except SyntaxError as e:
|
|
169
|
+
self.failed_modules.append((str(file_path), e))
|
|
170
|
+
except Exception as e:
|
|
171
|
+
if self.verbose:
|
|
172
|
+
print(f"Warning: Failed to parse {file_path}: {e}")
|
|
173
|
+
|
|
174
|
+
return imports
|
|
175
|
+
|
|
176
|
+
def is_aiecs_module(self, module_name: str) -> bool:
|
|
177
|
+
"""Check if a module name is part of the aiecs package"""
|
|
178
|
+
return module_name.startswith("aiecs")
|
|
179
|
+
|
|
180
|
+
def is_internal_import_error(self, error: Exception) -> bool:
|
|
181
|
+
"""Check if an import error is due to missing internal aiecs module"""
|
|
182
|
+
error_msg = str(error)
|
|
183
|
+
error_name = type(error).__name__
|
|
184
|
+
|
|
185
|
+
# ModuleNotFoundError or ImportError with "aiecs" in the message
|
|
186
|
+
# indicates an internal import problem
|
|
187
|
+
if error_name in ("ModuleNotFoundError", "ImportError"):
|
|
188
|
+
# Check if the error mentions an aiecs module
|
|
189
|
+
if "aiecs" in error_msg.lower():
|
|
190
|
+
return True
|
|
191
|
+
|
|
192
|
+
# Check if it's a "No module named" error for an aiecs module
|
|
193
|
+
if "no module named" in error_msg.lower():
|
|
194
|
+
# Extract the module name from error message
|
|
195
|
+
# Format: "No module named 'aiecs.xxx'"
|
|
196
|
+
import re
|
|
197
|
+
match = re.search(r"['\"]([^'\"]+)['\"]", error_msg)
|
|
198
|
+
if match:
|
|
199
|
+
module_in_error = match.group(1)
|
|
200
|
+
if self.is_aiecs_module(module_in_error):
|
|
201
|
+
return True
|
|
202
|
+
|
|
203
|
+
return False
|
|
204
|
+
|
|
205
|
+
def check_module_import(self, module_name: str) -> bool:
|
|
206
|
+
"""
|
|
207
|
+
Try to import a module and catch any import errors.
|
|
208
|
+
Returns True if import succeeds, False otherwise.
|
|
209
|
+
"""
|
|
210
|
+
if module_name in self.imported_modules:
|
|
211
|
+
return module_name in self.successful_modules
|
|
212
|
+
|
|
213
|
+
self.imported_modules.add(module_name)
|
|
214
|
+
|
|
215
|
+
# Skip if not an aiecs module
|
|
216
|
+
if not self.is_aiecs_module(module_name):
|
|
217
|
+
return True
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
# Try to import the module
|
|
221
|
+
importlib.import_module(module_name)
|
|
222
|
+
self.successful_modules.add(module_name)
|
|
223
|
+
if self.verbose:
|
|
224
|
+
print(f" ✓ {module_name}")
|
|
225
|
+
return True
|
|
226
|
+
except (ImportError, ModuleNotFoundError) as e:
|
|
227
|
+
# Check if it's an internal aiecs import error
|
|
228
|
+
if self.is_internal_import_error(e):
|
|
229
|
+
# This is a problem - internal import error
|
|
230
|
+
self.failed_modules.append((module_name, e))
|
|
231
|
+
if self.verbose:
|
|
232
|
+
print(f" ✗ {module_name}: {e}")
|
|
233
|
+
return False
|
|
234
|
+
else:
|
|
235
|
+
# External dependency missing - note it but don't fail
|
|
236
|
+
# (dependencies should be in pyproject.toml and will be installed from PyPI)
|
|
237
|
+
if self.verbose:
|
|
238
|
+
print(f" ⚠ {module_name}: External dependency missing (OK if in pyproject.toml): {e}")
|
|
239
|
+
# Don't mark as failed - external deps will be installed when package is installed
|
|
240
|
+
return True
|
|
241
|
+
except SyntaxError as e:
|
|
242
|
+
# Syntax errors are always problems
|
|
243
|
+
self.failed_modules.append((module_name, e))
|
|
244
|
+
if self.verbose:
|
|
245
|
+
print(f" ✗ {module_name}: Syntax error: {e}")
|
|
246
|
+
return False
|
|
247
|
+
except Exception as e:
|
|
248
|
+
# Other errors might be runtime issues, but we should note them
|
|
249
|
+
# Check if it's related to imports
|
|
250
|
+
if "import" in str(e).lower() or "module" in str(e).lower():
|
|
251
|
+
self.failed_modules.append((module_name, e))
|
|
252
|
+
if self.verbose:
|
|
253
|
+
print(f" ✗ {module_name}: {e}")
|
|
254
|
+
return False
|
|
255
|
+
else:
|
|
256
|
+
# Runtime error not related to imports - might be OK
|
|
257
|
+
if self.verbose:
|
|
258
|
+
print(f" ⚠ {module_name}: Runtime error (may be OK): {e}")
|
|
259
|
+
return True
|
|
260
|
+
|
|
261
|
+
def check_all_imports_in_module(self, module_name: str) -> bool:
|
|
262
|
+
"""
|
|
263
|
+
Check all imports within a module file to ensure they can resolve.
|
|
264
|
+
This catches cases where a module imports something that doesn't exist.
|
|
265
|
+
"""
|
|
266
|
+
if module_name not in self.module_files:
|
|
267
|
+
return True
|
|
268
|
+
|
|
269
|
+
# Skip if module failed to import (already reported)
|
|
270
|
+
if module_name not in self.successful_modules:
|
|
271
|
+
return True
|
|
272
|
+
|
|
273
|
+
file_path = self.module_files[module_name]
|
|
274
|
+
imports = self.extract_imports_from_file(file_path)
|
|
275
|
+
|
|
276
|
+
all_ok = True
|
|
277
|
+
|
|
278
|
+
for import_module, import_item in imports:
|
|
279
|
+
# Only check aiecs internal imports
|
|
280
|
+
if not self.is_aiecs_module(import_module):
|
|
281
|
+
continue
|
|
282
|
+
|
|
283
|
+
# Check if the module can be imported
|
|
284
|
+
try:
|
|
285
|
+
mod = importlib.import_module(import_module)
|
|
286
|
+
|
|
287
|
+
# If there's a specific item being imported, check if it exists
|
|
288
|
+
if import_item:
|
|
289
|
+
if not hasattr(mod, import_item):
|
|
290
|
+
self.import_errors.append((
|
|
291
|
+
module_name,
|
|
292
|
+
f"{import_module}.{import_item}",
|
|
293
|
+
AttributeError(f"{import_module} has no attribute '{import_item}'")
|
|
294
|
+
))
|
|
295
|
+
all_ok = False
|
|
296
|
+
except (ImportError, ModuleNotFoundError) as e:
|
|
297
|
+
# Check if it's an internal error
|
|
298
|
+
if self.is_internal_import_error(e):
|
|
299
|
+
self.import_errors.append((
|
|
300
|
+
module_name,
|
|
301
|
+
import_module,
|
|
302
|
+
e
|
|
303
|
+
))
|
|
304
|
+
all_ok = False
|
|
305
|
+
# External dependency errors are OK (will be installed from PyPI)
|
|
306
|
+
except Exception as e:
|
|
307
|
+
# Other errors - might be runtime, but note them
|
|
308
|
+
if self.verbose:
|
|
309
|
+
self.import_errors.append((
|
|
310
|
+
module_name,
|
|
311
|
+
import_module,
|
|
312
|
+
e
|
|
313
|
+
))
|
|
314
|
+
|
|
315
|
+
return all_ok
|
|
316
|
+
|
|
317
|
+
def run_checks(self, module_filter: Optional[str] = None) -> bool:
|
|
318
|
+
"""Run all import checks"""
|
|
319
|
+
print("=" * 80)
|
|
320
|
+
print("AIECS Module Import Checker")
|
|
321
|
+
print("=" * 80)
|
|
322
|
+
print()
|
|
323
|
+
print("This script tests all imports to catch errors before publishing to PyPI.")
|
|
324
|
+
print()
|
|
325
|
+
|
|
326
|
+
# Step 1: Find all modules
|
|
327
|
+
print("Step 1: Discovering all modules...")
|
|
328
|
+
all_modules = self.find_all_modules()
|
|
329
|
+
|
|
330
|
+
if module_filter:
|
|
331
|
+
all_modules = [m for m in all_modules if module_filter in m]
|
|
332
|
+
print(f" Filtered to modules containing '{module_filter}'")
|
|
333
|
+
|
|
334
|
+
print(f" Found {len(all_modules)} modules to check")
|
|
335
|
+
print()
|
|
336
|
+
|
|
337
|
+
# Step 2: Try importing each module
|
|
338
|
+
print("Step 2: Testing module imports...")
|
|
339
|
+
print(" (This simulates what happens when installing from PyPI)")
|
|
340
|
+
print()
|
|
341
|
+
|
|
342
|
+
for module_name in all_modules:
|
|
343
|
+
if self.verbose:
|
|
344
|
+
print(f" Checking {module_name}...")
|
|
345
|
+
self.check_module_import(module_name)
|
|
346
|
+
|
|
347
|
+
print(f" ✓ Successfully imported: {len(self.successful_modules)} modules")
|
|
348
|
+
if self.failed_modules:
|
|
349
|
+
print(f" ✗ Failed to import: {len(self.failed_modules)} modules")
|
|
350
|
+
print()
|
|
351
|
+
|
|
352
|
+
# Step 3: Check imports within each module
|
|
353
|
+
print("Step 3: Checking imports within modules...")
|
|
354
|
+
print(" (Verifying that all internal imports resolve correctly)")
|
|
355
|
+
print()
|
|
356
|
+
|
|
357
|
+
for module_name in all_modules:
|
|
358
|
+
if module_name in self.successful_modules:
|
|
359
|
+
self.check_all_imports_in_module(module_name)
|
|
360
|
+
|
|
361
|
+
if self.import_errors:
|
|
362
|
+
print(f" ✗ Found {len(self.import_errors)} import errors")
|
|
363
|
+
else:
|
|
364
|
+
print(f" ✓ All imports resolve correctly")
|
|
365
|
+
print()
|
|
366
|
+
|
|
367
|
+
# Step 4: Try importing the main package
|
|
368
|
+
print("Step 4: Testing main package import...")
|
|
369
|
+
try:
|
|
370
|
+
import aiecs
|
|
371
|
+
print(" ✓ Main package 'aiecs' imports successfully")
|
|
372
|
+
|
|
373
|
+
# Check main exports
|
|
374
|
+
if hasattr(aiecs, "__all__"):
|
|
375
|
+
all_items = aiecs.__all__
|
|
376
|
+
missing_items = []
|
|
377
|
+
for item in all_items:
|
|
378
|
+
if not hasattr(aiecs, item):
|
|
379
|
+
missing_items.append(item)
|
|
380
|
+
|
|
381
|
+
if missing_items:
|
|
382
|
+
print(f" ⚠ Warning: __all__ declares items not available: {missing_items}")
|
|
383
|
+
else:
|
|
384
|
+
print(f" ✓ All {len(all_items)} items in __all__ are available")
|
|
385
|
+
except Exception as e:
|
|
386
|
+
print(f" ✗ Failed to import main package: {e}")
|
|
387
|
+
self.failed_modules.append(("aiecs", e))
|
|
388
|
+
print()
|
|
389
|
+
|
|
390
|
+
return len(self.failed_modules) == 0 and len(self.import_errors) == 0
|
|
391
|
+
|
|
392
|
+
def print_report(self):
|
|
393
|
+
"""Print a comprehensive report"""
|
|
394
|
+
print("=" * 80)
|
|
395
|
+
print("IMPORT CHECK REPORT")
|
|
396
|
+
print("=" * 80)
|
|
397
|
+
print()
|
|
398
|
+
|
|
399
|
+
# Failed module imports
|
|
400
|
+
if self.failed_modules:
|
|
401
|
+
print(f"❌ FAILED MODULE IMPORTS ({len(self.failed_modules)}):")
|
|
402
|
+
print("-" * 80)
|
|
403
|
+
for module_name, error in self.failed_modules:
|
|
404
|
+
print(f" Module: {module_name}")
|
|
405
|
+
print(f" Error: {error}")
|
|
406
|
+
print(f" Type: {type(error).__name__}")
|
|
407
|
+
if self.verbose:
|
|
408
|
+
print(f" Traceback:")
|
|
409
|
+
traceback.print_exception(type(error), error, error.__traceback__)
|
|
410
|
+
print()
|
|
411
|
+
|
|
412
|
+
# Import errors within modules
|
|
413
|
+
if self.import_errors:
|
|
414
|
+
print(f"❌ IMPORT ERRORS WITHIN MODULES ({len(self.import_errors)}):")
|
|
415
|
+
print("-" * 80)
|
|
416
|
+
for module_name, import_name, error in self.import_errors:
|
|
417
|
+
print(f" In module: {module_name}")
|
|
418
|
+
print(f" Cannot import: {import_name}")
|
|
419
|
+
print(f" Error: {error}")
|
|
420
|
+
print()
|
|
421
|
+
|
|
422
|
+
# Summary
|
|
423
|
+
print("=" * 80)
|
|
424
|
+
print("SUMMARY")
|
|
425
|
+
print("=" * 80)
|
|
426
|
+
print(f"Total modules found: {len(self.module_files)}")
|
|
427
|
+
print(f"Successfully imported: {len(self.successful_modules)}")
|
|
428
|
+
print(f"Failed to import: {len(self.failed_modules)}")
|
|
429
|
+
print(f"Import errors within modules: {len(self.import_errors)}")
|
|
430
|
+
print()
|
|
431
|
+
|
|
432
|
+
if len(self.failed_modules) == 0 and len(self.import_errors) == 0:
|
|
433
|
+
print("✅ All imports successful! Package is ready for publishing.")
|
|
434
|
+
print()
|
|
435
|
+
print("Next steps:")
|
|
436
|
+
print(" 1. Build the package: python -m build")
|
|
437
|
+
print(" 2. Test installation: pip install dist/aiecs-*.whl")
|
|
438
|
+
print(" 3. Upload to PyPI: python -m twine upload dist/*")
|
|
439
|
+
return True
|
|
440
|
+
else:
|
|
441
|
+
print("❌ Import errors found. Please fix them before publishing.")
|
|
442
|
+
print()
|
|
443
|
+
print("Common issues:")
|
|
444
|
+
print(" - Missing __init__.py files")
|
|
445
|
+
print(" - Incorrect import paths")
|
|
446
|
+
print(" - Circular import dependencies")
|
|
447
|
+
print(" - Typos in module or attribute names")
|
|
448
|
+
return False
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def main():
|
|
452
|
+
"""Main entry point"""
|
|
453
|
+
parser = argparse.ArgumentParser(
|
|
454
|
+
prog="aiecs-check-modules",
|
|
455
|
+
description="AIECS Module Import Checker - Tests all imports before publishing to PyPI",
|
|
456
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
457
|
+
epilog="""
|
|
458
|
+
Examples:
|
|
459
|
+
aiecs-check-modules # Check all modules
|
|
460
|
+
aiecs-check-modules --verbose # Verbose output with details
|
|
461
|
+
aiecs-check-modules --module llm # Check only llm.* modules
|
|
462
|
+
|
|
463
|
+
This script simulates what happens when someone installs your package from PyPI.
|
|
464
|
+
It will catch import errors before you publish, saving you from broken releases.
|
|
465
|
+
""",
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
parser.add_argument(
|
|
469
|
+
"--verbose", "-v",
|
|
470
|
+
action="store_true",
|
|
471
|
+
help="Show verbose output including successful imports"
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
parser.add_argument(
|
|
475
|
+
"--module", "-m",
|
|
476
|
+
type=str,
|
|
477
|
+
help="Check only modules matching this filter (e.g., 'llm' to check llm.* modules)"
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
args = parser.parse_args()
|
|
481
|
+
|
|
482
|
+
try:
|
|
483
|
+
checker = ModuleImportChecker(verbose=args.verbose)
|
|
484
|
+
success = checker.run_checks(module_filter=args.module)
|
|
485
|
+
report_success = checker.print_report()
|
|
486
|
+
|
|
487
|
+
sys.exit(0 if (success and report_success) else 1)
|
|
488
|
+
except KeyboardInterrupt:
|
|
489
|
+
print("\n\nInterrupted by user", file=sys.stderr)
|
|
490
|
+
sys.exit(130)
|
|
491
|
+
except Exception as e:
|
|
492
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
493
|
+
if args.verbose:
|
|
494
|
+
traceback.print_exc()
|
|
495
|
+
sys.exit(1)
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
if __name__ == "__main__":
|
|
499
|
+
main()
|