code2docs 3.0.2__tar.gz → 3.0.4__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.
- {code2docs-3.0.2 → code2docs-3.0.4}/PKG-INFO +3 -3
- {code2docs-3.0.2 → code2docs-3.0.4}/README.md +2 -2
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/__init__.py +1 -1
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/analyzers/dependency_scanner.py +38 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/generators/_registry_adapters.py +10 -4
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/generators/contributing_gen.py +2 -1
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/generators/examples_gen.py +59 -15
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/generators/getting_started_gen.py +2 -1
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/generators/mkdocs_gen.py +44 -4
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs.egg-info/PKG-INFO +3 -3
- {code2docs-3.0.2 → code2docs-3.0.4}/pyproject.toml +1 -1
- {code2docs-3.0.2 → code2docs-3.0.4}/LICENSE +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/__main__.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/analyzers/__init__.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/analyzers/docstring_extractor.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/analyzers/endpoint_detector.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/analyzers/project_scanner.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/base.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/cli.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/config.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/examples/advanced_usage.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/examples/quickstart.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/formatters/__init__.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/formatters/badges.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/formatters/markdown.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/formatters/toc.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/generators/__init__.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/generators/_source_links.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/generators/api_changelog_gen.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/generators/api_reference_gen.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/generators/architecture_gen.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/generators/changelog_gen.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/generators/code2llm_gen.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/generators/config_docs_gen.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/generators/coverage_gen.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/generators/depgraph_gen.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/generators/module_docs_gen.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/generators/readme_gen.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/llm_helper.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/registry.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/sync/__init__.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/sync/differ.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/sync/updater.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/sync/watcher.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/templates/api_module.md.j2 +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/templates/architecture.md.j2 +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/templates/example_usage.py.j2 +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/templates/index.md.j2 +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/templates/module_doc.md.j2 +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs/templates/readme.md.j2 +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs.egg-info/SOURCES.txt +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs.egg-info/dependency_links.txt +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs.egg-info/entry_points.txt +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs.egg-info/requires.txt +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/code2docs.egg-info/top_level.txt +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/setup.cfg +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/tests/test_analyzers.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/tests/test_cli.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/tests/test_code2docs.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/tests/test_config.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/tests/test_formatters.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/tests/test_generators.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/tests/test_llm_helper.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/tests/test_registry.py +0 -0
- {code2docs-3.0.2 → code2docs-3.0.4}/tests/test_sync.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code2docs
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.4
|
|
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
|
|
@@ -50,7 +50,7 @@ Dynamic: license-file
|
|
|
50
50
|
|
|
51
51
|
# code2docs
|
|
52
52
|
|
|
53
|
-
  
|
|
54
54
|
|
|
55
55
|
> Auto-generate and sync project documentation from source code analysis.
|
|
56
56
|
|
|
@@ -190,7 +190,7 @@ code2docs can update only specific sections of an existing README using markers:
|
|
|
190
190
|
```markdown
|
|
191
191
|
<!-- code2docs:start --># code2docs
|
|
192
192
|
|
|
193
|
-
   
|
|
194
194
|
> **276** functions | **57** classes | **51** files | CC̄ = 3.8
|
|
195
195
|
|
|
196
196
|
> Auto-generated project documentation from source code analysis.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# code2docs
|
|
2
2
|
|
|
3
|
-
  
|
|
4
4
|
|
|
5
5
|
> Auto-generate and sync project documentation from source code analysis.
|
|
6
6
|
|
|
@@ -140,7 +140,7 @@ code2docs can update only specific sections of an existing README using markers:
|
|
|
140
140
|
```markdown
|
|
141
141
|
<!-- code2docs:start --># code2docs
|
|
142
142
|
|
|
143
|
-
   
