code2llm 0.5.117__tar.gz → 0.5.119__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {code2llm-0.5.117 → code2llm-0.5.119}/PKG-INFO +2 -2
- {code2llm-0.5.117 → code2llm-0.5.119}/README.md +1 -1
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/__init__.py +1 -1
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/call_graph.py +2 -7
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/cfg.py +2 -7
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/data_analysis.py +87 -3
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/utils/__init__.py +2 -2
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/utils/ast_helpers.py +13 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/file_cache.py +7 -3
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/streaming/cache.py +3 -4
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/article_view.py +3 -7
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/context_view.py +3 -7
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/flow_constants.py +13 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/flow_exporter.py +2 -10
- code2llm-0.5.119/code2llm/exporters/index_generator/__init__.py +73 -0
- code2llm-0.5.117/code2llm/exporters/index_generator.py → code2llm-0.5.119/code2llm/exporters/index_generator/renderer.py +168 -321
- code2llm-0.5.119/code2llm/exporters/index_generator/scanner.py +116 -0
- code2llm-0.5.119/code2llm/exporters/index_generator.py +29 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/map_exporter.py +2 -9
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/toon_view.py +3 -7
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/generators/__init__.py +3 -0
- code2llm-0.5.119/code2llm/generators/_utils.py +15 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/generators/llm_flow.py +2 -9
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/generators/llm_task.py +2 -9
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/nlp/__init__.py +1 -1
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm.egg-info/PKG-INFO +2 -2
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm.egg-info/SOURCES.txt +4 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/pyproject.toml +1 -1
- {code2llm-0.5.117 → code2llm-0.5.119}/setup.py +1 -1
- {code2llm-0.5.117 → code2llm-0.5.119}/LICENSE +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/__main__.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/__init__.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/coupling.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/dfg.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/pipeline_detector.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/side_effects.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/smells.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/type_inference.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/api.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli_analysis.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli_commands.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli_exports/__init__.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli_exports/code2logic.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli_exports/formats.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli_exports/orchestrator.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli_exports/prompt.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli_parser.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/__init__.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/analyzer.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/ast_registry.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/config.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/export_pipeline.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/file_analyzer.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/file_filter.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/gitignore.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/incremental.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/__init__.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/base.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/cpp.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/csharp.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/generic.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/go_lang.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/java.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/php.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/ruby.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/rust.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/ts_extractors.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/ts_parser.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/typescript.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/large_repo.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/models.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/persistent_cache.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/refactoring.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/repo_files.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/streaming/__init__.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/streaming/incremental.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/streaming/prioritizer.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/streaming/scanner.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/streaming/strategies.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/streaming_analyzer.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/toon_size_manager.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/__init__.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/base.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/context_exporter.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/evolution_exporter.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/flow_renderer.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/html_dashboard.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/json_exporter.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/llm_exporter.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/mermaid_exporter.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/project_yaml/__init__.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/project_yaml/constants.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/project_yaml/core.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/project_yaml/evolution.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/project_yaml/health.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/project_yaml/hotspots.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/project_yaml/modules.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/project_yaml_exporter.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/readme_exporter.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/report_generators.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/toon/__init__.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/toon/helpers.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/toon/metrics.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/toon/module_detail.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/toon/renderer.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/toon.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/validate_project.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/yaml_exporter.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/generators/mermaid.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/nlp/config.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/nlp/entity_resolution.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/nlp/intent_matching.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/nlp/normalization.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/nlp/pipeline.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/patterns/__init__.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/patterns/detector.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/refactor/__init__.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/refactor/prompt_engine.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm.egg-info/dependency_links.txt +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm.egg-info/entry_points.txt +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm.egg-info/requires.txt +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/code2llm.egg-info/top_level.txt +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/setup.cfg +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_advanced_analysis.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_analyzer.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_calls_toon_export.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_deep_analysis.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_edge_cases.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_flow_exporter.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_format_quality.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_multilanguage_e2e.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_nlp_pipeline.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_nonpython_cc_calls.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_persistent_cache.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_pipeline_detector.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_project_toon_export.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_prompt_engine.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_prompt_txt.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_refactoring_engine.py +0 -0
- {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_toon_v2.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code2llm
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.119
|
|
4
4
|
Summary: High-performance Python code flow analysis with optimized TOON format - CFG, DFG, call graphs, and intelligent code queries
|
|
5
5
|
Home-page: https://github.com/wronai/stts
|
|
6
6
|
Author: STTS Project
|
|
@@ -67,7 +67,7 @@ Dynamic: requires-python
|
|
|
67
67
|
|
|
68
68
|
## AI Cost Tracking
|
|
69
69
|
|
|
70
|
-
    
|
|
71
71
|
  
|
|
72
72
|
|
|
73
73
|
- 🤖 **LLM usage:** $7.5000 (166 commits)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
## AI Cost Tracking
|
|
5
5
|
|
|
6
|
-
    
|
|
7
7
|
  
|
|
8
8
|
|
|
9
9
|
- 🤖 **LLM usage:** $7.5000 (166 commits)
|
|
@@ -8,7 +8,7 @@ Includes NLP Processing Pipeline for query normalization, intent matching,
|
|
|
8
8
|
and entity resolution with multilingual support.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
__version__ = "0.5.
|
|
11
|
+
__version__ = "0.5.119"
|
|
12
12
|
__author__ = "STTS Project"
|
|
13
13
|
|
|
14
14
|
# Core analysis components (lightweight, always needed)
|
|
@@ -5,7 +5,7 @@ from typing import Optional, Set, List, Dict
|
|
|
5
5
|
|
|
6
6
|
from code2llm.core.config import Config
|
|
7
7
|
from code2llm.core.models import AnalysisResult, FlowEdge
|
|
8
|
-
from code2llm.analysis.utils import ast_unparse
|
|
8
|
+
from code2llm.analysis.utils import ast_unparse, qualified_name
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class CallGraphExtractor(ast.NodeVisitor):
|
|
@@ -140,12 +140,7 @@ class CallGraphExtractor(ast.NodeVisitor):
|
|
|
140
140
|
self.generic_visit(node)
|
|
141
141
|
|
|
142
142
|
def _qualified_name(self, name: str) -> str:
|
|
143
|
-
|
|
144
|
-
parts = [self.module_name]
|
|
145
|
-
if self.class_stack:
|
|
146
|
-
parts.append(self.class_stack[-1])
|
|
147
|
-
parts.append(name)
|
|
148
|
-
return '.'.join(parts)
|
|
143
|
+
return qualified_name(self.module_name, self.class_stack, name)
|
|
149
144
|
|
|
150
145
|
def _resolve_call(self, node: ast.AST) -> Optional[str]:
|
|
151
146
|
"""Resolve a call to its full name."""
|
|
@@ -6,7 +6,7 @@ from typing import Optional
|
|
|
6
6
|
|
|
7
7
|
from code2llm.core.config import Config
|
|
8
8
|
from code2llm.core.models import AnalysisResult, FlowNode, FlowEdge, FunctionInfo
|
|
9
|
-
from code2llm.analysis.utils import ast_unparse
|
|
9
|
+
from code2llm.analysis.utils import ast_unparse, qualified_name
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class CFGExtractor(ast.NodeVisitor):
|
|
@@ -261,12 +261,7 @@ class CFGExtractor(ast.NodeVisitor):
|
|
|
261
261
|
self.generic_visit(node)
|
|
262
262
|
|
|
263
263
|
def _qualified_name(self, name: str) -> str:
|
|
264
|
-
|
|
265
|
-
parts = [self.module_name]
|
|
266
|
-
if self.class_stack:
|
|
267
|
-
parts.append(self.class_stack[-1])
|
|
268
|
-
parts.append(name)
|
|
269
|
-
return '.'.join(parts)
|
|
264
|
+
return qualified_name(self.module_name, self.class_stack, name)
|
|
270
265
|
|
|
271
266
|
def _extract_condition(self, node: ast.AST) -> str:
|
|
272
267
|
"""Extract condition as string."""
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
"""Data Analysis logic for code2llm -
|
|
1
|
+
"""Data Analysis logic for code2llm - split into focused analyzers.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This module provides three analyzers:
|
|
4
|
+
- DataFlowAnalyzer: data pipelines, state patterns, dependencies, event flows
|
|
5
|
+
- OptimizationAdvisor: data types, optimization opportunities, process patterns
|
|
6
|
+
- DataAnalyzer: facade combining both analyzers (backward compatibility)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Any, Dict, List, Tuple
|
|
4
10
|
from code2llm.core.models import AnalysisResult
|
|
5
11
|
|
|
6
12
|
|
|
@@ -10,7 +16,7 @@ _OUTPUT_INDICATORS = ['serialize', 'format', 'write', 'save', 'send', 'output',
|
|
|
10
16
|
_MAX_PIPELINES = 15
|
|
11
17
|
|
|
12
18
|
|
|
13
|
-
def _categorize_functions(result: 'AnalysisResult'):
|
|
19
|
+
def _categorize_functions(result: 'AnalysisResult') -> Tuple[list, list, list]:
|
|
14
20
|
"""Categorize functions into input/transform/output based on name patterns."""
|
|
15
21
|
input_funcs, transform_funcs, output_funcs = [], [], []
|
|
16
22
|
for func_name, func in result.functions.items():
|
|
@@ -284,3 +290,81 @@ class DataAnalyzer:
|
|
|
284
290
|
opt['hub_optimization'].append({'function': hub['id'], 'connections': hub['in_degree'] + hub['out_degree'], 'optimization_type': 'split' if hub['out_degree'] > 10 else 'cache'})
|
|
285
291
|
opt['potential_score'] = (len(opt['type_consolidation']) * 10 + len(opt['process_consolidation']) * 15 + len(opt['hub_optimization']) * 5) / 100.0
|
|
286
292
|
return opt
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
# ---------------------------------------------------------------------------
|
|
296
|
+
# New focused analyzer classes (Step 5 refactoring)
|
|
297
|
+
# ---------------------------------------------------------------------------
|
|
298
|
+
|
|
299
|
+
class DataFlowAnalyzer:
|
|
300
|
+
"""Analyze data flows: pipelines, state patterns, dependencies, and event flows.
|
|
301
|
+
|
|
302
|
+
Extracted from DataAnalyzer to provide focused data flow analysis.
|
|
303
|
+
"""
|
|
304
|
+
|
|
305
|
+
def analyze(self, result: AnalysisResult) -> Dict[str, Any]:
|
|
306
|
+
"""Perform complete data flow analysis."""
|
|
307
|
+
return {
|
|
308
|
+
'data_pipelines': self.find_data_pipelines(result),
|
|
309
|
+
'state_patterns': self.find_state_patterns(result),
|
|
310
|
+
'data_dependencies': self.find_data_dependencies(result),
|
|
311
|
+
'event_flows': self.find_event_flows(result),
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
def find_data_pipelines(self, result: AnalysisResult) -> list:
|
|
315
|
+
"""Find data transformation pipelines (wrapper for backward compat)."""
|
|
316
|
+
# Delegate to the module-level helper via DataAnalyzer instance
|
|
317
|
+
return DataAnalyzer()._find_data_pipelines(result)
|
|
318
|
+
|
|
319
|
+
def find_state_patterns(self, result: AnalysisResult) -> list:
|
|
320
|
+
"""Find state management patterns."""
|
|
321
|
+
return DataAnalyzer()._find_state_patterns(result)
|
|
322
|
+
|
|
323
|
+
def find_data_dependencies(self, result: AnalysisResult) -> list:
|
|
324
|
+
"""Find cross-module data dependencies."""
|
|
325
|
+
return DataAnalyzer()._find_data_dependencies(result)
|
|
326
|
+
|
|
327
|
+
def find_event_flows(self, result: AnalysisResult) -> list:
|
|
328
|
+
"""Find event-driven patterns."""
|
|
329
|
+
return DataAnalyzer()._find_event_flows(result)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
class OptimizationAdvisor:
|
|
333
|
+
"""Analyze optimization opportunities: data types and process patterns.
|
|
334
|
+
|
|
335
|
+
Extracted from DataAnalyzer to provide focused optimization analysis.
|
|
336
|
+
"""
|
|
337
|
+
|
|
338
|
+
def analyze(self, result: AnalysisResult) -> Dict[str, Any]:
|
|
339
|
+
"""Perform complete optimization analysis."""
|
|
340
|
+
data_types = self.analyze_data_types(result)
|
|
341
|
+
data_flow_graph = self.build_data_flow_graph(result)
|
|
342
|
+
process_patterns = self.identify_process_patterns(result)
|
|
343
|
+
optimization_analysis = self.analyze_optimization_opportunities(
|
|
344
|
+
result, data_types, data_flow_graph
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
return {
|
|
348
|
+
'data_types': data_types,
|
|
349
|
+
'data_flow_graph': data_flow_graph,
|
|
350
|
+
'process_patterns': process_patterns,
|
|
351
|
+
'optimization_analysis': optimization_analysis,
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
def analyze_data_types(self, result: AnalysisResult) -> list:
|
|
355
|
+
"""Analyze data types and usage."""
|
|
356
|
+
return DataAnalyzer()._analyze_data_types(result)
|
|
357
|
+
|
|
358
|
+
def build_data_flow_graph(self, result: AnalysisResult) -> dict:
|
|
359
|
+
"""Build data flow graph from function relationships."""
|
|
360
|
+
return DataAnalyzer()._build_data_flow_graph(result)
|
|
361
|
+
|
|
362
|
+
def identify_process_patterns(self, result: AnalysisResult) -> list:
|
|
363
|
+
"""Identify common data processing patterns."""
|
|
364
|
+
return DataAnalyzer()._identify_process_patterns(result)
|
|
365
|
+
|
|
366
|
+
def analyze_optimization_opportunities(
|
|
367
|
+
self, result: AnalysisResult, data_types: list, dfg: dict
|
|
368
|
+
) -> dict:
|
|
369
|
+
"""Analyze optimization opportunities in data handling."""
|
|
370
|
+
return DataAnalyzer()._analyze_optimization_opportunities(result, data_types, dfg)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""Shared AST utilities for analysis modules."""
|
|
2
2
|
|
|
3
|
-
from .ast_helpers import get_ast, find_function_node, expr_to_str, ast_unparse
|
|
3
|
+
from .ast_helpers import get_ast, find_function_node, expr_to_str, ast_unparse, qualified_name
|
|
4
4
|
|
|
5
|
-
__all__ = ["get_ast", "find_function_node", "expr_to_str", "ast_unparse"]
|
|
5
|
+
__all__ = ["get_ast", "find_function_node", "expr_to_str", "ast_unparse", "qualified_name"]
|
|
@@ -57,6 +57,19 @@ def ast_unparse(node: Optional[ast.AST], default_none: str = "None") -> str:
|
|
|
57
57
|
return str(node)
|
|
58
58
|
|
|
59
59
|
|
|
60
|
+
def qualified_name(module_name: str, class_stack: list, name: str) -> str:
|
|
61
|
+
"""Build a fully-qualified dotted name from module, optional class scope, and name.
|
|
62
|
+
|
|
63
|
+
Shared replacement for the identical ``_qualified_name`` methods in
|
|
64
|
+
``CallGraphExtractor`` and ``CFGExtractor``.
|
|
65
|
+
"""
|
|
66
|
+
parts = [module_name]
|
|
67
|
+
if class_stack:
|
|
68
|
+
parts.append(class_stack[-1])
|
|
69
|
+
parts.append(name)
|
|
70
|
+
return '.'.join(parts)
|
|
71
|
+
|
|
72
|
+
|
|
60
73
|
def expr_to_str(node: ast.expr) -> Optional[str]:
|
|
61
74
|
"""Convert an AST expression to a dotted string (for call-name extraction).
|
|
62
75
|
|
|
@@ -9,6 +9,12 @@ from typing import Any, Optional, Tuple
|
|
|
9
9
|
import ast
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
def make_cache_key(file_path: str, content: str) -> str:
|
|
13
|
+
"""Generate a cache key from file stem and MD5 of content."""
|
|
14
|
+
content_hash = hashlib.md5(content.encode()).hexdigest()[:16]
|
|
15
|
+
return f"{Path(file_path).stem}_{content_hash}"
|
|
16
|
+
|
|
17
|
+
|
|
12
18
|
class FileCache:
|
|
13
19
|
"""Cache for parsed AST files."""
|
|
14
20
|
|
|
@@ -26,9 +32,7 @@ class FileCache:
|
|
|
26
32
|
return f"{Path(file_path).stem}_unknown"
|
|
27
33
|
|
|
28
34
|
def _get_cache_key(self, file_path: str, content: str) -> str:
|
|
29
|
-
|
|
30
|
-
content_hash = hashlib.md5(content.encode()).hexdigest()[:16]
|
|
31
|
-
return f"{Path(file_path).stem}_{content_hash}"
|
|
35
|
+
return make_cache_key(file_path, content)
|
|
32
36
|
|
|
33
37
|
def _get_cache_path(self, cache_key: str) -> Path:
|
|
34
38
|
"""Get cache file path."""
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"""Memory-efficient streaming cache with LRU eviction."""
|
|
2
2
|
|
|
3
3
|
import ast
|
|
4
|
-
import hashlib
|
|
5
4
|
from pathlib import Path
|
|
6
5
|
from typing import Dict, List, Optional, Tuple
|
|
7
6
|
|
|
7
|
+
from code2llm.core.file_cache import make_cache_key
|
|
8
|
+
|
|
8
9
|
|
|
9
10
|
class StreamingFileCache:
|
|
10
11
|
"""Memory-efficient cache with LRU eviction."""
|
|
@@ -17,9 +18,7 @@ class StreamingFileCache:
|
|
|
17
18
|
self._access_order: List[str] = []
|
|
18
19
|
|
|
19
20
|
def _get_cache_key(self, file_path: str, content: str) -> str:
|
|
20
|
-
|
|
21
|
-
content_hash = hashlib.md5(content.encode()).hexdigest()[:16]
|
|
22
|
-
return f"{Path(file_path).stem}_{content_hash}"
|
|
21
|
+
return make_cache_key(file_path, content)
|
|
23
22
|
|
|
24
23
|
def _evict_if_needed(self) -> None:
|
|
25
24
|
"""Evict oldest entries if cache is full."""
|
|
@@ -7,15 +7,11 @@ from datetime import datetime
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from typing import Any, Dict, List
|
|
9
9
|
|
|
10
|
+
from code2llm.exporters.base import ViewGeneratorMixin
|
|
10
11
|
|
|
11
|
-
class ArticleViewGenerator:
|
|
12
|
-
"""Generate status.md — publishable project health article."""
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
17
|
-
with open(output_path, "w", encoding="utf-8") as f:
|
|
18
|
-
f.write("\n".join(lines) + "\n")
|
|
13
|
+
class ArticleViewGenerator(ViewGeneratorMixin):
|
|
14
|
+
"""Generate status.md — publishable project health article."""
|
|
19
15
|
|
|
20
16
|
def _render(self, data: Dict[str, Any]) -> List[str]:
|
|
21
17
|
proj = data.get("project", {})
|
|
@@ -6,15 +6,11 @@ Generates context.md from project.yaml data.
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import Any, Dict, List
|
|
8
8
|
|
|
9
|
+
from code2llm.exporters.base import ViewGeneratorMixin
|
|
9
10
|
|
|
10
|
-
class ContextViewGenerator:
|
|
11
|
-
"""Generate context.md from project.yaml data."""
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
16
|
-
with open(output_path, "w", encoding="utf-8") as f:
|
|
17
|
-
f.write("\n".join(lines) + "\n")
|
|
12
|
+
class ContextViewGenerator(ViewGeneratorMixin):
|
|
13
|
+
"""Generate context.md from project.yaml data."""
|
|
18
14
|
|
|
19
15
|
def _render(self, data: Dict[str, Any]) -> List[str]:
|
|
20
16
|
proj = data.get("project", {})
|
|
@@ -15,6 +15,19 @@ EXCLUDE_PATTERNS = {
|
|
|
15
15
|
'dist', 'build', 'egg-info', '.tox', '.mypy_cache',
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
def is_excluded_path(path: str) -> bool:
|
|
19
|
+
"""Return True if *path* matches any standard exclusion pattern (venv, cache, etc.)."""
|
|
20
|
+
if not path:
|
|
21
|
+
return False
|
|
22
|
+
path_lower = path.lower().replace('\\', '/')
|
|
23
|
+
for pattern in EXCLUDE_PATTERNS:
|
|
24
|
+
if f'/{pattern}/' in path_lower or path_lower.startswith(f'{pattern}/'):
|
|
25
|
+
return True
|
|
26
|
+
if pattern in path_lower.split('/'):
|
|
27
|
+
return True
|
|
28
|
+
return False
|
|
29
|
+
|
|
30
|
+
|
|
18
31
|
# Rekomendacje podziału typów hub: typ -> sugerowane pod-interfejsy
|
|
19
32
|
HUB_SPLIT_RECOMMENDATIONS = {
|
|
20
33
|
"AnalysisResult": [
|
|
@@ -17,7 +17,7 @@ from pathlib import Path
|
|
|
17
17
|
from typing import Any, Dict, List, Optional, Set, Tuple
|
|
18
18
|
|
|
19
19
|
from .base import BaseExporter, export_format
|
|
20
|
-
from .flow_constants import CC_HIGH, FAN_OUT_THRESHOLD, EXCLUDE_PATTERNS, HUB_SPLIT_RECOMMENDATIONS, HUB_TYPE_THRESHOLD
|
|
20
|
+
from .flow_constants import CC_HIGH, FAN_OUT_THRESHOLD, EXCLUDE_PATTERNS, HUB_SPLIT_RECOMMENDATIONS, HUB_TYPE_THRESHOLD, is_excluded_path
|
|
21
21
|
from .flow_renderer import FlowRenderer
|
|
22
22
|
from code2llm.core.models import (
|
|
23
23
|
AnalysisResult, FunctionInfo, ClassInfo, ModuleInfo, FlowNode
|
|
@@ -382,12 +382,4 @@ class FlowExporter(BaseExporter):
|
|
|
382
382
|
# utility helpers
|
|
383
383
|
# ------------------------------------------------------------------
|
|
384
384
|
def _is_excluded(self, path: str) -> bool:
|
|
385
|
-
|
|
386
|
-
return False
|
|
387
|
-
path_lower = path.lower().replace('\\', '/')
|
|
388
|
-
for pattern in EXCLUDE_PATTERNS:
|
|
389
|
-
if f'/{pattern}/' in path_lower or path_lower.startswith(f'{pattern}/'):
|
|
390
|
-
return True
|
|
391
|
-
if pattern in path_lower.split('/'):
|
|
392
|
-
return True
|
|
393
|
-
return False
|
|
385
|
+
return is_excluded_path(path)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Index HTML Generator — web-based file browser for all generated outputs.
|
|
2
|
+
|
|
3
|
+
Generates index.html that provides a GitHub Pages-ready interface
|
|
4
|
+
for browsing all generated analysis files (toon, md, yaml, json, etc.)
|
|
5
|
+
|
|
6
|
+
This package provides three components:
|
|
7
|
+
- FileScanner: scans output directory and collects file metadata
|
|
8
|
+
- HTMLRenderer: generates HTML with CSS and JavaScript
|
|
9
|
+
- IndexHTMLGenerator: facade combining scanner and renderer
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Dict, List, Optional
|
|
14
|
+
|
|
15
|
+
from .scanner import FileScanner, get_file_types, get_default_file_info, FILE_TYPES
|
|
16
|
+
from .renderer import HTMLRenderer
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class IndexHTMLGenerator:
|
|
20
|
+
"""Generate index.html for browsing all generated files.
|
|
21
|
+
|
|
22
|
+
This is a facade class that combines FileScanner and HTMLRenderer
|
|
23
|
+
to provide a simple interface for generating the index file.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
# Backward compatibility: expose FILE_TYPES at class level
|
|
27
|
+
FILE_TYPES = FILE_TYPES
|
|
28
|
+
|
|
29
|
+
def __init__(self, output_dir: Path):
|
|
30
|
+
self.output_dir = Path(output_dir).resolve()
|
|
31
|
+
self._scanner = FileScanner(self.output_dir)
|
|
32
|
+
self._renderer = HTMLRenderer()
|
|
33
|
+
|
|
34
|
+
def generate(self) -> Path:
|
|
35
|
+
"""Generate index.html in the output directory."""
|
|
36
|
+
files = self._scanner.scan()
|
|
37
|
+
html = self._renderer.render(files)
|
|
38
|
+
index_path = self.output_dir / 'index.html'
|
|
39
|
+
with open(index_path, 'w', encoding='utf-8') as f:
|
|
40
|
+
f.write(html)
|
|
41
|
+
return index_path
|
|
42
|
+
|
|
43
|
+
def scan_files(self) -> List[Dict[str, Any]]:
|
|
44
|
+
"""Scan and return file metadata without generating HTML."""
|
|
45
|
+
return self._scanner.scan()
|
|
46
|
+
|
|
47
|
+
def render_html(self, files: List[Dict[str, Any]]) -> str:
|
|
48
|
+
"""Render HTML from pre-scanned file list."""
|
|
49
|
+
return self._renderer.render(files)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# Convenience function for direct usage
|
|
53
|
+
def generate_index_html(output_dir: Path) -> Path:
|
|
54
|
+
"""Generate index.html in the specified directory.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
output_dir: Directory containing generated analysis files
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Path to the generated index.html file
|
|
61
|
+
"""
|
|
62
|
+
return IndexHTMLGenerator(output_dir).generate()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
__all__ = [
|
|
66
|
+
'IndexHTMLGenerator',
|
|
67
|
+
'FileScanner',
|
|
68
|
+
'HTMLRenderer',
|
|
69
|
+
'generate_index_html',
|
|
70
|
+
'get_file_types',
|
|
71
|
+
'get_default_file_info',
|
|
72
|
+
'FILE_TYPES',
|
|
73
|
+
]
|