code2llm 0.5.115__tar.gz → 0.5.117__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.
Files changed (139) hide show
  1. {code2llm-0.5.115 → code2llm-0.5.117}/PKG-INFO +2 -2
  2. {code2llm-0.5.115 → code2llm-0.5.117}/README.md +1 -1
  3. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/__init__.py +1 -1
  4. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/call_graph.py +2 -7
  5. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/cfg.py +2 -7
  6. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/dfg.py +2 -7
  7. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli_exports/formats.py +17 -20
  8. code2llm-0.5.117/code2llm/core/lang/__init__.py +173 -0
  9. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/ruby.py +22 -1
  10. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/base.py +16 -0
  11. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/nlp/__init__.py +1 -1
  12. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm.egg-info/PKG-INFO +2 -2
  13. {code2llm-0.5.115 → code2llm-0.5.117}/pyproject.toml +1 -1
  14. {code2llm-0.5.115 → code2llm-0.5.117}/setup.py +1 -1
  15. code2llm-0.5.115/code2llm/core/lang/__init__.py +0 -11
  16. {code2llm-0.5.115 → code2llm-0.5.117}/LICENSE +0 -0
  17. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/__main__.py +0 -0
  18. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/__init__.py +0 -0
  19. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/coupling.py +0 -0
  20. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/data_analysis.py +0 -0
  21. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/pipeline_detector.py +0 -0
  22. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/side_effects.py +0 -0
  23. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/smells.py +0 -0
  24. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/type_inference.py +0 -0
  25. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/utils/__init__.py +0 -0
  26. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/utils/ast_helpers.py +0 -0
  27. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/api.py +0 -0
  28. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli.py +0 -0
  29. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli_analysis.py +0 -0
  30. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli_commands.py +0 -0
  31. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli_exports/__init__.py +0 -0
  32. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli_exports/code2logic.py +0 -0
  33. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli_exports/orchestrator.py +0 -0
  34. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli_exports/prompt.py +0 -0
  35. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli_parser.py +0 -0
  36. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/__init__.py +0 -0
  37. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/analyzer.py +0 -0
  38. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/ast_registry.py +0 -0
  39. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/config.py +0 -0
  40. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/export_pipeline.py +0 -0
  41. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/file_analyzer.py +0 -0
  42. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/file_cache.py +0 -0
  43. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/file_filter.py +0 -0
  44. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/gitignore.py +0 -0
  45. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/incremental.py +0 -0
  46. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/base.py +0 -0
  47. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/cpp.py +0 -0
  48. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/csharp.py +0 -0
  49. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/generic.py +0 -0
  50. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/go_lang.py +0 -0
  51. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/java.py +0 -0
  52. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/php.py +0 -0
  53. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/rust.py +0 -0
  54. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/ts_extractors.py +0 -0
  55. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/ts_parser.py +0 -0
  56. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/typescript.py +0 -0
  57. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/large_repo.py +0 -0
  58. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/models.py +0 -0
  59. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/persistent_cache.py +0 -0
  60. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/refactoring.py +0 -0
  61. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/repo_files.py +0 -0
  62. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/streaming/__init__.py +0 -0
  63. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/streaming/cache.py +0 -0
  64. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/streaming/incremental.py +0 -0
  65. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/streaming/prioritizer.py +0 -0
  66. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/streaming/scanner.py +0 -0
  67. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/streaming/strategies.py +0 -0
  68. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/streaming_analyzer.py +0 -0
  69. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/toon_size_manager.py +0 -0
  70. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/__init__.py +0 -0
  71. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/article_view.py +0 -0
  72. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/context_exporter.py +0 -0
  73. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/context_view.py +0 -0
  74. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/evolution_exporter.py +0 -0
  75. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/flow_constants.py +0 -0
  76. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/flow_exporter.py +0 -0
  77. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/flow_renderer.py +0 -0
  78. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/html_dashboard.py +0 -0
  79. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/index_generator.py +0 -0
  80. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/json_exporter.py +0 -0
  81. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/llm_exporter.py +0 -0
  82. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/map_exporter.py +0 -0
  83. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/mermaid_exporter.py +0 -0
  84. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
  85. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/project_yaml/__init__.py +0 -0
  86. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/project_yaml/constants.py +0 -0
  87. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/project_yaml/core.py +0 -0
  88. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/project_yaml/evolution.py +0 -0
  89. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/project_yaml/health.py +0 -0
  90. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/project_yaml/hotspots.py +0 -0
  91. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/project_yaml/modules.py +0 -0
  92. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/project_yaml_exporter.py +0 -0
  93. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/readme_exporter.py +0 -0
  94. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/report_generators.py +0 -0
  95. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/toon/__init__.py +0 -0
  96. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/toon/helpers.py +0 -0
  97. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/toon/metrics.py +0 -0
  98. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/toon/module_detail.py +0 -0
  99. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/toon/renderer.py +0 -0
  100. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/toon.py +0 -0
  101. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/toon_view.py +0 -0
  102. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/validate_project.py +0 -0
  103. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/yaml_exporter.py +0 -0
  104. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/generators/__init__.py +0 -0
  105. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/generators/llm_flow.py +0 -0
  106. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/generators/llm_task.py +0 -0
  107. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/generators/mermaid.py +0 -0
  108. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/nlp/config.py +0 -0
  109. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/nlp/entity_resolution.py +0 -0
  110. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/nlp/intent_matching.py +0 -0
  111. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/nlp/normalization.py +0 -0
  112. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/nlp/pipeline.py +0 -0
  113. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/patterns/__init__.py +0 -0
  114. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/patterns/detector.py +0 -0
  115. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/refactor/__init__.py +0 -0
  116. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/refactor/prompt_engine.py +0 -0
  117. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm.egg-info/SOURCES.txt +0 -0
  118. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm.egg-info/dependency_links.txt +0 -0
  119. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm.egg-info/entry_points.txt +0 -0
  120. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm.egg-info/requires.txt +0 -0
  121. {code2llm-0.5.115 → code2llm-0.5.117}/code2llm.egg-info/top_level.txt +0 -0
  122. {code2llm-0.5.115 → code2llm-0.5.117}/setup.cfg +0 -0
  123. {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_advanced_analysis.py +0 -0
  124. {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_analyzer.py +0 -0
  125. {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_calls_toon_export.py +0 -0
  126. {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_deep_analysis.py +0 -0
  127. {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_edge_cases.py +0 -0
  128. {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_flow_exporter.py +0 -0
  129. {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_format_quality.py +0 -0
  130. {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_multilanguage_e2e.py +0 -0
  131. {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_nlp_pipeline.py +0 -0
  132. {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_nonpython_cc_calls.py +0 -0
  133. {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_persistent_cache.py +0 -0
  134. {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_pipeline_detector.py +0 -0
  135. {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_project_toon_export.py +0 -0
  136. {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_prompt_engine.py +0 -0
  137. {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_prompt_txt.py +0 -0
  138. {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_refactoring_engine.py +0 -0
  139. {code2llm-0.5.115 → code2llm-0.5.117}/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.115
3
+ Version: 0.5.117
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
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.115-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
70
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.117-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
71
71
  ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-57.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
72
72
 
73
73
  - 🤖 **LLM usage:** $7.5000 (166 commits)
@@ -3,7 +3,7 @@
3
3
 
4
4
  ## AI Cost Tracking
5
5
 
6
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.115-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
6
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.117-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
7
7
  ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-57.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
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.115"
11
+ __version__ = "0.5.117"
12
12
  __author__ = "STTS Project"
13
13
 
14
14
  # Core analysis components (lightweight, always needed)
@@ -5,6 +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
9
 
9
10
 
10
11
  class CallGraphExtractor(ast.NodeVisitor):
@@ -202,10 +203,4 @@ class CallGraphExtractor(ast.NodeVisitor):
202
203
  return None
203
204
 
204
205
  def _expr_to_str(self, node: ast.AST) -> str:
205
- """Convert AST expression to string."""
206
- if node is None:
207
- return ""
208
- try:
209
- return ast.unparse(node) if hasattr(ast, 'unparse') else str(node)
210
- except:
211
- return str(node)
206
+ return ast_unparse(node, default_none="")
@@ -6,6 +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
10
 
10
11
 
11
12
  class CFGExtractor(ast.NodeVisitor):
@@ -275,13 +276,7 @@ class CFGExtractor(ast.NodeVisitor):
275
276
  return str(node)[:50]
276
277
 
277
278
  def _expr_to_str(self, node: ast.AST) -> str:
278
- """Convert AST expression to string."""
279
- if node is None:
280
- return "None"
281
- try:
282
- return ast.unparse(node) if hasattr(ast, 'unparse') else str(node)
283
- except:
284
- return str(node)
279
+ return ast_unparse(node)
285
280
 
286
281
  def _format_except(self, handler: ast.ExceptHandler) -> str:
287
282
  """Format except handler."""
@@ -6,6 +6,7 @@ from typing import Set, Dict, List
6
6
 
7
7
  from code2llm.core.config import Config
8
8
  from code2llm.core.models import AnalysisResult, FlowEdge, DataFlow, Mutation
9
+ from code2llm.analysis.utils import ast_unparse
9
10
 
10
11
 
11
12
  class DFGExtractor(ast.NodeVisitor):
@@ -207,13 +208,7 @@ class DFGExtractor(ast.NodeVisitor):
207
208
  return names
208
209
 
209
210
  def _expr_to_str(self, node: ast.AST) -> str:
210
- """Convert AST expression to string."""
211
- if node is None:
212
- return "None"
213
- try:
214
- return ast.unparse(node) if hasattr(ast, 'unparse') else str(node)
215
- except:
216
- return str(node)
211
+ return ast_unparse(node)
217
212
 
218
213
  def _build_data_flow_edges(self):
219
214
  """Build DFG edges from data flow records."""
@@ -215,30 +215,27 @@ def _export_mermaid_pngs(args, output_dir: Path) -> None:
215
215
  print(f" - PNG: Skipped (install with: make install-mermaid)")
216
216
 
217
217
 
218
- def _export_calls(args, result, output_dir: Path):
219
- """Export standalone calls.yaml (structured call graph YAML).
220
-
221
- Generates calls.yaml with structured call graph data:
222
- - nodes: functions with metadata (CC, calls_in/out)
223
- - edges: caller -> callee relationships
224
- - modules: grouping by module
225
- - stats: summary statistics
226
- """
218
+ def _export_calls_format(args, result, output_dir: Path, toon: bool = False) -> None:
219
+ """Shared helper: export call graph in YAML or toon format."""
227
220
  yaml_exporter = YAMLExporter()
228
- yaml_exporter.export_calls(result, str(output_dir / 'calls.yaml'))
229
- if args.verbose:
230
- print(f" - CALLS (call graph YAML): {output_dir / 'calls.yaml'}")
221
+ if toon:
222
+ yaml_exporter.export_calls_toon(result, str(output_dir / 'calls.toon.yaml'))
223
+ if args.verbose:
224
+ print(f" - CALLS (toon format): {output_dir / 'calls.toon.yaml'}")
225
+ else:
226
+ yaml_exporter.export_calls(result, str(output_dir / 'calls.yaml'))
227
+ if args.verbose:
228
+ print(f" - CALLS (call graph YAML): {output_dir / 'calls.yaml'}")
231
229
 
232
230
 
233
- def _export_calls_toon(args, result, output_dir: Path):
234
- """Export calls.toon.yaml (call graph in human-readable toon format).
231
+ def _export_calls(args, result, output_dir: Path):
232
+ """Export standalone calls.yaml (structured call graph YAML)."""
233
+ _export_calls_format(args, result, output_dir, toon=False)
235
234
 
236
- Generates calls.toon.yaml with hubs, modules, and edges sections.
237
- """
238
- yaml_exporter = YAMLExporter()
239
- yaml_exporter.export_calls_toon(result, str(output_dir / 'calls.toon.yaml'))
240
- if args.verbose:
241
- print(f" - CALLS (toon format): {output_dir / 'calls.toon.yaml'}")
235
+
236
+ def _export_calls_toon(args, result, output_dir: Path):
237
+ """Export calls.toon.yaml (call graph in human-readable toon format)."""
238
+ _export_calls_format(args, result, output_dir, toon=True)
242
239
 
243
240
 
244
241
  def _export_mermaid(args, result, output_dir: Path):
@@ -0,0 +1,173 @@
1
+ """Language-specific analyzers for non-Python source files.
2
+
3
+ Provides:
4
+ - LanguageParser ABC — abstract base class for all language parsers
5
+ - LANGUAGE_REGISTRY — dict mapping extensions to parser functions
6
+ - register_language — decorator for auto-registration
7
+ - get_parser(extension) — lookup parser by file extension
8
+ """
9
+
10
+ from abc import ABC, abstractmethod
11
+ from typing import Dict, Any, Callable, Set, Optional
12
+ from pathlib import Path
13
+
14
+ from code2llm.core.models import ModuleInfo, FunctionInfo, ClassInfo
15
+
16
+
17
+ # Type alias for parser results
18
+ ParserResult = Dict[str, Any]
19
+
20
+
21
+ class LanguageParser(ABC):
22
+ """Abstract base class for language-specific parsers.
23
+
24
+ All language parsers must inherit from this class and implement:
25
+ - analyze() method to parse file content
26
+ - supported_extensions class attribute
27
+
28
+ Example:
29
+ @register_language('.go', '.golang')
30
+ class GoParser(LanguageParser):
31
+ supported_extensions = ('.go', '.golang')
32
+
33
+ def analyze(self, content, file_path, module_name, stats):
34
+ # Parse Go code
35
+ return {'module': ..., 'functions': ..., 'classes': ...}
36
+ """
37
+
38
+ supported_extensions: tuple = ()
39
+ language_name: str = ""
40
+
41
+ @abstractmethod
42
+ def analyze(
43
+ self,
44
+ content: str,
45
+ file_path: str,
46
+ module_name: str,
47
+ stats: Dict[str, Any]
48
+ ) -> ParserResult:
49
+ """Analyze file content and return parsed structure.
50
+
51
+ Args:
52
+ content: File content as string
53
+ file_path: Absolute path to file
54
+ module_name: Logical module name
55
+ stats: Statistics dict to update
56
+
57
+ Returns:
58
+ Dict with 'module', 'functions', 'classes', 'nodes', 'edges'
59
+ """
60
+ pass
61
+
62
+ def can_parse(self, file_path: str) -> bool:
63
+ """Check if this parser can handle the given file."""
64
+ ext = Path(file_path).suffix.lower()
65
+ return ext in self.supported_extensions
66
+
67
+
68
+ # Legacy parser function type
69
+ LegacyParser = Callable[[str, str, str, str, Dict[str, Any]], ParserResult]
70
+
71
+
72
+ # Registry: extension -> parser instance or function
73
+ LANGUAGE_REGISTRY: Dict[str, Any] = {}
74
+
75
+
76
+ def register_language(*extensions: str, name: str = ""):
77
+ """Decorator to register a language parser.
78
+
79
+ Args:
80
+ *extensions: File extensions this parser handles (e.g., '.go', '.rs')
81
+ name: Human-readable language name
82
+
83
+ Example:
84
+ @register_language('.go', '.golang', name='Go')
85
+ class GoParser(LanguageParser):
86
+ ...
87
+
88
+ @register_language('.rb', name='Ruby')
89
+ def analyze_ruby(content, file_path, module_name, ext, stats):
90
+ ...
91
+ """
92
+ def decorator(cls_or_func):
93
+ if isinstance(cls_or_func, type) and issubclass(cls_or_func, LanguageParser):
94
+ # It's a class
95
+ parser = cls_or_func()
96
+ parser.language_name = name or cls_or_func.__name__.replace('Parser', '')
97
+ for ext in extensions:
98
+ LANGUAGE_REGISTRY[ext.lower()] = parser
99
+ else:
100
+ # It's a function
101
+ for ext in extensions:
102
+ LANGUAGE_REGISTRY[ext.lower()] = cls_or_func
103
+ return cls_or_func
104
+ return decorator
105
+
106
+
107
+ def get_parser(extension: str) -> Optional[Any]:
108
+ """Get parser for a file extension.
109
+
110
+ Args:
111
+ extension: File extension (e.g., '.go', '.rs')
112
+
113
+ Returns:
114
+ LanguageParser instance or legacy function, or None if not found
115
+ """
116
+ return LANGUAGE_REGISTRY.get(extension.lower())
117
+
118
+
119
+ def list_parsers() -> Dict[str, Any]:
120
+ """List all registered parsers."""
121
+ return dict(LANGUAGE_REGISTRY)
122
+
123
+
124
+ # Legacy function imports (for backward compatibility)
125
+ from .typescript import analyze_typescript_js
126
+ from .go_lang import analyze_go
127
+ from .rust import analyze_rust
128
+ from .java import analyze_java
129
+ from .cpp import analyze_cpp
130
+ from .csharp import analyze_csharp
131
+ from .php import analyze_php
132
+ from .ruby import analyze_ruby
133
+ from .generic import analyze_generic
134
+
135
+
136
+ # Register legacy parsers
137
+ register_language('.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs')(analyze_typescript_js)
138
+ register_language('.go')(analyze_go)
139
+ register_language('.rs')(analyze_rust)
140
+ register_language('.java')(analyze_java)
141
+ register_language('.cpp', '.cc', '.cxx', '.hpp', '.h', '.c')(analyze_cpp)
142
+ register_language('.cs')(analyze_csharp)
143
+ register_language('.php')(analyze_php)
144
+
145
+ # Import and register RubyParser class (demonstrating new ABC pattern)
146
+ from .ruby import RubyParser
147
+ LANGUAGE_REGISTRY['.rb'] = RubyParser()
148
+ LANGUAGE_REGISTRY['.rbw'] = RubyParser()
149
+
150
+ # Generic parser as fallback for unknown extensions
151
+ LANGUAGE_REGISTRY['*'] = analyze_generic
152
+
153
+
154
+ __all__ = [
155
+ 'LanguageParser',
156
+ 'ParserResult',
157
+ 'register_language',
158
+ 'LANGUAGE_REGISTRY',
159
+ 'get_parser',
160
+ 'list_parsers',
161
+ # New class-based parsers
162
+ 'RubyParser',
163
+ # Legacy exports
164
+ 'analyze_typescript_js',
165
+ 'analyze_go',
166
+ 'analyze_rust',
167
+ 'analyze_java',
168
+ 'analyze_cpp',
169
+ 'analyze_csharp',
170
+ 'analyze_php',
171
+ 'analyze_ruby',
172
+ 'analyze_generic',
173
+ ]
@@ -138,6 +138,27 @@ def analyze_ruby(content: str, file_path: str, module_name: str,
138
138
  }
139
139
 
140
140
  extract_calls_regex(content, module_name, result)
141
-
141
+
142
142
  stats['files_processed'] += 1
143
143
  return result
144
+
145
+
146
+ # New LanguageParser ABC implementation (demonstrating the new pattern)
147
+ # Note: Imports at the bottom to avoid circular imports
148
+ # The __init__.py handles the actual registration
149
+ class RubyParser:
150
+ """Ruby language parser - registered via @register_language in __init__.py."""
151
+
152
+ supported_extensions = ('.rb', '.rbw')
153
+ language_name = 'Ruby'
154
+
155
+ def analyze(
156
+ self,
157
+ content: str,
158
+ file_path: str,
159
+ module_name: str,
160
+ stats: Dict
161
+ ) -> Dict:
162
+ """Analyze Ruby file content."""
163
+ # Delegate to the existing legacy function for now
164
+ return analyze_ruby(content, file_path, module_name, '.rb', stats)
@@ -94,6 +94,22 @@ class BaseExporter(ABC):
94
94
  Exporter = BaseExporter
95
95
 
96
96
 
97
+ class ViewGeneratorMixin:
98
+ """Mixin providing the shared ``generate`` implementation for view generators.
99
+
100
+ Eliminates the identical ``generate`` method duplicated across
101
+ ``ArticleViewGenerator``, ``ContextViewGenerator``, and ``ToonViewGenerator``.
102
+
103
+ Subclasses must implement ``_render(data) -> List[str]``.
104
+ """
105
+
106
+ def generate(self, data: Dict[str, Any], output_path: str) -> None:
107
+ lines = self._render(data)
108
+ Path(output_path).parent.mkdir(parents=True, exist_ok=True)
109
+ with open(output_path, "w", encoding="utf-8") as f:
110
+ f.write("\n".join(lines) + "\n")
111
+
112
+
97
113
  # Export registry: format_name -> exporter_class
98
114
  EXPORT_REGISTRY: Dict[str, Type[BaseExporter]] = {}
99
115
 
@@ -4,7 +4,7 @@ Provides query normalization, intent matching, and entity resolution
4
4
  with multilingual support and fuzzy matching.
5
5
  """
6
6
 
7
- __version__ = "0.5.115"
7
+ __version__ = "0.5.117"
8
8
 
9
9
  from .pipeline import NLPPipeline
10
10
  from .normalization import QueryNormalizer
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code2llm
3
- Version: 0.5.115
3
+ Version: 0.5.117
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
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.115-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
70
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.117-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
71
71
  ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-57.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
72
72
 
73
73
  - 🤖 **LLM usage:** $7.5000 (166 commits)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code2llm"
7
- version = "0.5.115"
7
+ version = "0.5.117"
8
8
  description = "High-performance Python code flow analysis with optimized TOON format - CFG, DFG, call graphs, and intelligent code queries"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -4,7 +4,7 @@ from setuptools import setup, find_packages
4
4
  import os
5
5
 
6
6
  # Read version
7
- version = "0.5.114"
7
+ version = "0.5.116"
8
8
 
9
9
  # Read long description
10
10
  def read_readme():
@@ -1,11 +0,0 @@
1
- """Language-specific analyzers for non-Python source files."""
2
-
3
- from .typescript import analyze_typescript_js
4
- from .go_lang import analyze_go
5
- from .rust import analyze_rust
6
- from .java import analyze_java
7
- from .cpp import analyze_cpp
8
- from .csharp import analyze_csharp
9
- from .php import analyze_php
10
- from .ruby import analyze_ruby
11
- from .generic import analyze_generic
File without changes
File without changes
File without changes
File without changes