code2docs 3.0.34__py3-none-any.whl → 3.0.35__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 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.34"
8
+ __version__ = "3.0.35"
9
9
  __author__ = "Tom Sapletta"
10
10
  __all__ = ["Code2DocsConfig", "generate_readme", "generate_docs", "analyze_and_document"]
11
11
 
@@ -1,5 +1,6 @@
1
1
  """Detect web framework endpoints (Flask, FastAPI, Django) from AST analysis."""
2
2
 
3
+ import os
3
4
  import re
4
5
  from dataclasses import dataclass, field
5
6
  from pathlib import Path
@@ -23,6 +24,36 @@ class Endpoint:
23
24
  return_type: Optional[str] = None
24
25
 
25
26
 
27
+ # Directories never searched for Django urls.py: virtualenvs, vendored packages,
28
+ # VCS/worktree copies and build output. Walking them dominated runtime (~40s on
29
+ # a workspace with vendored site-packages). Any dot-directory is skipped too.
30
+ _SKIP_DIRS = frozenset(
31
+ {
32
+ "venv",
33
+ "env",
34
+ "node_modules",
35
+ "site-packages",
36
+ "__pycache__",
37
+ "dist",
38
+ "build",
39
+ "target",
40
+ "bower_components",
41
+ }
42
+ )
43
+
44
+
45
+ def _iter_urls_files(root: Path):
46
+ """Yield ``urls.py`` files under *root*, pruning irrelevant directories."""
47
+ for dirpath, dirnames, filenames in os.walk(root):
48
+ dirnames[:] = [
49
+ d
50
+ for d in dirnames
51
+ if d not in _SKIP_DIRS and not d.startswith(".") and not d.endswith(".egg-info")
52
+ ]
53
+ if "urls.py" in filenames:
54
+ yield Path(dirpath) / "urls.py"
55
+
56
+
26
57
  class EndpointDetector:
27
58
  """Detects web endpoints from decorator patterns in source code."""
28
59
 
@@ -91,7 +122,7 @@ class EndpointDetector:
91
122
  endpoints: List[Endpoint] = []
92
123
  project = Path(project_path)
93
124
 
94
- for urls_file in project.rglob("urls.py"):
125
+ for urls_file in _iter_urls_files(project):
95
126
  try:
96
127
  source = urls_file.read_text(encoding="utf-8")
97
128
  for match in self.DJANGO_URL_PATTERN.finditer(source):
code2docs/llm_helper.py CHANGED
@@ -4,6 +4,7 @@ If litellm is not installed or LLM is not configured, all methods return None
4
4
  and generators fall back to algorithm-based template output.
5
5
  """
6
6
 
7
+ import importlib.util
7
8
  import logging
8
9
  from typing import Optional
9
10
 
@@ -29,6 +30,18 @@ def _get_litellm():
29
30
  return _litellm if _litellm is not False else None
30
31
 
31
32
 
33
+ def _litellm_installed() -> bool:
34
+ """Cheap availability check: locate litellm without importing it.
35
+
36
+ Importing litellm costs ~10s, so it must only happen when a completion is
37
+ actually requested (see ``_get_litellm`` in ``LLMHelper.complete``).
38
+ """
39
+ try:
40
+ return importlib.util.find_spec("litellm") is not None
41
+ except (ImportError, ValueError):
42
+ return False
43
+
44
+
32
45
  class LLMHelper:
33
46
  """Thin wrapper around litellm for documentation generation.
34
47
 
@@ -45,7 +58,7 @@ class LLMHelper:
45
58
  """Check if LLM is configured and litellm is installed."""
46
59
  if self._available is None:
47
60
  self._available = (
48
- self.config.enabled and bool(self.config.model) and _get_litellm() is not None
61
+ self.config.enabled and bool(self.config.model) and _litellm_installed()
49
62
  )
50
63
  if self._available:
51
64
  logger.info("LLM enabled: model=%s", self.config.model)
@@ -54,7 +67,7 @@ class LLMHelper:
54
67
  "LLM disabled (enabled=%s, model=%s, litellm=%s)",
55
68
  self.config.enabled,
56
69
  bool(self.config.model),
57
- _get_litellm() is not None,
70
+ _litellm_installed(),
58
71
  )
59
72
  return self._available
60
73
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code2docs
3
- Version: 3.0.34
3
+ Version: 3.0.35
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
@@ -11,14 +11,13 @@ Keywords: documentation,auto-docs,readme-generator,api-reference,code-analysis,s
11
11
  Classifier: Development Status :: 3 - Alpha
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3.9
15
14
  Classifier: Programming Language :: Python :: 3.10
