code2docs 3.0.30__py3-none-any.whl → 3.0.31__py3-none-any.whl
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/__init__.py +1 -1
- code2docs/cli.py +30 -0
- code2docs/generators/readme_gen.py +42 -8
- code2docs/templates/readme.md.j2 +10 -25
- {code2docs-3.0.30.dist-info → code2docs-3.0.31.dist-info}/METADATA +7 -7
- {code2docs-3.0.30.dist-info → code2docs-3.0.31.dist-info}/RECORD +10 -10
- {code2docs-3.0.30.dist-info → code2docs-3.0.31.dist-info}/WHEEL +0 -0
- {code2docs-3.0.30.dist-info → code2docs-3.0.31.dist-info}/entry_points.txt +0 -0
- {code2docs-3.0.30.dist-info → code2docs-3.0.31.dist-info}/licenses/LICENSE +0 -0
- {code2docs-3.0.30.dist-info → code2docs-3.0.31.dist-info}/top_level.txt +0 -0
code2docs/__init__.py
CHANGED
|
@@ -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.31'
|
|
9
9
|
__author__ = 'Tom Sapletta'
|
|
10
10
|
__all__ = ['Code2DocsConfig', 'generate_readme', 'generate_docs', 'analyze_and_document']
|
|
11
11
|
|
code2docs/cli.py
CHANGED
|
@@ -93,6 +93,36 @@ def check(project_path, config_path, target) -> None:
|
|
|
93
93
|
config = _load_config(project_path, config_path)
|
|
94
94
|
_run_check(project_path, config, target)
|
|
95
95
|
|
|
96
|
+
@main.command()
|
|
97
|
+
@click.argument('project_path', default='.', type=click.Path(exists=True))
|
|
98
|
+
@click.option('--pattern', multiple=True, default=('*.md',), show_default=True,
|
|
99
|
+
help='Glob pattern(s) of markdown files to validate.')
|
|
100
|
+
@click.option('--strict', is_flag=True, help='Exit with non-zero status if any issues are found.')
|
|
101
|
+
def validate(project_path, pattern, strict) -> None:
|
|
102
|
+
"""Validate generated markdown: broken links, table shape, duplicate headings."""
|
|
103
|
+
from .analyzers.markdown_validator import validate_markdown_tree
|
|
104
|
+
project = Path(project_path).resolve()
|
|
105
|
+
console.print(f'[bold blue]🔎 code2docs validate:[/] {project.name}')
|
|
106
|
+
report = validate_markdown_tree(project, patterns=pattern)
|
|
107
|
+
if report.ok:
|
|
108
|
+
console.print(f'[green]{report.summary()}[/]')
|
|
109
|
+
return
|
|
110
|
+
table = Table(title='Markdown Issues', show_lines=False)
|
|
111
|
+
table.add_column('File', style='cyan', overflow='fold')
|
|
112
|
+
table.add_column('Line', justify='right')
|
|
113
|
+
table.add_column('Kind', style='magenta')
|
|
114
|
+
table.add_column('Message', style='dim', overflow='fold')
|
|
115
|
+
for issue in report.issues:
|
|
116
|
+
try:
|
|
117
|
+
rel = Path(issue.file).relative_to(project)
|
|
118
|
+
except ValueError:
|
|
119
|
+
rel = Path(issue.file)
|
|
120
|
+
table.add_row(str(rel), str(issue.line), issue.kind, issue.message)
|
|
121
|
+
console.print(table)
|
|
122
|
+
console.print(f'[yellow]{report.summary()}[/]')
|
|
123
|
+
if strict:
|
|
124
|
+
sys.exit(1)
|
|
125
|
+
|
|
96
126
|
@main.command()
|
|
97
127
|
@click.argument('project_path', default='.', type=click.Path(exists=True))
|
|
98
128
|
@click.option('--config', '-c', 'config_path', default=None, help='Path to code2docs.yaml')
|
|
@@ -59,7 +59,36 @@ class ReadmeGenerator:
|
|
|
59
59
|
extras = self._extract_extras()
|
|
60
60
|
lang = getattr(deps, 'language', 'python') or 'python'
|
|
61
61
|
docs_nav_items, generated_files = self._collect_existing_docs()
|
|
62
|
-
|
|
62
|
+
has_contributing = (Path(self.result.project_path) / 'CONTRIBUTING.md').exists()
|
|
63
|
+
return {'docs_nav_items': docs_nav_items, 'generated_files': generated_files, 'has_contributing': has_contributing, 'project_name': project_name, 'project_path': self.result.project_path, 'project_description': project_description, 'badges': generate_badges(project_name, self.config.readme.badges, stats, deps), 'stats': stats, 'avg_complexity': avg_complexity, 'dependencies': deps, 'language': lang, 'endpoints': endpoints, 'public_functions': public_functions, 'public_classes': public_classes, 'entry_points': entry_points, 'module_tree': module_tree, 'modules': self.result.modules, 'sync_markers': self.config.readme.sync_markers, 'author': metadata.get('author', ''), 'license': metadata.get('license', ''), 'license_file': metadata.get('license_file', ''), 'contributors': metadata.get('contributors', []), 'repo_url': self.config.repo_url, 'version': metadata.get('version', '0.1.0'), 'extras': extras}
|
|
64
|
+
|
|
65
|
+
_DOC_FILE_SPECS = [
|
|
66
|
+
('docs/getting-started.md', 'Getting Started', '🚀', 'Quick start guide'),
|
|
67
|
+
('docs/api.md', 'API Reference', '📚', 'Complete API documentation'),
|
|
68
|
+
('docs/modules.md', 'Module Reference', '📦', 'Module reference with metrics'),
|
|
69
|
+
('docs/architecture.md', 'Architecture', '🏛️', 'Architecture with diagrams'),
|
|
70
|
+
('docs/dependency-graph.md', 'Dependency Graph', '🔗', 'Module dependency graphs'),
|
|
71
|
+
('docs/coverage.md', 'Coverage', '📊', 'Docstring coverage report'),
|
|
72
|
+
('docs/configuration.md', 'Configuration', '🔧', 'Configuration reference'),
|
|
73
|
+
('docs/api-changelog.md', 'API Changelog', '📝', 'API change tracking'),
|
|
74
|
+
('CONTRIBUTING.md', 'Contributing', '🤝', 'Contribution guidelines'),
|
|
75
|
+
('examples', 'Examples', '💡', 'Usage examples and code samples'),
|
|
76
|
+
('mkdocs.yml', 'MkDocs Config', '⚙️', 'MkDocs site configuration'),
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
def _collect_existing_docs(self) -> tuple:
|
|
80
|
+
"""Return (docs_nav_items, generated_files) only for files/dirs that exist."""
|
|
81
|
+
project = Path(self.result.project_path)
|
|
82
|
+
nav_items = []
|
|
83
|
+
generated = []
|
|
84
|
+
for rel_path, title, icon, description in self._DOC_FILE_SPECS:
|
|
85
|
+
target = project / rel_path
|
|
86
|
+
if not target.exists():
|
|
87
|
+
continue
|
|
88
|
+
link = f'./{rel_path}'
|
|
89
|
+
nav_items.append({'title': title, 'icon': icon, 'path': link, 'description': description})
|
|
90
|
+
generated.append({'output': rel_path, 'description': description, 'link': link})
|
|
91
|
+
return nav_items, generated
|
|
63
92
|
|
|
64
93
|
def _calc_avg_complexity(self) -> float:
|
|
65
94
|
"""Calculate average cyclomatic complexity."""
|
|
@@ -181,14 +210,19 @@ class ReadmeGenerator:
|
|
|
181
210
|
pass
|
|
182
211
|
|
|
183
212
|
def _detect_license(self, metadata: Dict) -> None:
|
|
184
|
-
"""Detect license type from LICENSE files.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
213
|
+
"""Detect license type from LICENSE files.
|
|
214
|
+
|
|
215
|
+
Only sets ``license_file`` (used as a link target) when the file lives
|
|
216
|
+
in the project root, so README links resolve correctly.
|
|
217
|
+
"""
|
|
218
|
+
project_root = Path(self.result.project_path)
|
|
219
|
+
in_project = [project_root / lf for lf in ['LICENSE', 'LICENSE.txt', 'LICENSE.md', 'COPYING']]
|
|
220
|
+
parent = project_root.parent
|
|
221
|
+
in_parent = [parent / lf for lf in ['LICENSE', 'LICENSE.txt', 'LICENSE.md', 'COPYING']] if parent != project_root else []
|
|
222
|
+
for license_path in in_project + in_parent:
|
|
190
223
|
if license_path.exists():
|
|
191
|
-
|
|
224
|
+
if license_path.parent == project_root:
|
|
225
|
+
metadata['license_file'] = license_path.name
|
|
192
226
|
if not metadata['license']:
|
|
193
227
|
try:
|
|
194
228
|
content = license_path.read_text(encoding='utf-8').lower()
|
code2docs/templates/readme.md.j2
CHANGED
|
@@ -316,7 +316,7 @@ Content outside the markers is preserved when regenerating. Enable this with `sy
|
|
|
316
316
|
{% endfor %}
|
|
317
317
|
{% endif %}
|
|
318
318
|
|
|
319
|
-
We welcome contributions! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
|
|
319
|
+
{% if has_contributing %}We welcome contributions! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.{% else %}We welcome contributions! Open an issue or pull request to get started.{% endif %}
|
|
320
320
|
|
|
321
321
|
### Development Setup
|
|
322
322
|
|
|
@@ -353,38 +353,23 @@ pytest
|
|
|
353
353
|
```
|
|
354
354
|
{% endif %}
|
|
355
355
|
|
|
356
|
-
{% if "docs_nav" in sections %}
|
|
356
|
+
{% if "docs_nav" in sections and docs_nav_items %}
|
|
357
357
|
## Documentation
|
|
358
358
|
|
|
359
|
-
{%
|
|
360
|
-
-
|
|
361
|
-
|
|
362
|
-
- 📚 [API Reference]({{ repo_url }}/blob/main/docs/api.md) — Complete API documentation
|
|
363
|
-
- 🔧 [Configuration]({{ repo_url }}/blob/main/docs/configuration.md) — Configuration options
|
|
364
|
-
{% else %}
|
|
365
|
-
- 📖 [Full Documentation](./docs) — API reference, module docs, architecture
|
|
366
|
-
- 🚀 [Getting Started](./docs/getting-started.md) — Quick start guide
|
|
367
|
-
- 📚 [API Reference](./docs/api.md) — Complete API documentation
|
|
368
|
-
- 🔧 [Configuration](./docs/configuration.md) — Configuration options
|
|
369
|
-
{% endif %}
|
|
370
|
-
- 💡 [Examples](./examples) — Usage examples and code samples
|
|
359
|
+
{% for item in docs_nav_items %}
|
|
360
|
+
- {{ item.icon }} [{{ item.title }}]({{ item.path }}) — {{ item.description }}
|
|
361
|
+
{% endfor %}
|
|
371
362
|
|
|
363
|
+
{% if generated_files %}
|
|
372
364
|
### Generated Files
|
|
373
365
|
|
|
374
366
|
| Output | Description | Link |
|
|
375
367
|
|--------|-------------|------|
|
|
376
368
|
| `README.md` | Project overview (this file) | — |
|
|
377
|
-
|
|
378
|
-
| `
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
| `docs/coverage.md` | Docstring coverage report | [View](./docs/coverage.md) |
|
|
382
|
-
| `docs/getting-started.md` | Getting started guide | [View](./docs/getting-started.md) |
|
|
383
|
-
| `docs/configuration.md` | Configuration reference | [View](./docs/configuration.md) |
|
|
384
|
-
| `docs/api-changelog.md` | API change tracking | [View](./docs/api-changelog.md) |
|
|
385
|
-
| `CONTRIBUTING.md` | Contribution guidelines | [View](./CONTRIBUTING.md) |
|
|
386
|
-
| `examples/` | Usage examples | [Browse](./examples) |
|
|
387
|
-
| `mkdocs.yml` | MkDocs configuration | — |
|
|
369
|
+
{% for file in generated_files %}
|
|
370
|
+
| `{{ file.output }}` | {{ file.description }} | [View]({{ file.link }}) |
|
|
371
|
+
{% endfor %}
|
|
372
|
+
{% endif %}
|
|
388
373
|
{% endif %}
|
|
389
374
|
|
|
390
375
|
{% if sync_markers %}<!-- code2docs:end -->{% endif %}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code2docs
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.31
|
|
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
|
|
@@ -53,17 +53,17 @@ Dynamic: license-file
|
|
|
53
53
|
|
|
54
54
|
## AI Cost Tracking
|
|
55
55
|
|
|
56
|
-
     
|
|
57
|
+
  
|
|
58
58
|
|
|
59
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
60
|
-
- 👤 **Human dev:** ~$
|
|
59
|
+
- 🤖 **LLM usage:** $7.5000 (71 commits)
|
|
60
|
+
- 👤 **Human dev:** ~$2534 (25.3h @ $100/h, 30min dedup)
|
|
61
61
|
|
|
62
62
|
Generated on 2026-04-20 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
63
63
|
|
|
64
64
|
---
|
|
65
65
|
|
|
66
|
-
  
|
|
67
67
|
|
|
68
68
|
> Auto-generate and sync project documentation from source code analysis.
|
|
69
69
|
|
|
@@ -200,7 +200,7 @@ code2docs can update only specific sections of an existing README using markers:
|
|
|
200
200
|
```markdown
|
|
201
201
|
<!-- code2docs:start --># code2docs
|
|
202
202
|
|
|
203
|
-
   
|
|
204
204
|
> **276** functions | **57** classes | **51** files | CC̄ = 3.8
|
|
205
205
|
|
|
206
206
|
> Auto-generated project documentation from source code analysis.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
code2docs/__init__.py,sha256=
|
|
1
|
+
code2docs/__init__.py,sha256=yQwu6DuPyl_1LGiKuY-cKbn4ETF5DnoKdYyKlauZU8U,901
|
|
2
2
|
code2docs/__main__.py,sha256=dGGkQIZVFDZXEXa9PWew7cBh7hBAYDWfJ9LeezMf2UI,116
|
|
3
3
|
code2docs/base.py,sha256=tQT0I9iAxpxtCNHFZL8kYdgCD-96ywOvDKD2Iiksgj4,1319
|
|
4
|
-
code2docs/cli.py,sha256=
|
|
4
|
+
code2docs/cli.py,sha256=L9HcHqWhtyY22FGTyWZ1MhVTgatCcb_vwXJK7l6uoYo,12727
|
|
5
5
|
code2docs/config.py,sha256=WH101glXT9chJH016g1z7uZy8l8dbNbhnN_rOU2BSsE,8825
|
|
6
6
|
code2docs/llm_helper.py,sha256=2Q_pnOBeei9EVU9HL_dWmqUpdkSzYNlMfIr5OOQcLws,6155
|
|
7
7
|
code2docs/registry.py,sha256=aD3pdTAF2VeTMVddoQdt6QCB9b15FQywLE0W8Uca0xk,1256
|
|
@@ -34,7 +34,7 @@ code2docs/generators/getting_started_gen.py,sha256=P--4A0Lmeo338zKjuD1D5BYxVJ5IG
|
|
|
34
34
|
code2docs/generators/mkdocs_gen.py,sha256=ciETCRUzb3V3kSGAslM5DStHJUQ1Ju-pTxC1Iv_duzo,3849
|
|
35
35
|
code2docs/generators/module_docs_gen.py,sha256=6FFH69AQlNxPlEMEEeX0ND366rb7URSP6geNlvf_Sjk,10090
|
|
36
36
|
code2docs/generators/org_readme_gen.py,sha256=Saf7iZbHwtXcqXAl1EYmufl6mZ4msc-SFsVWj1Zqptw,9411
|
|
37
|
-
code2docs/generators/readme_gen.py,sha256=
|
|
37
|
+
code2docs/generators/readme_gen.py,sha256=SGe8LCueRvbMR-4VNk2C2L-OyZtKNOSxDd-VhoukOek,19496
|
|
38
38
|
code2docs/sync/__init__.py,sha256=MOTJYvJXPZF-4PS47lv-6bOc9J2ZoQGoIchp3vIuwrU,162
|
|
39
39
|
code2docs/sync/differ.py,sha256=3dtu95BiiTXaz8tU63Ot6_W9DAHdFoKVd3OIiFz7Qiw,4160
|
|
40
40
|
code2docs/sync/updater.py,sha256=ZuRcVtoubxwPnuuTn8s05h1xLoSNwzaNlm_XKAJdf94,1971
|
|
@@ -44,10 +44,10 @@ code2docs/templates/architecture.md.j2,sha256=kx_lECcC9FGHTEnhgPNVGIijDFXu5xFcm4
|
|
|
44
44
|
code2docs/templates/example_usage.py.j2,sha256=G0mF7iLtisnsh-PfPTMLgVoNSDw6J-MqPndzMTtcuJk,225
|
|
45
45
|
code2docs/templates/index.md.j2,sha256=pj56gwiLuXxV64xVYfzmtboMLyFKF-Kty01Ps0t37xM,653
|
|
46
46
|
code2docs/templates/module_doc.md.j2,sha256=oMcaDSSCbyMSjQtnal7garYr4dDbTcRAQobsjrYVYGo,1876
|
|
47
|
-
code2docs/templates/readme.md.j2,sha256=
|
|
48
|
-
code2docs-3.0.
|
|
49
|
-
code2docs-3.0.
|
|
50
|
-
code2docs-3.0.
|
|
51
|
-
code2docs-3.0.
|
|
52
|
-
code2docs-3.0.
|
|
53
|
-
code2docs-3.0.
|
|
47
|
+
code2docs/templates/readme.md.j2,sha256=OJ3EA3gezuBLyjYUDVtRI4whg4Pcjvkh5ZgnsuwDKv4,9170
|
|
48
|
+
code2docs-3.0.31.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
49
|
+
code2docs-3.0.31.dist-info/METADATA,sha256=Ssx2_7HTGKtMlRLrbQ39Uvfeb59YTj1ynqHCepvs8Ug,26843
|
|
50
|
+
code2docs-3.0.31.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
51
|
+
code2docs-3.0.31.dist-info/entry_points.txt,sha256=ATamjPNo2CbTaAUN3LfK8qrnY3EQE9gE7lMDgPYhLdQ,49
|
|
52
|
+
code2docs-3.0.31.dist-info/top_level.txt,sha256=L-O2M4M-NSiphgcKSfXFsC-t6PqziMl8Ef6tsNR2DaM,10
|
|
53
|
+
code2docs-3.0.31.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|