code2llm 0.5.112__tar.gz → 0.5.114__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 (136) hide show
  1. {code2llm-0.5.112 → code2llm-0.5.114}/PKG-INFO +2 -2
  2. {code2llm-0.5.112 → code2llm-0.5.114}/README.md +1 -1
  3. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/__init__.py +1 -1
  4. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/nlp/__init__.py +1 -1
  5. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm.egg-info/PKG-INFO +2 -2
  6. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm.egg-info/SOURCES.txt +1 -0
  7. {code2llm-0.5.112 → code2llm-0.5.114}/pyproject.toml +1 -1
  8. {code2llm-0.5.112 → code2llm-0.5.114}/setup.py +1 -1
  9. code2llm-0.5.114/tests/test_calls_toon_export.py +241 -0
  10. {code2llm-0.5.112 → code2llm-0.5.114}/LICENSE +0 -0
  11. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/__main__.py +0 -0
  12. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/analysis/__init__.py +0 -0
  13. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/analysis/call_graph.py +0 -0
  14. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/analysis/cfg.py +0 -0
  15. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/analysis/coupling.py +0 -0
  16. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/analysis/data_analysis.py +0 -0
  17. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/analysis/dfg.py +0 -0
  18. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/analysis/pipeline_detector.py +0 -0
  19. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/analysis/side_effects.py +0 -0
  20. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/analysis/smells.py +0 -0
  21. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/analysis/type_inference.py +0 -0
  22. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/analysis/utils/__init__.py +0 -0
  23. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/analysis/utils/ast_helpers.py +0 -0
  24. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/api.py +0 -0
  25. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/cli.py +0 -0
  26. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/cli_analysis.py +0 -0
  27. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/cli_commands.py +0 -0
  28. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/cli_exports/__init__.py +0 -0
  29. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/cli_exports/code2logic.py +0 -0
  30. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/cli_exports/formats.py +0 -0
  31. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/cli_exports/orchestrator.py +0 -0
  32. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/cli_exports/prompt.py +0 -0
  33. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/cli_parser.py +0 -0
  34. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/__init__.py +0 -0
  35. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/analyzer.py +0 -0
  36. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/ast_registry.py +0 -0
  37. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/config.py +0 -0
  38. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/export_pipeline.py +0 -0
  39. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/file_analyzer.py +0 -0
  40. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/file_cache.py +0 -0
  41. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/file_filter.py +0 -0
  42. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/gitignore.py +0 -0
  43. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/incremental.py +0 -0
  44. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/lang/__init__.py +0 -0
  45. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/lang/base.py +0 -0
  46. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/lang/cpp.py +0 -0
  47. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/lang/csharp.py +0 -0
  48. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/lang/generic.py +0 -0
  49. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/lang/go_lang.py +0 -0
  50. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/lang/java.py +0 -0
  51. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/lang/php.py +0 -0
  52. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/lang/ruby.py +0 -0
  53. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/lang/rust.py +0 -0
  54. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/lang/ts_extractors.py +0 -0
  55. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/lang/ts_parser.py +0 -0
  56. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/lang/typescript.py +0 -0
  57. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/large_repo.py +0 -0
  58. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/models.py +0 -0
  59. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/refactoring.py +0 -0
  60. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/repo_files.py +0 -0
  61. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/streaming/__init__.py +0 -0
  62. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/streaming/cache.py +0 -0
  63. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/streaming/incremental.py +0 -0
  64. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/streaming/prioritizer.py +0 -0
  65. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/streaming/scanner.py +0 -0
  66. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/streaming/strategies.py +0 -0
  67. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/streaming_analyzer.py +0 -0
  68. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/core/toon_size_manager.py +0 -0
  69. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/__init__.py +0 -0
  70. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/article_view.py +0 -0
  71. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/base.py +0 -0
  72. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/context_exporter.py +0 -0
  73. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/context_view.py +0 -0
  74. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/evolution_exporter.py +0 -0
  75. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/flow_constants.py +0 -0
  76. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/flow_exporter.py +0 -0
  77. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/flow_renderer.py +0 -0
  78. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/html_dashboard.py +0 -0
  79. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/index_generator.py +0 -0
  80. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/json_exporter.py +0 -0
  81. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/llm_exporter.py +0 -0
  82. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/map_exporter.py +0 -0
  83. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/mermaid_exporter.py +0 -0
  84. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
  85. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/project_yaml/__init__.py +0 -0
  86. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/project_yaml/constants.py +0 -0
  87. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/project_yaml/core.py +0 -0
  88. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/project_yaml/evolution.py +0 -0
  89. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/project_yaml/health.py +0 -0
  90. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/project_yaml/hotspots.py +0 -0
  91. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/project_yaml/modules.py +0 -0
  92. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/project_yaml_exporter.py +0 -0
  93. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/readme_exporter.py +0 -0
  94. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/report_generators.py +0 -0
  95. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/toon/__init__.py +0 -0
  96. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/toon/helpers.py +0 -0
  97. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/toon/metrics.py +0 -0
  98. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/toon/module_detail.py +0 -0
  99. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/toon/renderer.py +0 -0
  100. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/toon.py +0 -0
  101. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/toon_view.py +0 -0
  102. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/validate_project.py +0 -0
  103. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/exporters/yaml_exporter.py +0 -0
  104. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/generators/__init__.py +0 -0
  105. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/generators/llm_flow.py +0 -0
  106. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/generators/llm_task.py +0 -0
  107. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/generators/mermaid.py +0 -0
  108. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/nlp/config.py +0 -0
  109. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/nlp/entity_resolution.py +0 -0
  110. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/nlp/intent_matching.py +0 -0
  111. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/nlp/normalization.py +0 -0
  112. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/nlp/pipeline.py +0 -0
  113. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/patterns/__init__.py +0 -0
  114. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/patterns/detector.py +0 -0
  115. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/refactor/__init__.py +0 -0
  116. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm/refactor/prompt_engine.py +0 -0
  117. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm.egg-info/dependency_links.txt +0 -0
  118. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm.egg-info/entry_points.txt +0 -0
  119. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm.egg-info/requires.txt +0 -0
  120. {code2llm-0.5.112 → code2llm-0.5.114}/code2llm.egg-info/top_level.txt +0 -0
  121. {code2llm-0.5.112 → code2llm-0.5.114}/setup.cfg +0 -0
  122. {code2llm-0.5.112 → code2llm-0.5.114}/tests/test_advanced_analysis.py +0 -0
  123. {code2llm-0.5.112 → code2llm-0.5.114}/tests/test_analyzer.py +0 -0
  124. {code2llm-0.5.112 → code2llm-0.5.114}/tests/test_deep_analysis.py +0 -0
  125. {code2llm-0.5.112 → code2llm-0.5.114}/tests/test_edge_cases.py +0 -0
  126. {code2llm-0.5.112 → code2llm-0.5.114}/tests/test_flow_exporter.py +0 -0
  127. {code2llm-0.5.112 → code2llm-0.5.114}/tests/test_format_quality.py +0 -0
  128. {code2llm-0.5.112 → code2llm-0.5.114}/tests/test_multilanguage_e2e.py +0 -0
  129. {code2llm-0.5.112 → code2llm-0.5.114}/tests/test_nlp_pipeline.py +0 -0
  130. {code2llm-0.5.112 → code2llm-0.5.114}/tests/test_nonpython_cc_calls.py +0 -0
  131. {code2llm-0.5.112 → code2llm-0.5.114}/tests/test_pipeline_detector.py +0 -0
  132. {code2llm-0.5.112 → code2llm-0.5.114}/tests/test_project_toon_export.py +0 -0
  133. {code2llm-0.5.112 → code2llm-0.5.114}/tests/test_prompt_engine.py +0 -0
  134. {code2llm-0.5.112 → code2llm-0.5.114}/tests/test_prompt_txt.py +0 -0
  135. {code2llm-0.5.112 → code2llm-0.5.114}/tests/test_refactoring_engine.py +0 -0
  136. {code2llm-0.5.112 → code2llm-0.5.114}/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.112