16
15
  Classifier: Programming Language :: Python :: 3.11
17
16
  Classifier: Programming Language :: Python :: 3.12
18
17
  Classifier: Programming Language :: Python :: 3.13
19
18
  Classifier: Topic :: Software Development :: Documentation
20
19
  Classifier: Topic :: Software Development :: Code Generators
21
- Requires-Python: >=3.9
20
+ Requires-Python: >=3.10
22
21
  Description-Content-Type: text/markdown
23
22
  License-File: LICENSE
24
23
  Requires-Dist: code2llm>=0.5.0
@@ -44,24 +43,24 @@ Requires-Dist: ruff>=0.1; extra == "dev"
44
43
  Requires-Dist: mypy>=1.0; extra == "dev"
45
44
  Requires-Dist: build>=1.0; extra == "dev"
46
45
  Requires-Dist: twine>=5.0; extra == "dev"
47
- Requires-Dist: goal>=2.1.0; extra == "dev"
48
46
  Requires-Dist: costs>=0.1.20; extra == "dev"
49
- Requires-Dist: pfix>=0.1.60; extra == "dev"
50
47
  Provides-Extra: all
51
48
  Requires-Dist: code2docs[git,llm,mkdocs,watch]; extra == "all"
52
49
  Dynamic: license-file
53
50
 
54
51
  ## AI Cost Tracking
55
52
 
56
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-yellow) ![AI Model](https://img.shields.io/badge/AI%20Model-openrouter%2Fqwen%2Fqwen3-coder-next-lightgrey)
53
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-3.0.35-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
54
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$5.36-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-45.9h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
57
55
 
58
- This project uses AI-generated code. Total cost: **$7.5000** with **77** AI commits.
56
+ - 🤖 **LLM usage:** $5.3635 (99 commits)
57
+ - 👤 **Human dev:** ~$4589 (45.9h @ $100/h, 30min dedup)
59
58
 
60
- Generated on 2026-06-29 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/models/openrouter/qwen/qwen3-coder-next)
59
+ Generated on 2026-10-04 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
61
60
 
62
61
  ---
63
62
 
