code2docs 0.1.2__tar.gz → 0.1.3__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 (46) hide show
  1. {code2docs-0.1.2 → code2docs-0.1.3}/PKG-INFO +2 -2
  2. {code2docs-0.1.2 → code2docs-0.1.3}/README.md +1 -1
  3. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/__init__.py +1 -1
  4. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/analyzers/docstring_extractor.py +31 -23
  5. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/analyzers/endpoint_detector.py +1 -1
  6. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/analyzers/project_scanner.py +2 -5
  7. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/cli.py +19 -16
  8. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/generators/api_reference_gen.py +61 -49
  9. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/generators/architecture_gen.py +1 -1
  10. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/generators/changelog_gen.py +1 -1
  11. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/generators/examples_gen.py +48 -44
  12. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/generators/module_docs_gen.py +90 -74
  13. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/generators/readme_gen.py +91 -60
  14. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs.egg-info/PKG-INFO +2 -2
  15. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs.egg-info/SOURCES.txt +1 -0
  16. {code2docs-0.1.2 → code2docs-0.1.3}/pyproject.toml +1 -1
  17. code2docs-0.1.3/tests/test_generators.py +366 -0
  18. {code2docs-0.1.2 → code2docs-0.1.3}/LICENSE +0 -0
  19. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/__main__.py +0 -0
  20. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/analyzers/__init__.py +0 -0
  21. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/analyzers/dependency_scanner.py +0 -0
  22. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/config.py +0 -0
  23. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/formatters/__init__.py +0 -0
  24. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/formatters/badges.py +0 -0
  25. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/formatters/markdown.py +0 -0
  26. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/formatters/toc.py +0 -0
  27. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/generators/__init__.py +0 -0
  28. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/sync/__init__.py +0 -0
  29. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/sync/differ.py +0 -0
  30. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/sync/updater.py +0 -0
  31. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/sync/watcher.py +0 -0
  32. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/templates/api_module.md.j2 +0 -0
  33. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/templates/architecture.md.j2 +0 -0
  34. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/templates/example_usage.py.j2 +0 -0
  35. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/templates/index.md.j2 +0 -0
  36. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs/templates/readme.md.j2 +0 -0
  37. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs.egg-info/dependency_links.txt +0 -0
  38. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs.egg-info/entry_points.txt +0 -0
  39. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs.egg-info/requires.txt +0 -0
  40. {code2docs-0.1.2 → code2docs-0.1.3}/code2docs.egg-info/top_level.txt +0 -0
  41. {code2docs-0.1.2 → code2docs-0.1.3}/setup.cfg +0 -0
  42. {code2docs-0.1.2 → code2docs-0.1.3}/tests/test_analyzers.py +0 -0
  43. {code2docs-0.1.2 → code2docs-0.1.3}/tests/test_code2docs.py +0 -0
  44. {code2docs-0.1.2 → code2docs-0.1.3}/tests/test_config.py +0 -0
  45. {code2docs-0.1.2 → code2docs-0.1.3}/tests/test_formatters.py +0 -0
  46. {code2docs-0.1.2 → code2docs-0.1.3}/tests/test_sync.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code2docs
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Auto-generate and sync project documentation from source code analysis
5
5
  Author-email: Tom Sapletta <tom@sapletta.com>
6
6
  License-Expression: Apache-2.0
@@ -40,7 +40,7 @@ Dynamic: license-file
40
40
 
41
41
  # code2docs
42
42
 