3
+ Version: 0.5.114
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.112-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.114-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-55.0h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
72
72
 
73
73
  - 🤖 **LLM usage:** $7.5000 (162 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.112-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.114-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-55.0h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
8
8
 
9
9
  - 🤖 **LLM usage:** $7.5000 (162 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.112"
11
+ __version__ = "0.5.114"
12
12
  __author__ = "STTS Project"
13
13
 
14
14
  # Core analysis components (lightweight, always needed)
@@ -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.112"
7
+ __version__ = "0.5.114"
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.112
3
+ Version: 0.5.114
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.112-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.114-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-55.0h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
72
72
 
73
73
  - 🤖 **LLM usage:** $7.5000 (162 commits)
@@ -118,6 +118,7 @@ code2llm/refactor/__init__.py
118
118
  code2llm/refactor/prompt_engine.py
119
119
  tests/test_advanced_analysis.py
120
120
  tests/test_analyzer.py
121
+ tests/test_calls_toon_export.py
121
122
  tests/test_deep_analysis.py
122
123
  tests/test_edge_cases.py
123
124
  tests/test_flow_exporter.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code2llm"
7
- version = "0.5.112"
7
+ version = "0.5.114"
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.111"
7
+ version = "0.5.113"
8
8
 
9
9
  # Read long description
10
10
  def read_readme():
@@ -0,0 +1,241 @@
1
+ """Tests for calls.toon.yaml export functionality."""
2
+
3
+ from pathlib import Path
4
+ from unittest.mock import MagicMock
5
+
6
+ from code2llm.core.models import AnalysisResult, FunctionInfo, ClassInfo
7
+ from code2llm.exporters.yaml_exporter import YAMLExporter
8
+
9
+
10
+ def test_export_calls_toon_generates_file(tmp_path):
11
+ """Test that export_calls_toon generates a valid toon format file."""
12
+ # Create a mock result with some functions and calls
13
+ result = MagicMock(spec=AnalysisResult)
14
+ result.project_path = "."
15
+
16
+ # Add some mock functions
17
+ func1 = MagicMock(spec=FunctionInfo)
18
+ func1.name = "func1"
19
+ func1.module = "module1"
20
+ func1.line = 10
21
+ func1.complexity = {"cyclomatic_complexity": 5}
22
+ func1.calls = ["func2", "func3"]
23
+
24
+ func2 = MagicMock(spec=FunctionInfo)
25
+ func2.name = "func2"
26
+ func2.module = "module1"
27
+ func2.line = 20
28
+ func2.complexity = {"cyclomatic_complexity": 3}
29
+ func2.calls = []
30
+
31
+ func3 = MagicMock(spec=FunctionInfo)
32
+ func3.name = "func3"
33
+ func3.module = "module2"
34
+ func3.line = 30
35
+ func3.complexity = {"cyclomatic_complexity": 2}
36
+ func3.calls = []
37
+
38
+ result.functions = {
39
+ "module1.func1": func1,
40
+ "module1.func2": func2,
41
+ "module2.func3": func3,
42
+ }
43
+ result.classes = {}
44
+ result.entry_points = ["module1.func1"]
45
+
46
+ # Export
47
+ exporter = YAMLExporter()
48
+ output_path = tmp_path / "calls.toon.yaml"
49
+ exporter.export_calls_toon(result, str(output_path))
50
+
51
+ # Verify file exists
52
+ assert output_path.exists()
53
+
54
+ # Verify content
55
+ content = output_path.read_text(encoding="utf-8")
56
+ assert "# code2llm call graph" in content
57
+ assert "HUBS[" in content
58
+ assert "MODULES:" in content
59
+ assert "EDGES:" in content
60
+
61
+
62
+ def test_export_calls_toon_hubs_section(tmp_path):
63
+ """Test that HUBS section contains high-degree functions sorted by total calls."""
64
+ result = MagicMock(spec=AnalysisResult)
65
+ result.project_path = "."
66
+
67
+ # Create functions with different call patterns
68
+ func_hub = MagicMock(spec=FunctionInfo)
69
+ func_hub.name = "hub_func"
70
+ func_hub.module = "module1"
71
+ func_hub.line = 10
72
+ func_hub.complexity = {"cyclomatic_complexity": 10}
73
+ func_hub.calls = ["func2", "func3", "func4", "func5"] # 4 outgoing calls
74
+
75
+ func2 = MagicMock(spec=FunctionInfo)
76
+ func2.name = "func2"
77
+ func2.module = "module1"
78
+ func2.line = 20
79
+ func2.complexity = {"cyclomatic_complexity": 2}
80
+ func2.calls = []
81
+
82
+ func3 = MagicMock(spec=FunctionInfo)
83
+ func3.name = "func3"
84
+ func3.module = "module2"
85
+ func3.line = 30
86
+ func3.complexity = {"cyclomatic_complexity": 1}
87
+ func3.calls = []
88
+
89
+ func4 = MagicMock(spec=FunctionInfo)
90
+ func4.name = "func4"
91
+ func4.module = "module3"
92
+ func4.line = 40
93
+ func4.complexity = {"cyclomatic_complexity": 1}
94
+ func4.calls = []
95
+
96
+ func5 = MagicMock(spec=FunctionInfo)
97
+ func5.name = "func5"
98
+ func5.module = "module4"
99
+ func5.line = 50
100
+ func5.complexity = {"cyclomatic_complexity": 1}
101
+ func5.calls = []
102
+
103
+ result.functions = {
104
+ "module1.hub_func": func_hub,
105
+ "module1.func2": func2,
106
+ "module2.func3": func3,
107
+ "module3.func4": func4,
108
+ "module4.func5": func5,
109
+ }
110
+ result.classes = {}
111
+ result.entry_points = []
112
+
113
+ exporter = YAMLExporter()
114
+ output_path = tmp_path / "calls.toon.yaml"
115
+ exporter.export_calls_toon(result, str(output_path))
116
+
117
+ content = output_path.read_text(encoding="utf-8")
118
+ assert "HUBS[" in content
119
+ assert "hub_func" in content
120
+
121
+
122
+ def test_export_calls_toon_modules_section(tmp_path):
123
+ """Test that MODULES section groups functions by module."""
124
+ result = MagicMock(spec=AnalysisResult)
125
+ result.project_path = "."
126
+
127
+ func1 = MagicMock(spec=FunctionInfo)
128
+ func1.name = "func1"
129
+ func1.module = "module_a"
130
+ func1.line = 10
131
+ func1.complexity = {"cyclomatic_complexity": 3}
132
+ func1.calls = ["func2", "func3"]
133
+
134
+ func2 = MagicMock(spec=FunctionInfo)
135
+ func2.name = "func2"
136
+ func2.module = "module_a"
137
+ func2.line = 20
138
+ func2.complexity = {"cyclomatic_complexity": 2}
139
+ func2.calls = []
140
+
141
+ func3 = MagicMock(spec=FunctionInfo)
142
+ func3.name = "func3"
143
+ func3.module = "module_b"
144
+ func3.line = 30
145
+ func3.complexity = {"cyclomatic_complexity": 4}
146
+ func3.calls = []
147
+
148
+ result.functions = {
149
+ "module_a.func1": func1,
150
+ "module_a.func2": func2,
151
+ "module_b.func3": func3,
152
+ }
153
+ result.classes = {}
154
+ result.entry_points = []
155
+
156
+ exporter = YAMLExporter()
157
+ output_path = tmp_path / "calls.toon.yaml"
158
+ exporter.export_calls_toon(result, str(output_path))
159
+
160
+ content = output_path.read_text(encoding="utf-8")
161
+ assert "MODULES:" in content
162
+ assert "module_a" in content
163
+ assert "module_b" in content
164
+ assert "[2 funcs]" in content or "[1 func]" in content
165
+
166
+
167
+ def test_export_calls_toon_edges_section(tmp_path):
168
+ """Test that EDGES section contains caller -> callee relationships."""
169
+ result = MagicMock(spec=AnalysisResult)
170
+ result.project_path = "."
171
+
172
+ caller = MagicMock(spec=FunctionInfo)
173
+ caller.name = "caller"
174
+ caller.module = "module1"
175
+ caller.line = 10
176
+ caller.complexity = {"cyclomatic_complexity": 2}
177
+ caller.calls = ["callee"]
178
+
179
+ callee = MagicMock(spec=FunctionInfo)
180
+ callee.name = "callee"
181
+ callee.module = "module1"
182
+ callee.line = 20
183
+ callee.complexity = {"cyclomatic_complexity": 1}
184
+ callee.calls = []
185
+
186
+ result.functions = {
187
+ "module1.caller": caller,
188
+ "module1.callee": callee,
189
+ }
190
+ result.classes = {}
191
+ result.entry_points = []
192
+
193
+ exporter = YAMLExporter()
194
+ output_path = tmp_path / "calls.toon.yaml"
195
+ exporter.export_calls_toon(result, str(output_path))
196
+
197
+ content = output_path.read_text(encoding="utf-8")
198
+ assert "EDGES:" in content
199
+ assert "→" in content
200
+ assert "caller" in content
201
+ assert "callee" in content
202
+
203
+
204
+ def test_export_calls_toon_header_stats(tmp_path):
205
+ """Test that header contains correct statistics."""
206
+ result = MagicMock(spec=AnalysisResult)
207
+ result.project_path = "."
208
+
209
+ func1 = MagicMock(spec=FunctionInfo)
210
+ func1.name = "func1"
211
+ func1.module = "module1"
212
+ func1.line = 10
213
+ func1.complexity = {"cyclomatic_complexity": 5}
214
+ func1.calls = ["func2"]
215
+
216
+ func2 = MagicMock(spec=FunctionInfo)
217
+ func2.name = "func2"
218
+ func2.module = "module1"
219
+ func2.line = 20
220
+ func2.complexity = {"cyclomatic_complexity": 3}
221
+ func2.calls = []
222
+
223
+ result.functions = {
224
+ "module1.func1": func1,
225
+ "module1.func2": func2,
226
+ }
227
+ result.classes = {}
228
+ result.entry_points = []
229
+
230
+ exporter = YAMLExporter()
231
+ output_path = tmp_path / "calls.toon.yaml"
232
+ exporter.export_calls_toon(result, str(output_path))
233
+
234
+ content = output_path.read_text(encoding="utf-8")
235
+ # Check header lines
236
+ lines = content.split('\n')[:5]
237
+ assert any("# code2llm call graph" in line for line in lines)
238
+ assert any("nodes:" in line for line in lines)
239
+ assert any("edges:" in line for line in lines)
240
+ assert any("modules:" in line for line in lines)
241
+ assert any("CC̄=" in line for line in lines)
File without changes
File without changes
File without changes
File without changes