64
- ![version](https://img.shields.io/badge/version-3.0.34-blue) ![python](https://img.shields.io/badge/python-%3E%3D3.9-blue) ![docs](https://img.shields.io/badge/docs-auto--generated-blueviolet)
63
+ ![version](https://img.shields.io/badge/version-3.0.35-blue) ![python](https://img.shields.io/badge/python-%3E%3D3.9-blue) ![docs](https://img.shields.io/badge/docs-auto--generated-blueviolet)
65
64
 
66
65
  > Auto-generate and sync project documentation from source code analysis.
67
66
 
@@ -198,7 +197,7 @@ code2docs can update only specific sections of an existing README using markers:
198
197
  ```markdown
199
198
  <!-- code2docs:start --># code2docs
200
199
 
201
- ![version](https://img.shields.io/badge/version-3.0.34-blue) ![python](https://img.shields.io/badge/python-%3E%3D3.9-blue) ![coverage](https://img.shields.io/badge/coverage-unknown-lightgrey) ![functions](https://img.shields.io/badge/functions-276-green)
200
+ ![version](https://img.shields.io/badge/version-3.0.35-blue) ![python](https://img.shields.io/badge/python-%3E%3D3.9-blue) ![coverage](https://img.shields.io/badge/coverage-unknown-lightgrey) ![functions](https://img.shields.io/badge/functions-276-green)
202
201
  > **276** functions | **57** classes | **51** files | CC̄ = 3.8
203
202
 
204
203
  > Auto-generated project documentation from source code analysis.
@@ -1,14 +1,14 @@
1
- code2docs/__init__.py,sha256=JUMSlGDQHDpjuFBPq3HrlLFCJJIXod7y0H9nvKU6Uzs,905
1
+ code2docs/__init__.py,sha256=V3QR1P-IHILp5xf_gSCapzNREt8vbCNqveJMGz9ZgBs,905
2
2
  code2docs/__main__.py,sha256=dGGkQIZVFDZXEXa9PWew7cBh7hBAYDWfJ9LeezMf2UI,116
3
3
  code2docs/base.py,sha256=RXj0wlgj_5uTBlX-xEY2WTLcLlOhd1zDT83on1GGCVw,1320
4
4
  code2docs/cli.py,sha256=EDaaF3-LQW-E1FzlQ6cN1wkjnUDZo8Qz3eh9MVwQVcc,12848
5
5
  code2docs/config.py,sha256=WrDlCVOJfAIzONTJq5QGvSVpM50NueJVfAkc5Fg-zJ0,11320
6
- code2docs/llm_helper.py,sha256=e4UGcFW2n-eWlFAf0weFOpG3HCmKMOnZLl8cD7pyx5c,5885
6
+ code2docs/llm_helper.py,sha256=KY6Y03ve7ZhZ10CjKw3NlfZtYvhhadX1lbxM6xrdk_U,6292
7
7
  code2docs/registry.py,sha256=gPANpUCQJ_seb3JLnRTxXbZ0vvlvDwBR0r7XdF0Fdm8,1261
8
8
  code2docs/analyzers/__init__.py,sha256=4m_W6ch3mfNJVVk5CG5G4-kqA28KzPz_uvzYhznxj6Q,608
9
9
  code2docs/analyzers/dependency_scanner.py,sha256=sQraMuMdNc7eYZnWYuOkukCa9U4B6ENabEgsZY4LmH8,12122
10
10
  code2docs/analyzers/docstring_extractor.py,sha256=HkLHY4ZMUu-WFJbcxHC8wTv3H1JSMhDNR27jobT_6Y0,5053
11
- code2docs/analyzers/endpoint_detector.py,sha256=sYhx_VGw28rYURayGY-6W5dWsYCxelvkvItVJpD_URU,3875
11
+ code2docs/analyzers/endpoint_detector.py,sha256=HSpjRBFryU4xozfedxTOnKIA5BykGsAZEvnhTNHdP30,4768
12
12
  code2docs/analyzers/markdown_validator.py,sha256=Uy2J2ApnteBzPDtMHWm3SIXjqtIE81YygvtN-5fvgfg,7310
13
13
  code2docs/analyzers/project_scanner.py,sha256=dqK2z_slJ_pF3jU7sg3bvx80-wVVnRHpTi4pYK2Qm5Q,1605
14
14
  code2docs/examples/advanced_usage.py,sha256=sHyXrXV5p9t6B9OpZndPNrClBSSEzSmjlsvoMT7cp9k,2741
@@ -45,9 +45,9 @@ code2docs/templates/example_usage.py.j2,sha256=G0mF7iLtisnsh-PfPTMLgVoNSDw6J-MqP
45
45
  code2docs/templates/index.md.j2,sha256=pj56gwiLuXxV64xVYfzmtboMLyFKF-Kty01Ps0t37xM,653
46
46
  code2docs/templates/module_doc.md.j2,sha256=oMcaDSSCbyMSjQtnal7garYr4dDbTcRAQobsjrYVYGo,1876
47
47
  code2docs/templates/readme.md.j2,sha256=OJ3EA3gezuBLyjYUDVtRI4whg4Pcjvkh5ZgnsuwDKv4,9170
48
- code2docs-3.0.34.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
49
- code2docs-3.0.34.dist-info/METADATA,sha256=SyH34v7KTguzt2GD3FkKTxOrSRoQ0d-m65m3Tspop-Y,26969
50
- code2docs-3.0.34.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
51
- code2docs-3.0.34.dist-info/entry_points.txt,sha256=ATamjPNo2CbTaAUN3LfK8qrnY3EQE9gE7lMDgPYhLdQ,49
52
- code2docs-3.0.34.dist-info/top_level.txt,sha256=L-O2M4M-NSiphgcKSfXFsC-t6PqziMl8Ef6tsNR2DaM,10
53
- code2docs-3.0.34.dist-info/RECORD,,
48
+ code2docs-3.0.35.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
49
+ code2docs-3.0.35.dist-info/METADATA,sha256=MEtBKdQXNe0lhC84h0DSZ7VY_Q33Mn6SMtct5fJ53VU,27133
50
+ code2docs-3.0.35.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
51
+ code2docs-3.0.35.dist-info/entry_points.txt,sha256=ATamjPNo2CbTaAUN3LfK8qrnY3EQE9gE7lMDgPYhLdQ,49
52
+ code2docs-3.0.35.dist-info/top_level.txt,sha256=L-O2M4M-NSiphgcKSfXFsC-t6PqziMl8Ef6tsNR2DaM,10
53
+ code2docs-3.0.35.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (82.0.1)
2
+ Generator: setuptools (84.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5