|
|
144
144
|
> **276** functions | **57** classes | **51** files | CC̄ = 3.8
|
|
145
145
|
|
|
146
146
|
> Auto-generated project documentation from source code analysis.
|
|
@@ -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__ = "3.0.
|
|
8
|
+
__version__ = "3.0.4"
|
|
9
9
|
__author__ = "Tom Sapletta"
|
|
10
10
|
|
|
11
11
|
from .config import Code2DocsConfig
|
|
@@ -32,6 +32,11 @@ class ProjectDependencies:
|
|
|
32
32
|
optional_groups: Dict[str, List[DependencyInfo]] = field(default_factory=dict)
|
|
33
33
|
install_command: str = "pip install ."
|
|
34
34
|
source_file: str = ""
|
|
35
|
+
# Additional metadata from pyproject.toml
|
|
36
|
+
keywords: List[str] = field(default_factory=list)
|
|
37
|
+
classifiers: List[str] = field(default_factory=list)
|
|
38
|
+
urls: Dict[str, str] = field(default_factory=dict)
|
|
39
|
+
version: str = ""
|
|
35
40
|
|
|
36
41
|
|
|
37
42
|
class DependencyScanner:
|
|
@@ -76,6 +81,10 @@ class DependencyScanner:
|
|
|
76
81
|
|
|
77
82
|
project = data.get("project", {})
|
|
78
83
|
deps.python_version = project.get("requires-python", "")
|
|
84
|
+
deps.version = project.get("version", "")
|
|
85
|
+
deps.keywords = project.get("keywords", [])
|
|
86
|
+
deps.classifiers = project.get("classifiers", [])
|
|
87
|
+
deps.urls = project.get("urls", {})
|
|
79
88
|
|
|
80
89
|
# Main dependencies
|
|
81
90
|
for dep_str in project.get("dependencies", []):
|
|
@@ -96,6 +105,9 @@ class DependencyScanner:
|
|
|
96
105
|
if name:
|
|
97
106
|
deps.install_command = f"pip install {name}"
|
|
98
107
|
|
|
108
|
+
# Detect version with fallback to git tags or VERSION file
|
|
109
|
+
deps.version = self._detect_version(path.parent, deps.version)
|
|
110
|
+
|
|
99
111
|
return deps
|
|
100
112
|
|
|
101
113
|
def _parse_pyproject_regex(self, path: Path) -> ProjectDependencies:
|
|
@@ -157,3 +169,29 @@ class DependencyScanner:
|
|
|
157
169
|
version_spec=match.group(2).strip(),
|
|
158
170
|
)
|
|
159
171
|
return DependencyInfo(name=dep_str.strip())
|
|
172
|
+
|
|
173
|
+
def _detect_version(self, project_path: Path, pyproject_version: str = "") -> str:
|
|
174
|
+
"""Detect version from pyproject.toml, git tags, or VERSION file."""
|
|
175
|
+
# Priority 1: pyproject.toml version
|
|
176
|
+
if pyproject_version:
|
|
177
|
+
return pyproject_version
|
|
178
|
+
|
|
179
|
+
# Priority 2: VERSION file
|
|
180
|
+
version_file = project_path / "VERSION"
|
|
181
|
+
if version_file.exists():
|
|
182
|
+
return version_file.read_text(encoding="utf-8").strip()
|
|
183
|
+
|
|
184
|
+
# Priority 3: git tags (latest tag)
|
|
185
|
+
try:
|
|
186
|
+
import subprocess
|
|
187
|
+
result = subprocess.run(
|
|
188
|
+
["git", "describe", "--tags", "--abbrev=0"],
|
|
189
|
+
cwd=str(project_path),
|
|
190
|
+
capture_output=True, text=True, timeout=5,
|
|
191
|
+
)
|
|
192
|
+
if result.returncode == 0:
|
|
193
|
+
return result.stdout.strip().lstrip("v")
|
|
194
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
195
|
+
pass
|
|
196
|
+
|
|
197
|
+
return ""
|
|
@@ -25,7 +25,12 @@ class ReadmeGeneratorAdapter(BaseGenerator):
|
|
|
25
25
|
preview = content[:500] + "..." if len(content) > 500 else content
|
|
26
26
|
click.echo(preview)
|
|
27
27
|
return None
|
|
28
|
-
|
|
28
|
+
# Use docs_dir if readme_output is a simple filename, otherwise respect the path
|
|
29
|
+
readme_output = Path(self.config.readme_output)
|
|
30
|
+
if readme_output.parent == Path("."):
|
|
31
|
+
readme_path = ctx.docs_dir / readme_output.name
|
|
32
|
+
else:
|
|
33
|
+
readme_path = ctx.project / readme_output
|
|
29
34
|
gen.write(str(readme_path), content)
|
|
30
35
|
return f"✅ {readme_path.relative_to(ctx.project)}"
|
|
31
36
|
|
|
@@ -141,7 +146,7 @@ class ExamplesAdapter(BaseGenerator):
|
|
|
141
146
|
files = gen.generate_all()
|
|
142
147
|
if ctx.dry_run:
|
|
143
148
|
return f"[dry-run] examples/ ({len(files)} files)"
|
|
144
|
-
examples_dir = ctx.
|
|
149
|
+
examples_dir = ctx.docs_dir / "examples"
|
|
145
150
|
gen.write_all(str(examples_dir), files)
|
|
146
151
|
return f"✅ examples/ ({len(files)} files)"
|
|
147
152
|
|
|
@@ -158,7 +163,7 @@ class MkDocsAdapter(BaseGenerator):
|
|
|
158
163
|
content = gen.generate(str(ctx.docs_dir))
|
|
159
164
|
if ctx.dry_run:
|
|
160
165
|
return "[dry-run] mkdocs.yml"
|
|
161
|
-
gen.write(str(ctx.
|
|
166
|
+
gen.write(str(ctx.docs_dir / "mkdocs.yml"), content)
|
|
162
167
|
return "✅ mkdocs.yml"
|
|
163
168
|
|
|
164
169
|
|
|
@@ -208,7 +213,8 @@ class ContributingAdapter(BaseGenerator):
|
|
|
208
213
|
content = gen.generate()
|
|
209
214
|
if ctx.dry_run:
|
|
210
215
|
return "[dry-run] CONTRIBUTING.md"
|
|
211
|
-
|
|
216
|
+
ctx.docs_dir.mkdir(parents=True, exist_ok=True)
|
|
217
|
+
(ctx.docs_dir / "CONTRIBUTING.md").write_text(content, encoding="utf-8")
|
|
212
218
|
return "✅ CONTRIBUTING.md"
|
|
213
219
|
|
|
214
220
|
|
|
@@ -54,10 +54,11 @@ class ContributingGenerator:
|
|
|
54
54
|
def _render_setup(self, tools: Dict[str, bool]) -> str:
|
|
55
55
|
"""Render development setup instructions."""
|
|
56
56
|
project = self.config.project_name or "project"
|
|
57
|
+
repo_url = self.config.repo_url or "<repository-url>"
|
|
57
58
|
lines = [
|
|
58
59
|
"## Development Setup\n",
|
|
59
60
|
"```bash",
|
|
60
|
-
f"git clone
|
|
61
|
+
f"git clone {repo_url}",
|
|
61
62
|
f"cd {project}",
|
|
62
63
|
"python -m venv .venv",
|
|
63
64
|
"source .venv/bin/activate # or .venv\\Scripts\\activate on Windows",
|
|
@@ -59,6 +59,44 @@ class ExamplesGenerator:
|
|
|
59
59
|
self.result = result
|
|
60
60
|
self._pkg = self._detect_package_name()
|
|
61
61
|
|
|
62
|
+
def _get_example_value(self, arg_name: str) -> str:
|
|
63
|
+
"""Get realistic example value based on actual project config."""
|
|
64
|
+
project_name = self.config.project_name or Path(self.result.project_path).name
|
|
65
|
+
|
|
66
|
+
# Map argument names to actual config values
|
|
67
|
+
arg_mappings = {
|
|
68
|
+
"project_path": f'"./{project_name}"',
|
|
69
|
+
"path": f'"./{project_name}"',
|
|
70
|
+
"source": f'"{self.config.source}"' if self.config.source else '"./src"',
|
|
71
|
+
"output": f'"{self.config.output}"' if self.config.output else '"./docs"',
|
|
72
|
+
"output_dir": f'"{self.config.output}"' if self.config.output else '"./docs"',
|
|
73
|
+
"output_path": f'"{self.config.readme_output}"' if self.config.readme_output else '"./docs/README.md"',
|
|
74
|
+
"config": "config",
|
|
75
|
+
"config_path": '"code2docs.yaml"',
|
|
76
|
+
"result": "result",
|
|
77
|
+
"name": f'"{project_name}"',
|
|
78
|
+
"project_name": f'"{project_name}"',
|
|
79
|
+
"verbose": "True",
|
|
80
|
+
"dry_run": "False",
|
|
81
|
+
"readme_only": "False",
|
|
82
|
+
"sections": '["overview", "install", "quickstart"]',
|
|
83
|
+
"content": '"# My Doc\\n## Section"',
|
|
84
|
+
"markdown_content": '"# My Doc\\n## Section"',
|
|
85
|
+
"max_depth": "3",
|
|
86
|
+
"target": "80",
|
|
87
|
+
"badge_types": '["version", "python"]',
|
|
88
|
+
"stats": "{}",
|
|
89
|
+
"deps": "[]",
|
|
90
|
+
"sync_markers": "True",
|
|
91
|
+
"docstring": '"""My function docstring."""',
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if arg_name in arg_mappings:
|
|
95
|
+
return arg_mappings[arg_name]
|
|
96
|
+
|
|
97
|
+
# Fallback to original static examples
|
|
98
|
+
return _ARG_EXAMPLES.get(arg_name, '"..."')
|
|
99
|
+
|
|
62
100
|
def generate_all(self) -> Dict[str, str]:
|
|
63
101
|
"""Generate all example files. Returns {filename: content}."""
|
|
64
102
|
files: Dict[str, str] = {}
|
|
@@ -100,15 +138,18 @@ class ExamplesGenerator:
|
|
|
100
138
|
|
|
101
139
|
# --- Example 1: Config (define first so later examples can use it) ---
|
|
102
140
|
config_cls = self._find_class_by_name("Code2DocsConfig")
|
|
141
|
+
project_name = self.config.project_name or Path(self.result.project_path).name
|
|
142
|
+
source = self.config.source or "./"
|
|
143
|
+
output = self.config.output or "./docs"
|
|
103
144
|
if config_cls:
|
|
104
145
|
lines.append('# ' + '=' * 50)
|
|
105
146
|
lines.append("# Example 1: Configuration")
|
|
106
147
|
lines.append('# ' + '=' * 50)
|
|
107
148
|
lines.append("")
|
|
108
149
|
lines.append(f"config = {config_cls.name}(")
|
|
109
|
-
lines.append(' project_name="
|
|
110
|
-
lines.append(' source="
|
|
111
|
-
lines.append(' output="
|
|
150
|
+
lines.append(f' project_name="{project_name}",')
|
|
151
|
+
lines.append(f' source="{source}",')
|
|
152
|
+
lines.append(f' output="{output}",')
|
|
112
153
|
lines.append(" verbose=True,")
|
|
113
154
|
lines.append(")")
|
|
114
155
|
lines.append("")
|
|
@@ -119,20 +160,22 @@ class ExamplesGenerator:
|
|
|
119
160
|
lines.append("# Example 2: Generate documentation")
|
|
120
161
|
lines.append('# ' + '=' * 50)
|
|
121
162
|
lines.append("")
|
|
163
|
+
|
|
164
|
+
project_path = f'"./{project_name}"' if project_name != "." else '"./"'
|
|
122
165
|
|
|
123
166
|
gen_func = self._find_function_by_name("generate_readme")
|
|
124
167
|
if gen_func:
|
|
125
168
|
lines.append("# Generate a README for your project")
|
|
126
|
-
lines.append('generate_readme(
|
|
169
|
+
lines.append(f'generate_readme({project_path}, output="README.md")')
|
|
127
170
|
lines.append("")
|
|
128
171
|
|
|
129
172
|
docs_func = self._find_function_by_name("generate_docs")
|
|
130
173
|
if docs_func:
|
|
131
174
|
lines.append("# Generate all documentation")
|
|
132
175
|
if config_cls:
|
|
133
|
-
lines.append('docs = generate_docs(
|
|
176
|
+
lines.append(f'docs = generate_docs({project_path}, config=config)')
|
|
134
177
|
else:
|
|
135
|
-
lines.append('docs = generate_docs(
|
|
178
|
+
lines.append(f'docs = generate_docs({project_path})')
|
|
136
179
|
lines.append('print(f"Generated {len(docs)} documentation sections")')
|
|
137
180
|
lines.append("")
|
|
138
181
|
|
|
@@ -147,7 +190,7 @@ class ExamplesGenerator:
|
|
|
147
190
|
lines.append(f"from {pkg}.analyzers.project_scanner import ProjectScanner")
|
|
148
191
|
lines.append("")
|
|
149
192
|
lines.append("scanner = ProjectScanner(config)")
|
|
150
|
-
lines.append('result = scanner.analyze(
|
|
193
|
+
lines.append(f'result = scanner.analyze({project_path})')
|
|
151
194
|
lines.append("")
|
|
152
195
|
lines.append('print(f"Found {len(result.functions)} functions")')
|
|
153
196
|
lines.append('print(f"Found {len(result.classes)} classes")')
|
|
@@ -191,7 +234,7 @@ class ExamplesGenerator:
|
|
|
191
234
|
lines.append("# Step 1: Analyze the project")
|
|
192
235
|
lines.append("config = Code2DocsConfig(project_name=\"my-project\")")
|
|
193
236
|
lines.append("scanner = ProjectScanner(config)")
|
|
194
|
-
lines.append('result = scanner.analyze("./
|
|
237
|
+
lines.append('result = scanner.analyze(f"./${project_name_adv}") if project_name_adv != "." else result = scanner.analyze("./")')
|
|
195
238
|
lines.append("")
|
|
196
239
|
|
|
197
240
|
for i, cls in enumerate(gen_classes[:4], start=2):
|
|
@@ -351,8 +394,8 @@ class ExamplesGenerator:
|
|
|
351
394
|
args = [a for a in func.args if a not in ("self", "cls")]
|
|
352
395
|
parts = []
|
|
353
396
|
for arg in args[:5]:
|
|
354
|
-
val =
|
|
355
|
-
if not val:
|
|
397
|
+
val = self._get_example_value(arg)
|
|
398
|
+
if not val or val == '"..."':
|
|
356
399
|
# Try type hint
|
|
357
400
|
val = self._example_from_type(func, arg)
|
|
358
401
|
if not val:
|
|
@@ -360,17 +403,18 @@ class ExamplesGenerator:
|
|
|
360
403
|
parts.append(f"{arg}={val}" if len(args) > 1 else val)
|
|
361
404
|
return ", ".join(parts)
|
|
362
405
|
|
|
363
|
-
|
|
364
|
-
def _example_from_type(func: FunctionInfo, arg: str) -> Optional[str]:
|
|
406
|
+
def _example_from_type(self, func: FunctionInfo, arg: str) -> Optional[str]:
|
|
365
407
|
"""Try to infer example value from type annotation."""
|
|
408
|
+
project_name = self.config.project_name or Path(self.result.project_path).name
|
|
409
|
+
|
|
366
410
|
# FunctionInfo.returns gives return type, but arg types
|
|
367
411
|
# are not always available; use naming heuristics
|
|
368
412
|
if "path" in arg.lower():
|
|
369
|
-
return '"./
|
|
413
|
+
return f'"./{project_name}"'
|
|
370
414
|
if "name" in arg.lower():
|
|
371
|
-
return '"
|
|
415
|
+
return f'"{project_name}"'
|
|
372
416
|
if "dir" in arg.lower():
|
|
373
|
-
return '"./docs"'
|
|
417
|
+
return f'"{self.config.output}"' if self.config.output else '"./docs"'
|
|
374
418
|
if arg.startswith("is_") or arg.startswith("enable"):
|
|
375
419
|
return "True"
|
|
376
420
|
if "count" in arg.lower() or "max" in arg.lower() or "min" in arg.lower():
|
|
@@ -59,6 +59,7 @@ class GettingStartedGenerator:
|
|
|
59
59
|
dep_scanner = DependencyScanner()
|
|
60
60
|
deps = dep_scanner.scan(self.result.project_path)
|
|
61
61
|
cmd = deps.install_command or f"pip install {self.config.project_name or '.'}"
|
|
62
|
+
repo_url = self.config.repo_url or "<repository-url>"
|
|
62
63
|
lines = [
|
|
63
64
|
"## Installation\n",
|
|
64
65
|
"```bash",
|
|
@@ -66,7 +67,7 @@ class GettingStartedGenerator:
|
|
|
66
67
|
"```\n",
|
|
67
68
|
"To install from source:\n",
|
|
68
69
|
"```bash",
|
|
69
|
-
"git clone
|
|
70
|
+
f"git clone {repo_url}",
|
|
70
71
|
f"cd {self.config.project_name or 'project'}",
|
|
71
72
|
"pip install -e .",
|
|
72
73
|
"```",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""MkDocs configuration generator — auto-generate mkdocs.yml from docs tree."""
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Dict, List, Optional
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
5
|
|
|
6
6
|
import yaml
|
|
7
7
|
|
|
@@ -22,11 +22,14 @@ class MkDocsGenerator:
|
|
|
22
22
|
project_name = self.config.project_name or "Project"
|
|
23
23
|
nav = self._build_nav(docs_dir)
|
|
24
24
|
|
|
25
|
+
# Read MkDocs config from pyproject.toml if available
|
|
26
|
+
mkdocs_config = self._read_pyproject_mkdocs()
|
|
27
|
+
|
|
25
28
|
data = {
|
|
26
29
|
"site_name": f"{project_name} Documentation",
|
|
27
|
-
"theme": {"name": "material"},
|
|
30
|
+
"theme": mkdocs_config.get("theme", {"name": "material"}),
|
|
28
31
|
"nav": nav,
|
|
29
|
-
"markdown_extensions": [
|
|
32
|
+
"markdown_extensions": mkdocs_config.get("markdown_extensions", [
|
|
30
33
|
"admonition",
|
|
31
34
|
"pymdownx.highlight",
|
|
32
35
|
"pymdownx.superfences",
|
|
@@ -37,10 +40,47 @@ class MkDocsGenerator:
|
|
|
37
40
|
"format": "!!python/name:pymdownx.superfences.fence_code_format",
|
|
38
41
|
}]
|
|
39
42
|
}},
|
|
40
|
-
],
|
|
43
|
+
]),
|
|
41
44
|
}
|
|
45
|
+
|
|
46
|
+
# Add extra fields from pyproject.toml if present
|
|
47
|
+
for key in ["extra_css", "extra_javascript", "plugins", "copyright"]:
|
|
48
|
+
if key in mkdocs_config:
|
|
49
|
+
data[key] = mkdocs_config[key]
|
|
50
|
+
|
|
42
51
|
return yaml.dump(data, default_flow_style=False, sort_keys=False)
|
|
43
52
|
|
|
53
|
+
def _read_pyproject_mkdocs(self) -> Dict[str, Any]:
|
|
54
|
+
"""Read MkDocs configuration from [tool.mkdocs] in pyproject.toml."""
|
|
55
|
+
project_path = Path(self.result.project_path)
|
|
56
|
+
pyproject_path = project_path / "pyproject.toml"
|
|
57
|
+
|
|
58
|
+
if not pyproject_path.exists():
|
|
59
|
+
# Also check parent directory (for nested packages)
|
|
60
|
+
pyproject_path = project_path.parent / "pyproject.toml"
|
|
61
|
+
|
|
62
|
+
if not pyproject_path.exists():
|
|
63
|
+
return {}
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
import tomllib
|
|
67
|
+
with open(pyproject_path, "rb") as f:
|
|
68
|
+
data = tomllib.load(f)
|
|
69
|
+
|
|
70
|
+
# Support both [tool.mkdocs] and [tool.poetry.plugins.mkdocs] formats
|
|
71
|
+
tool_data = data.get("tool", {})
|
|
72
|
+
mkdocs_data = tool_data.get("mkdocs", {})
|
|
73
|
+
|
|
74
|
+
# Also check for poetry-style config
|
|
75
|
+
if not mkdocs_data:
|
|
76
|
+
poetry = tool_data.get("poetry", {})
|
|
77
|
+
plugins = poetry.get("plugins", {})
|
|
78
|
+
mkdocs_data = plugins.get("mkdocs", {})
|
|
79
|
+
|
|
80
|
+
return mkdocs_data
|
|
81
|
+
except Exception:
|
|
82
|
+
return {}
|
|
83
|
+
|
|
44
84
|
def _build_nav(self, docs_dir: Optional[str] = None) -> List:
|
|
45
85
|
"""Build navigation structure from docs tree and analysis."""
|
|
46
86
|
nav: List = [{"Home": "index.md"}]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code2docs
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.4
|
|
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
|
|
@@ -50,7 +50,7 @@ Dynamic: license-file
|
|
|
50
50
|
|
|
51
51
|
# code2docs
|
|
52
52
|
|
|
53
|
-
  
|
|
54
54
|
|
|
55
55
|
> Auto-generate and sync project documentation from source code analysis.
|
|
56
56
|
|
|
@@ -190,7 +190,7 @@ code2docs can update only specific sections of an existing README using markers:
|
|
|
190
190
|
```markdown
|
|
191
191
|
<!-- code2docs:start --># code2docs
|
|
192
192
|
|
|
193
|
-
   
|
|
194
194
|
> **276** functions | **57** classes | **51** files | CC̄ = 3.8
|
|
195
195
|
|
|
196
196
|
> Auto-generated project documentation from source code analysis.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|