43
- ![version](https://img.shields.io/badge/version-0.1.2-blue) ![python](https://img.shields.io/badge/python-%3E%3D3.9-blue) ![docs](https://img.shields.io/badge/docs-auto--generated-blueviolet)
43
+ ![version](https://img.shields.io/badge/version-0.1.3-blue) ![python](https://img.shields.io/badge/python-%3E%3D3.9-blue) ![docs](https://img.shields.io/badge/docs-auto--generated-blueviolet)
44
44
 
45
45
  > Auto-generate and sync project documentation from source code analysis.
46
46
 
@@ -1,6 +1,6 @@
1
1
  # code2docs
2
2
 
3
- ![version](https://img.shields.io/badge/version-0.1.2-blue) ![python](https://img.shields.io/badge/python-%3E%3D3.9-blue) ![docs](https://img.shields.io/badge/docs-auto--generated-blueviolet)
3
+ ![version](https://img.shields.io/badge/version-0.1.3-blue) ![python](https://img.shields.io/badge/python-%3E%3D3.9-blue) ![docs](https://img.shields.io/badge/docs-auto--generated-blueviolet)
4
4
 
5
5
  > Auto-generate and sync project documentation from source code analysis.
6
6
 
@@ -5,7 +5,7 @@ Uses code2llm's AnalysisResult to produce human-readable documentation:
5
5
  README.md, API references, module docs, examples, and architecture diagrams.
6
6
  """
7
7
 
8
- __version__ = "0.1.2"
8
+ __version__ = "0.1.3"
9
9
  __author__ = "Tom Sapletta"
10
10
 
11
11
  from .config import Code2DocsConfig
@@ -5,7 +5,7 @@ from dataclasses import dataclass, field
5
5
  from pathlib import Path
6
6
  from typing import Dict, List, Optional, Tuple
7
7
 
8
- from code2llm.core.models import AnalysisResult, FunctionInfo, ClassInfo
8
+ from code2llm.api import AnalysisResult, FunctionInfo, ClassInfo
9
9
 
10
10
 
11
11
  @dataclass
@@ -38,44 +38,53 @@ class DocstringExtractor:
38
38
  return docs
39
39
 
40
40
  def parse(self, docstring: str) -> DocstringInfo:
41
- """Parse a docstring into structured sections."""
41
+ """Parse a docstring into structured sections (orchestrator)."""
42
42
  if not docstring:
43
43
  return DocstringInfo(raw="")
44
44
 
45
45
  lines = docstring.strip().splitlines()
46
46
  info = DocstringInfo(raw=docstring)
47
+ info.summary = self._extract_summary(lines)
48
+ self._parse_sections(lines[1:], info)
49
+ return info
47
50
 
48
- if lines:
49
- info.summary = lines[0].strip()
50
-
51
- # Simple parser for Google/Numpy/Sphinx styles
51
+ @staticmethod
52
+ def _extract_summary(lines: List[str]) -> str:
53
+ """Extract the first-line summary."""
54
+ return lines[0].strip() if lines else ""
55
+
56
+ @staticmethod
57
+ def _classify_section(line: str) -> Optional[str]:
58
+ """Classify a line as a section header, or return None."""
59
+ lower = line.strip().lower()
60
+ if lower.startswith(("args:", "parameters:", "params:")):
61
+ return "params"
62
+ if lower.startswith(("returns:", "return:")):
63
+ return "returns"
64
+ if lower.startswith(("raises:", "raise:")):
65
+ return "raises"
66
+ if lower.startswith(("example:", "examples:", ">>>")):
67
+ return "examples"
68
+ return None
69
+
70
+ def _parse_sections(self, lines: List[str], info: DocstringInfo) -> None:
71
+ """Walk remaining lines, dispatching content to the right section."""
52
72
  current_section = "description"
53
73
  desc_lines: List[str] = []
54
- param_lines: List[Tuple[str, str]] = []
55
74
 
56
- for line in lines[1:]:
75
+ for line in lines:
57
76
  stripped = line.strip()
58
- lower = stripped.lower()
59
77
 
60
- if lower.startswith(("args:", "parameters:", "params:")):
61
- current_section = "params"
62
- continue
63
- elif lower.startswith(("returns:", "return:")):
64
- current_section = "returns"
65
- continue
66
- elif lower.startswith(("raises:", "raise:")):
67
- current_section = "raises"
68
- continue
69
- elif lower.startswith(("example:", "examples:", ">>>")):
70
- current_section = "examples"
71
- if stripped.startswith(">>>"):
78
+ new_section = self._classify_section(stripped)
79
+ if new_section is not None:
80
+ current_section = new_section
81
+ if current_section == "examples" and stripped.startswith(">>>"):
72
82
  info.examples.append(stripped)
73
83
  continue
74
84
 
75
85
  if current_section == "description":
76
86
  desc_lines.append(stripped)
77
87
  elif current_section == "params" and stripped:
78
- # Parse "name: description" or "name (type): description"
79
88
  if ":" in stripped:
80
89
  pname, pdesc = stripped.split(":", 1)
81
90
  info.params[pname.strip()] = pdesc.strip()
@@ -87,7 +96,6 @@ class DocstringExtractor:
87
96
  info.examples.append(stripped)
88
97
 
89
98
  info.description = "\n".join(desc_lines).strip()
90
- return info
91
99
 
92
100
  def coverage_report(self, result: AnalysisResult) -> Dict[str, float]:
93
101
  """Calculate docstring coverage statistics."""
@@ -6,7 +6,7 @@ from dataclasses import dataclass, field
6
6
  from pathlib import Path
7
7
  from typing import Dict, List, Optional
8
8
 
9
- from code2llm.core.models import AnalysisResult, FunctionInfo
9
+ from code2llm.api import AnalysisResult, FunctionInfo
10
10
 
11
11
 
12
12
  @dataclass
@@ -3,9 +3,7 @@
3
3
  from pathlib import Path
4
4
  from typing import Optional
5
5
 
6
- from code2llm import Config, FAST_CONFIG
7
- from code2llm.core.analyzer import ProjectAnalyzer
8
- from code2llm.core.models import AnalysisResult
6
+ from code2llm.api import Config, FAST_CONFIG, AnalysisResult, analyze
9
7
 
10
8
  from ..config import Code2DocsConfig
11
9
 
@@ -35,8 +33,7 @@ class ProjectScanner:
35
33
 
36
34
  def analyze(self, project_path: str) -> AnalysisResult:
37
35
  """Analyze a project and return AnalysisResult for doc generation."""
38
- analyzer = ProjectAnalyzer(self._llm_config)
39
- return analyzer.analyze_project(project_path)
36
+ return analyze(project_path, self._llm_config)
40
37
 
41
38
 
42
39
  def analyze_and_document(project_path: str, config: Optional[Code2DocsConfig] = None) -> AnalysisResult:
@@ -9,7 +9,23 @@ import click
9
9
  from .config import Code2DocsConfig
10
10
 
11
11
 
12
- @click.group(invoke_without_command=True)
12
+ class DefaultGroup(click.Group):
13
+ """Click Group that routes unknown subcommands to 'generate'."""
14
+
15
+ def parse_args(self, ctx, args):
16
+ if not args:
17
+ args = ["generate"]
18
+ elif args[0] not in self.commands and args[0] not in ("--help", "-h"):
19
+ args = ["generate"] + args
20
+ return super().parse_args(ctx, args)
21
+
22
+
23
+ @click.group(cls=DefaultGroup)
24
+ def main():
25
+ """code2docs — Auto-generate project documentation from source code."""
26
+
27
+
28
+ @main.command()
13
29
  @click.argument("project_path", default=".", type=click.Path(exists=True))
14
30
  @click.option("--config", "-c", "config_path", default=None, help="Path to code2docs.yaml")
15
31
  @click.option("--readme-only", is_flag=True, help="Generate only README.md")
@@ -17,21 +33,8 @@ from .config import Code2DocsConfig
17
33
  @click.option("--output", "-o", default=None, help="Output directory for docs")
18
34
  @click.option("--verbose", "-v", is_flag=True, help="Verbose output")
19
35
  @click.option("--dry-run", is_flag=True, help="Show what would be generated without writing")
20
- @click.pass_context
21
- def main(ctx, project_path, config_path, readme_only, sections, output, verbose, dry_run):
22
- """code2docs — Auto-generate project documentation from source code.
23
-
24
- Analyzes PROJECT_PATH using code2llm and generates human-readable documentation.
25
- """
26
- if ctx.invoked_subcommand is not None:
27
- ctx.ensure_object(dict)
28
- ctx.obj["project_path"] = project_path
29
- ctx.obj["config_path"] = config_path
30
- ctx.obj["verbose"] = verbose
31
- ctx.obj["dry_run"] = dry_run
32
- return
33
-
34
- # Default action: full generation
36
+ def generate(project_path, config_path, readme_only, sections, output, verbose, dry_run):
37
+ """Generate documentation (default command)."""
35
38
  config = _load_config(project_path, config_path)
36
39
  if verbose:
37
40
  config.verbose = True
@@ -5,7 +5,7 @@ from typing import Dict, List, Optional
5
5
 
6
6
  from jinja2 import Environment, PackageLoader, select_autoescape
7
7
 
8
- from code2llm.core.models import AnalysisResult, FunctionInfo, ClassInfo, ModuleInfo
8
+ from code2llm.api import AnalysisResult, FunctionInfo, ClassInfo, ModuleInfo
9
9
 
10
10
  from ..config import Code2DocsConfig
11
11
 
@@ -61,67 +61,79 @@ class ApiReferenceGenerator:
61
61
  return "\n".join(lines) + "\n"
62
62
 
63
63
  def _generate_module_api(self, mod_name: str, mod_info: ModuleInfo) -> str:
64
- """Generate API reference for a single module."""
65
- lines = [f"# `{mod_name}`\n"]
64
+ """Generate API reference for a single module (orchestrator)."""
65
+ parts: List[str] = [
66
+ self._render_api_header(mod_name, mod_info),
67
+ self._render_api_classes(mod_name),
68
+ self._render_api_functions(mod_name),
69
+ self._render_api_imports(mod_info),
70
+ ]
71
+ return "\n".join(p for p in parts if p)
66
72
 
67
- # Source info
68
- lines.append(f"> Source: `{mod_info.file}`\n")
73
+ def _render_api_header(self, mod_name: str, mod_info: ModuleInfo) -> str:
74
+ """Render module header with source info."""
75
+ return f"# `{mod_name}`\n\n> Source: `{mod_info.file}`\n"
69
76
 
70
- # Classes in this module
77
+ def _render_api_classes(self, mod_name: str) -> str:
78
+ """Render classes with their method signatures."""
71
79
  module_classes = {
72
80
  k: v for k, v in self.result.classes.items()
73
81
  if v.module == mod_name or k.startswith(mod_name + ".")
74
82
  }
75
- if module_classes:
76
- lines.append("## Classes\n")
77
- for cls_name, cls_info in sorted(module_classes.items()):
78
- lines.append(f"### `{cls_info.name}`\n")
79
- if cls_info.bases:
80
- lines.append(f"Inherits from: {', '.join(f'`{b}`' for b in cls_info.bases)}\n")
81
- if cls_info.docstring:
82
- lines.append(f"{cls_info.docstring.strip()}\n")
83
-
84
- # Methods of this class
85
- methods = self._get_class_methods(cls_info)
86
- if methods:
87
- lines.append("#### Methods\n")
88
- for method in methods:
89
- sig = self._format_signature(method)
90
- doc_line = ""
91
- if method.docstring:
92
- doc_line = f" — {method.docstring.splitlines()[0]}"
93
- cc = method.complexity.get("cyclomatic", 0)
94
- cc_badge = f" ⚠️ CC={cc}" if cc > 10 else ""
95
- lines.append(f"- `{sig}`{doc_line}{cc_badge}")
96
- lines.append("")
97
-
98
- # Standalone functions in this module
83
+ if not module_classes:
84
+ return ""
85
+ lines = ["## Classes\n"]
86
+ for cls_name, cls_info in sorted(module_classes.items()):
87
+ lines.append(f"### `{cls_info.name}`\n")
88
+ if cls_info.bases:
89
+ lines.append(f"Inherits from: {', '.join(f'`{b}`' for b in cls_info.bases)}\n")
90
+ if cls_info.docstring:
91
+ lines.append(f"{cls_info.docstring.strip()}\n")
92
+ methods = self._get_class_methods(cls_info)
93
+ if methods:
94
+ lines.append("#### Methods\n")
95
+ for method in methods:
96
+ sig = self._format_signature(method)
97
+ doc_line = ""
98
+ if method.docstring:
99
+ doc_line = f" — {method.docstring.splitlines()[0]}"
100
+ cc = method.complexity.get("cyclomatic", 0)
101
+ cc_badge = f" ⚠️ CC={cc}" if cc > 10 else ""
102
+ lines.append(f"- `{sig}`{doc_line}{cc_badge}")
103
+ lines.append("")
104
+ return "\n".join(lines)
105
+
106
+ def _render_api_functions(self, mod_name: str) -> str:
107
+ """Render standalone functions with signatures and complexity."""
99
108
  module_functions = {
100
109
  k: v for k, v in self.result.functions.items()
101
110
  if (v.module == mod_name or k.startswith(mod_name + "."))
102
111
  and not v.is_method
103
112
  }
104
- if module_functions:
105
- lines.append("## Functions\n")
106
- for func_name, func_info in sorted(module_functions.items()):
107
- sig = self._format_signature(func_info)
108
- lines.append(f"### `{sig}`\n")
109
- if func_info.docstring:
110
- lines.append(f"{func_info.docstring.strip()}\n")
111
- cc = func_info.complexity.get("cyclomatic", 0)
112
- if cc:
113
- lines.append(f"- Complexity: {cc}")
114
- if func_info.calls:
115
- lines.append(f"- Calls: {', '.join(f'`{c}`' for c in func_info.calls[:10])}")
116
- lines.append("")
117
-
118
- # Imports
119
- if mod_info.imports:
120
- lines.append("## Imports\n")
121
- for imp in sorted(mod_info.imports):
122
- lines.append(f"- `{imp}`")
113
+ if not module_functions:
114
+ return ""
115
+ lines = ["## Functions\n"]
116
+ for func_name, func_info in sorted(module_functions.items()):
117
+ sig = self._format_signature(func_info)
118
+ lines.append(f"### `{sig}`\n")
119
+ if func_info.docstring:
120
+ lines.append(f"{func_info.docstring.strip()}\n")
121
+ cc = func_info.complexity.get("cyclomatic", 0)
122
+ if cc:
123
+ lines.append(f"- Complexity: {cc}")
124
+ if func_info.calls:
125
+ lines.append(f"- Calls: {', '.join(f'`{c}`' for c in func_info.calls[:10])}")
123
126
  lines.append("")
127
+ return "\n".join(lines)
124
128
 
129
+ def _render_api_imports(self, mod_info: ModuleInfo) -> str:
130
+ """Render module imports list."""
131
+ if not mod_info.imports:
132
+ return ""
133
+ lines = ["## Imports\n"]
134
+ for imp in sorted(mod_info.imports):
135
+ lines.append(f"- `{imp}`")
136
+ lines.append("")
125
137
  return "\n".join(lines)
126
138
 
127
139
  def _get_class_methods(self, cls_info: ClassInfo) -> List[FunctionInfo]:
@@ -3,7 +3,7 @@
3
3
  from pathlib import Path
4
4
  from typing import Dict, List, Set
5
5
 
6
- from code2llm.core.models import AnalysisResult, ModuleInfo
6
+ from code2llm.api import AnalysisResult, ModuleInfo
7
7
 
8
8
  from ..config import Code2DocsConfig
9
9
 
@@ -5,7 +5,7 @@ from dataclasses import dataclass, field
5
5
  from pathlib import Path
6
6
  from typing import Dict, List, Optional
7
7
 
8
- from code2llm.core.models import AnalysisResult
8
+ from code2llm.api import AnalysisResult
9
9
 
10
10
  from ..config import Code2DocsConfig
11
11
 
@@ -3,7 +3,7 @@
3
3
  from pathlib import Path
4
4
  from typing import Dict, List
5
5
 
6
- from code2llm.core.models import AnalysisResult, FunctionInfo, ClassInfo
6
+ from code2llm.api import AnalysisResult, FunctionInfo, ClassInfo
7
7
 
8
8
  from ..config import Code2DocsConfig
9
9
 
@@ -34,70 +34,74 @@ class ExamplesGenerator:
34
34
  return files
35
35
 
36
36
  def _generate_basic_usage(self) -> str:
37
- """Generate basic_usage.py example."""
37
+ """Generate basic_usage.py example (orchestrator)."""
38
38
  project_name = self.config.project_name or Path(self.result.project_path).name
39
- lines: List[str] = [
40
- f'"""Basic usage of {project_name}."""',
41
- "",
42
- ]
43
-
44
- # Find importable public classes and functions
45
- public_classes = [
46
- c for c in self.result.classes.values()
47
- if not c.name.startswith("_")
48
- ]
39
+ public_classes = [c for c in self.result.classes.values() if not c.name.startswith("_")]
49
40
  public_functions = [
50
41
  f for f in self.result.functions.values()
51
42
  if not f.is_private and not f.is_method and not f.name.startswith("_")
52
43
  ]
53
44
 
54
- # Generate import statements
45
+ sections = [
46
+ f'"""Basic usage of {project_name}."""',
47
+ "",
48
+ self._generate_import_section(project_name, public_classes, public_functions),
49
+ "",
50
+ self._generate_class_usage_section(public_classes),
51
+ self._generate_function_usage_section(public_functions),
52
+ "",
53
+ ]
54
+ return "\n".join(s for s in sections if s is not None)
55
+
56
+ def _generate_import_section(self, project_name: str,
57
+ public_classes: List[ClassInfo],
58
+ public_functions: List[FunctionInfo]) -> str:
59
+ """Generate import statements for the example."""
60
+ lines: List[str] = []
55
61
  imported = set()
56
62
  for cls in public_classes[:5]:
57
63
  mod = cls.module or project_name
58
64
  lines.append(f"from {mod} import {cls.name}")
59
65
  imported.add(cls.name)
60
-
61
66
  for func in public_functions[:5]:
62
67
  if func.name not in imported:
63
68
  mod = func.module or project_name
64
69
  lines.append(f"from {mod} import {func.name}")
65
70
  imported.add(func.name)
66
-
67
71
  if not imported:
68
72
  lines.append(f"import {project_name}")
73
+ return "\n".join(lines)
69
74
 
75
+ def _generate_class_usage_section(self, public_classes: List[ClassInfo]) -> str:
76
+ """Generate class instantiation and method call examples."""
77
+ if not public_classes:
78
+ return ""
79
+ lines: List[str] = []
80
+ cls = public_classes[0]
81
+ lines.append(f"# Create {cls.name} instance")
82
+ init_args = self._get_init_args(cls)
83
+ if init_args:
84
+ args_str = ", ".join(f"{a}=..." for a in init_args[:3])
85
+ lines.append(f"obj = {cls.name}({args_str})")
86
+ else:
87
+ lines.append(f"obj = {cls.name}()")
70
88
  lines.append("")
71
- lines.append("")
72
-
73
- # Generate usage examples
74
- if public_classes:
75
- cls = public_classes[0]
76
- lines.append(f"# Create {cls.name} instance")
77
- init_args = self._get_init_args(cls)
78
- if init_args:
79
- args_str = ", ".join(f"{a}=..." for a in init_args[:3])
80
- lines.append(f"obj = {cls.name}({args_str})")
81
- else:
82
- lines.append(f"obj = {cls.name}()")
83
- lines.append("")
84
-
85
- # Show method calls
86
- methods = self._get_public_methods(cls)
87
- for method in methods[:3]:
88
- args_str = ", ".join(f"{a}=..." for a in method.args if a != "self")
89
- ret_comment = f" # → {method.returns}" if method.returns else ""
90
- lines.append(f"result = obj.{method.name}({args_str}){ret_comment}")
91
-
92
- if public_functions:
93
- lines.append("")
94
- lines.append("# Standalone functions")
95
- for func in public_functions[:5]:
96
- args_str = ", ".join(f"{a}=..." for a in func.args[:4])
97
- ret_comment = f" # → {func.returns}" if func.returns else ""
98
- lines.append(f"result = {func.name}({args_str}){ret_comment}")
89
+ methods = self._get_public_methods(cls)
90
+ for method in methods[:3]:
91
+ args_str = ", ".join(f"{a}=..." for a in method.args if a != "self")
92
+ ret_comment = f" # → {method.returns}" if method.returns else ""
93
+ lines.append(f"result = obj.{method.name}({args_str}){ret_comment}")
94
+ return "\n".join(lines)
99
95
 
100
- lines.append("")
96
+ def _generate_function_usage_section(self, public_functions: List[FunctionInfo]) -> str:
97
+ """Generate standalone function call examples."""
98
+ if not public_functions:
99
+ return ""
100
+ lines = ["", "# Standalone functions"]
101
+ for func in public_functions[:5]:
102
+ args_str = ", ".join(f"{a}=..." for a in func.args[:4])
103
+ ret_comment = f" # → {func.returns}" if func.returns else ""
104
+ lines.append(f"result = {func.name}({args_str}){ret_comment}")
101
105
  return "\n".join(lines)
102
106
 
103
107
  def _generate_entry_point_examples(self) -> str: