code2docs 3.0.26__tar.gz → 3.0.27__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 (66) hide show
  1. {code2docs-3.0.26 → code2docs-3.0.27}/PKG-INFO +6 -6
  2. {code2docs-3.0.26 → code2docs-3.0.27}/README.md +5 -5
  3. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/__init__.py +1 -1
  4. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/analyzers/docstring_extractor.py +2 -4
  5. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/analyzers/endpoint_detector.py +1 -2
  6. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/analyzers/project_scanner.py +1 -2
  7. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/cli.py +1 -1
  8. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/examples/advanced_usage.py +0 -1
  9. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/examples/quickstart.py +0 -2
  10. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/formatters/markdown.py +1 -1
  11. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/api_changelog_gen.py +2 -2
  12. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/api_reference_gen.py +1 -1
  13. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/architecture_gen.py +1 -1
  14. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/changelog_gen.py +1 -1
  15. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/code2llm_gen.py +1 -1
  16. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/config_docs_gen.py +1 -2
  17. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/contributing_gen.py +1 -1
  18. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/depgraph_gen.py +1 -1
  19. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/examples_gen.py +33 -13
  20. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/getting_started_gen.py +1 -1
  21. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/org_readme_gen.py +1 -1
  22. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/readme_gen.py +34 -6
  23. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/sync/differ.py +1 -1
  24. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs.egg-info/PKG-INFO +6 -6
  25. {code2docs-3.0.26 → code2docs-3.0.27}/pyproject.toml +1 -1
  26. {code2docs-3.0.26 → code2docs-3.0.27}/tests/test_analyzers.py +2 -2
  27. {code2docs-3.0.26 → code2docs-3.0.27}/tests/test_config.py +1 -2
  28. {code2docs-3.0.26 → code2docs-3.0.27}/tests/test_formatters.py +0 -1
  29. {code2docs-3.0.26 → code2docs-3.0.27}/tests/test_generators.py +61 -13
  30. {code2docs-3.0.26 → code2docs-3.0.27}/tests/test_llm_helper.py +7 -8
  31. {code2docs-3.0.26 → code2docs-3.0.27}/tests/test_registry.py +0 -3
  32. {code2docs-3.0.26 → code2docs-3.0.27}/tests/test_sync.py +0 -1
  33. {code2docs-3.0.26 → code2docs-3.0.27}/LICENSE +0 -0
  34. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/__main__.py +0 -0
  35. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/analyzers/__init__.py +0 -0
  36. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/analyzers/dependency_scanner.py +0 -0
  37. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/base.py +0 -0
  38. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/config.py +0 -0
  39. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/formatters/__init__.py +0 -0
  40. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/formatters/badges.py +0 -0
  41. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/formatters/toc.py +0 -0
  42. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/__init__.py +0 -0
  43. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/_registry_adapters.py +0 -0
  44. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/_source_links.py +0 -0
  45. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/coverage_gen.py +0 -0
  46. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/mkdocs_gen.py +0 -0
  47. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/generators/module_docs_gen.py +0 -0
  48. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/llm_helper.py +0 -0
  49. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/registry.py +0 -0
  50. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/sync/__init__.py +0 -0
  51. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/sync/updater.py +0 -0
  52. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/sync/watcher.py +0 -0
  53. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/templates/api_module.md.j2 +0 -0
  54. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/templates/architecture.md.j2 +0 -0
  55. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/templates/example_usage.py.j2 +0 -0
  56. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/templates/index.md.j2 +0 -0
  57. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/templates/module_doc.md.j2 +0 -0
  58. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs/templates/readme.md.j2 +0 -0
  59. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs.egg-info/SOURCES.txt +0 -0
  60. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs.egg-info/dependency_links.txt +0 -0
  61. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs.egg-info/entry_points.txt +0 -0
  62. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs.egg-info/requires.txt +0 -0
  63. {code2docs-3.0.26 → code2docs-3.0.27}/code2docs.egg-info/top_level.txt +0 -0
  64. {code2docs-3.0.26 → code2docs-3.0.27}/setup.cfg +0 -0
  65. {code2docs-3.0.26 → code2docs-3.0.27}/tests/test_cli.py +0 -0
  66. {code2docs-3.0.26 → code2docs-3.0.27}/tests/test_code2docs.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code2docs
3
- Version: 3.0.26
3
+ Version: 3.0.27
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
@@ -56,17 +56,17 @@ Dynamic: license-file
56
56
 
57
57
  ## AI Cost Tracking
58
58
 
59
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-3.0.26-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
59
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-3.0.27-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
60
60
  ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-18.6h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
61
61
 
62
- - 🤖 **LLM usage:** $7.5000 (58 commits)
62
+ - 🤖 **LLM usage:** $7.5000 (59 commits)
63
63
  - 👤 **Human dev:** ~$1857 (18.6h @ $100/h, 30min dedup)
64
64
 
65
- Generated on 2026-04-08 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
65
+ Generated on 2026-04-09 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
66
66
 
67
67
  ---
68
68
 
69
- ![version](https://img.shields.io/badge/version-3.0.26-blue) ![python](https://img.shields.io/badge/python-%3E%3D3.9-blue) ![docs](https://img.shields.io/badge/docs-auto--generated-blueviolet)
69
+ ![version](https://img.shields.io/badge/version-3.0.27-blue) ![python](https://img.shields.io/badge/python-%3E%3D3.9-blue) ![docs](https://img.shields.io/badge/docs-auto--generated-blueviolet)
70
70
 
71
71
  > Auto-generate and sync project documentation from source code analysis.
72
72
 
@@ -206,7 +206,7 @@ code2docs can update only specific sections of an existing README using markers:
206
206
  ```markdown
207
207
  <!-- code2docs:start --># code2docs
208
208
 
209
- ![version](https://img.shields.io/badge/version-3.0.26-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)
209
+ ![version](https://img.shields.io/badge/version-3.0.27-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)
210
210
  > **276** functions | **57** classes | **51** files | CC̄ = 3.8
211
211
 
212
212
  > Auto-generated project documentation from source code analysis.
@@ -3,17 +3,17 @@
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-3.0.26-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-3.0.27-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-18.6h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
8
8
 
9
- - 🤖 **LLM usage:** $7.5000 (58 commits)
9
+ - 🤖 **LLM usage:** $7.5000 (59 commits)
10
10
  - 👤 **Human dev:** ~$1857 (18.6h @ $100/h, 30min dedup)
11
11
 
12
- Generated on 2026-04-08 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
12
+ Generated on 2026-04-09 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
13
13
 
14
14
  ---
15
15
 
16
- ![version](https://img.shields.io/badge/version-3.0.26-blue) ![python](https://img.shields.io/badge/python-%3E%3D3.9-blue) ![docs](https://img.shields.io/badge/docs-auto--generated-blueviolet)
16
+ ![version](https://img.shields.io/badge/version-3.0.27-blue) ![python](https://img.shields.io/badge/python-%3E%3D3.9-blue) ![docs](https://img.shields.io/badge/docs-auto--generated-blueviolet)
17
17
 
18
18
  > Auto-generate and sync project documentation from source code analysis.
19
19
 
@@ -153,7 +153,7 @@ code2docs can update only specific sections of an existing README using markers:
153
153
  ```markdown
154
154
  <!-- code2docs:start --># code2docs
155
155
 
156
- ![version](https://img.shields.io/badge/version-3.0.26-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)
156
+ ![version](https://img.shields.io/badge/version-3.0.27-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)
157
157
  > **276** functions | **57** classes | **51** files | CC̄ = 3.8
158
158
 
159
159
  > 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.26'
8
+ __version__ = '3.0.27'
9
9
  __author__ = 'Tom Sapletta'
10
10
  __all__ = ['Code2DocsConfig', 'generate_readme', 'generate_docs', 'analyze_and_document']
11
11
 
@@ -1,11 +1,9 @@
1
1
  """Extract and analyze docstrings from source code."""
2
2
 
3
- import ast
4
3
  from dataclasses import dataclass, field
5
- from pathlib import Path
6
- from typing import Dict, List, Optional, Tuple
4
+ from typing import Dict, List, Optional
7
5
 
8
- from code2llm.api import AnalysisResult, FunctionInfo, ClassInfo
6
+ from code2llm.api import AnalysisResult
9
7
 
10
8
 
11
9
  @dataclass
@@ -1,10 +1,9 @@
1
1
  """Detect web framework endpoints (Flask, FastAPI, Django) from AST analysis."""
2
2
 
3
- import ast
4
3
  import re
5
4
  from dataclasses import dataclass, field
6
5
  from pathlib import Path
7
- from typing import Dict, List, Optional
6
+ from typing import List, Optional
8
7
 
9
8
  from code2llm.api import AnalysisResult, FunctionInfo
10
9
 
@@ -1,9 +1,8 @@
1
1
  """Wrapper around code2llm's ProjectAnalyzer for documentation purposes."""
2
2
 
3
- from pathlib import Path
4
3
  from typing import Optional
5
4
 
6
- from code2llm.api import Config, FAST_CONFIG, AnalysisResult, analyze
5
+ from code2llm.api import Config, AnalysisResult, analyze
7
6
 
8
7
  from ..config import Code2DocsConfig
9
8
 
@@ -32,7 +32,7 @@ def main() -> None:
32
32
  @click.option('--output', '-o', default=None, help='Output directory for docs')
33
33
  @click.option('--verbose', '-v', is_flag=True, help='Verbose output')
34
34
  @click.option('--dry-run', is_flag=True, help='Show what would be generated without writing')
35
- @click.option('--llm', 'llm_model', default=None, help='Enable LLM-assisted generation (e.g. openai/gpt-4o-mini, ollama/llama3)')
35
+ @click.option('--llm', 'llm_model', default=None, help='Enable LLM-assisted generation (e.g. openai/gpt-5.4-mini, ollama/llama3)')
36
36
  @click.option('--org-name', default=None, help='Organization name for org-mode README generation')
37
37
  def generate(project_path, config_path, readme_only, sections, output, verbose, dry_run, llm_model, org_name) -> None:
38
38
  """Generate documentation (default command)."""
@@ -5,7 +5,6 @@ Shows how to use individual generators, sync, and formatters.
5
5
  Run: python examples/advanced_usage.py
6
6
  """
7
7
 
8
- from pathlib import Path
9
8
 
10
9
  # ==================================================
11
10
  # Using individual generators
@@ -5,10 +5,8 @@ Minimal working examples for the most common use cases.
5
5
  Run: python examples/quickstart.py
6
6
  """
7
7
 
8
- from pathlib import Path
9
8
 
10
9
  from code2docs import Code2DocsConfig
11
- from code2docs import analyze_and_document
12
10
  from code2docs import generate_docs
13
11
  from code2docs import generate_readme
14
12
 
@@ -1,6 +1,6 @@
1
1
  """Markdown formatting utilities."""
2
2
 
3
- from typing import Dict, List, Optional
3
+ from typing import List
4
4
 
5
5
 
6
6
  class MarkdownFormatter:
@@ -1,9 +1,9 @@
1
1
  """API changelog generator — diff function/class signatures between versions."""
2
2
 
3
3
  import json
4
- from dataclasses import dataclass, field
4
+ from dataclasses import dataclass
5
5
  from pathlib import Path
6
- from typing import Dict, List, Optional, Tuple
6
+ from typing import Dict, List, Optional
7
7
 
8
8
  from code2llm.api import AnalysisResult
9
9
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  from collections import defaultdict
4
4
  from pathlib import Path
5
- from typing import Dict, List, Optional
5
+ from typing import Dict, List
6
6
 
7
7
  from code2llm.api import AnalysisResult, FunctionInfo, ClassInfo, ModuleInfo
8
8
 
@@ -3,7 +3,7 @@
3
3
  from pathlib import Path
4
4
  from typing import Dict, List, Set
5
5
 
6
- from code2llm.api import AnalysisResult, ModuleInfo
6
+ from code2llm.api import AnalysisResult
7
7
 
8
8
  from ..config import Code2DocsConfig
9
9
  from ..llm_helper import LLMHelper
@@ -1,7 +1,7 @@
1
1
  """Changelog generator from git log and API diff."""
2
2
 
3
3
  import subprocess
4
- from dataclasses import dataclass, field
4
+ from dataclasses import dataclass
5
5
  from pathlib import Path
6
6
  from typing import Dict, List, Optional
7
7
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  import subprocess
4
4
  from pathlib import Path
5
- from typing import List, Optional, Dict, Set
5
+ from typing import List, Optional, Dict
6
6
 
7
7
  from code2llm.api import AnalysisResult
8
8
 
@@ -1,7 +1,6 @@
1
1
  """Configuration documentation generator."""
2
2
 
3
3
  from dataclasses import fields, MISSING
4
- from typing import Any
5
4
 
6
5
  from code2llm.api import AnalysisResult
7
6
 
@@ -55,7 +54,7 @@ class ConfigDocsGenerator:
55
54
  "watch": "Enable file watcher for auto-resync",
56
55
  "ignore": "Glob patterns to ignore during sync",
57
56
  "enabled": "Enable LLM-assisted documentation generation",
58
- "model": "LLM model identifier (litellm format, e.g. `openai/gpt-4o-mini`, `ollama/llama3`)",
57
+ "model": "LLM model identifier (litellm format, e.g. `openai/gpt-5.4-mini`, `ollama/llama3`)",
59
58
  "api_key": "API key for the LLM provider (use `.env` or env var `CODE2DOCS_LLM_API_KEY`)",
60
59
  "api_base": "Custom API base URL (for self-hosted or proxy endpoints)",
61
60
  "max_tokens": "Maximum tokens per LLM call",
@@ -1,6 +1,6 @@
1
1
  """CONTRIBUTING.md generator from project tooling detection."""
2
2
 
3
- from typing import Dict, List
3
+ from typing import Dict
4
4
 
5
5
  from code2llm.api import AnalysisResult
6
6
 
@@ -4,7 +4,7 @@ import ast
4
4
  from pathlib import Path
5
5
  from typing import Dict, List, Set, Tuple
6
6
 
7
- from code2llm.api import AnalysisResult, ModuleInfo
7
+ from code2llm.api import AnalysisResult
8
8
 
9
9
  from ..config import Code2DocsConfig
10
10
 
@@ -1,12 +1,32 @@
1
1
 
2
-
3
2
  PORT_4 = 4
4
3
  CONSTANT_5 = 5
5
4
  CONSTANT_50 = 50
6
5
 
7
- PORT_4 = 4
8
- CONSTANT_5 = 5
9
- CONSTANT_50 = 50
6
+
7
+ PORT_4 = PORT_4
8
+ CONSTANT_5 = CONSTANT_5
9
+ CONSTANT_50 = CONSTANT_50
10
+
11
+
12
+ PORT_4 = PORT_4
13
+ CONSTANT_5 = CONSTANT_5
14
+ CONSTANT_50 = CONSTANT_50
15
+
16
+
17
+ PORT_4 = PORT_4
18
+ CONSTANT_5 = CONSTANT_5
19
+ CONSTANT_50 = CONSTANT_50
20
+
21
+
22
+
23
+ PORT_4 = PORT_4
24
+ CONSTANT_5 = CONSTANT_5
25
+ CONSTANT_50 = CONSTANT_50
26
+
27
+ PORT_4 = PORT_4
28
+ CONSTANT_5 = CONSTANT_5
29
+ CONSTANT_50 = CONSTANT_50
10
30
 
11
31
  """Auto-generate usage examples from public signatures and entry points."""
12
32
 
@@ -154,7 +174,7 @@ class ExamplesGenerator:
154
174
  source = self.config.source or "./"
155
175
  output = self.config.output or "./docs"
156
176
  if config_cls:
157
- lines.append('# ' + '=' * 50)
177
+ lines.append('# ' + '=' * CONSTANT_50)
158
178
  lines.append("# Example 1: Configuration")
159
179
  lines.append('# ' + '=' * CONSTANT_50)
160
180
  lines.append("")
@@ -168,7 +188,7 @@ class ExamplesGenerator:
168
188
 
169
189
  # --- Example 2: Quick generate ---
170
190
  lines.append("")
171
- lines.append('# ' + '=' * 50)
191
+ lines.append('# ' + '=' * CONSTANT_50)
172
192
  lines.append("# Example 2: Generate documentation")
173
193
  lines.append('# ' + '=' * CONSTANT_50)
174
194
  lines.append("")
@@ -195,7 +215,7 @@ class ExamplesGenerator:
195
215
  scanner_cls = self._find_class_by_name("ProjectScanner")
196
216
  if scanner_cls:
197
217
  lines.append("")
198
- lines.append('# ' + '=' * 50)
218
+ lines.append('# ' + '=' * CONSTANT_50)
199
219
  lines.append("# Example 3: Analyze a project programmatically")
200
220
  lines.append('# ' + '=' * CONSTANT_50)
201
221
  lines.append("")
@@ -231,13 +251,13 @@ class ExamplesGenerator:
231
251
  # --- Individual generators ---
232
252
  gen_classes = self._find_generator_classes()
233
253
  if gen_classes:
234
- lines.append('# ' + '=' * 50)
254
+ lines.append('# ' + '=' * CONSTANT_50)
235
255
  lines.append("# Using individual generators")
236
256
  lines.append('# ' + '=' * CONSTANT_50)
237
257
  lines.append("")
238
258
  lines.append(f"from {pkg} import Code2DocsConfig")
239
259
  lines.append(f"from {pkg}.analyzers.project_scanner import ProjectScanner")
240
- for cls in gen_classes[:4]:
260
+ for cls in gen_classes[:PORT_4]:
241
261
  mod = cls.module or pkg
242
262
  if not mod.startswith(pkg):
243
263
  mod = f"{pkg}.{mod}"
@@ -250,7 +270,7 @@ class ExamplesGenerator:
250
270
  lines.append('result = scanner.analyze(f"./{project_name_adv}") if project_name_adv != "." else scanner.analyze("./")')
251
271
  lines.append("")
252
272
 
253
- for i, cls in enumerate(gen_classes[:4], start=2):
273
+ for i, cls in enumerate(gen_classes[:PORT_4], start=2):
254
274
  gen_name = cls.name[0].lower() + cls.name[1:]
255
275
  gen_name = gen_name.replace("Generator", "_gen")
256
276
  lines.append(f"# Step {i}: Generate with {cls.name}")
@@ -280,7 +300,7 @@ class ExamplesGenerator:
280
300
  fmt_funcs = [f for f in fmt_funcs if f]
281
301
  if fmt_funcs:
282
302
  lines.append("")
283
- lines.append('# ' + '=' * 50)
303
+ lines.append('# ' + '=' * CONSTANT_50)
284
304
  lines.append("# Formatters")
285
305
  lines.append('# ' + '=' * CONSTANT_50)
286
306
  lines.append("")
@@ -315,7 +335,7 @@ class ExamplesGenerator:
315
335
  differ_cls = self._find_class_by_name("Differ")
316
336
  if differ_cls:
317
337
  lines.append("")
318
- lines.append('# ' + '=' * 50)
338
+ lines.append('# ' + '=' * CONSTANT_50)
319
339
  lines.append("# Sync — detect and apply changes")
320
340
  lines.append('# ' + '=' * CONSTANT_50)
321
341
  lines.append("")
@@ -406,7 +426,7 @@ class ExamplesGenerator:
406
426
  """Build a realistic argument string for a function call."""
407
427
  args = [a for a in func.args if a not in ("self", "cls")]
408
428
  parts = []
409
- for arg in args[:5]:
429
+ for arg in args[:CONSTANT_5]:
410
430
  val = self._get_example_value(arg)
411
431
  if not val or val == '"..."':
412
432
  # Try type hint
@@ -1,6 +1,6 @@
1
1
  """Getting Started guide generator."""
2
2
 
3
- from typing import List, Optional
3
+ from typing import List
4
4
 
5
5
  from code2llm.api import AnalysisResult
6
6
 
@@ -1,7 +1,7 @@
1
1
  """Organization README generator - generates overview of multiple projects."""
2
2
 
3
3
  from pathlib import Path
4
- from typing import Dict, List, Optional
4
+ from typing import Dict, Optional
5
5
 
6
6
  from code2llm.api import AnalysisResult
7
7
 
@@ -5,6 +5,34 @@ CONSTANT_15 = 15
5
5
  CONSTANT_20 = 20
6
6
  CONSTANT_30 = 30
7
7
 
8
+
9
+ CONSTANT_3 = CONSTANT_3
10
+ CONSTANT_5 = CONSTANT_5
11
+ CONSTANT_15 = CONSTANT_15
12
+ CONSTANT_20 = CONSTANT_20
13
+ CONSTANT_30 = CONSTANT_30
14
+
15
+
16
+ CONSTANT_3 = CONSTANT_3
17
+ CONSTANT_5 = CONSTANT_5
18
+ CONSTANT_15 = CONSTANT_15
19
+ CONSTANT_20 = CONSTANT_20
20
+ CONSTANT_30 = CONSTANT_30
21
+
22
+
23
+ CONSTANT_3 = CONSTANT_3
24
+ CONSTANT_5 = CONSTANT_5
25
+ CONSTANT_15 = CONSTANT_15
26
+ CONSTANT_20 = CONSTANT_20
27
+ CONSTANT_30 = CONSTANT_30
28
+
29
+
30
+ CONSTANT_3 = CONSTANT_3
31
+ CONSTANT_5 = CONSTANT_5
32
+ CONSTANT_15 = CONSTANT_15
33
+ CONSTANT_20 = CONSTANT_20
34
+ CONSTANT_30 = CONSTANT_30
35
+
8
36
  """README.md generator from AnalysisResult."""
9
37
  import re
10
38
  from pathlib import Path
@@ -90,7 +118,7 @@ class ReadmeGenerator:
90
118
  def _generate_description(self, project_name: str, entry_points: List[str]) -> str:
91
119
  """Generate project description: LLM if available, else package docstring."""
92
120
  if self.llm.available:
93
- modules_summary = ', '.join(sorted(self.result.modules.keys())[:15])
121
+ modules_summary = ', '.join(sorted(self.result.modules.keys())[:CONSTANT_15])
94
122
  eps_str = ', '.join(entry_points[:10])
95
123
  llm_desc = self.llm.generate_project_description(project_name, modules_summary, eps_str)
96
124
  if llm_desc:
@@ -143,7 +171,7 @@ class ReadmeGenerator:
143
171
  result = subprocess.run(['git', 'shortlog', '-sne', 'HEAD'], capture_output=True, text=True, cwd=self.result.project_path)
144
172
  if result.returncode == 0:
145
173
  contributors = []
146
- for line in result.stdout.strip().split('\n')[:5]:
174
+ for line in result.stdout.strip().split('\n')[:CONSTANT_5]:
147
175
  parts = line.split('\t')
148
176
  if len(parts) >= 2:
149
177
  contributors.append(parts[1])
@@ -257,7 +285,7 @@ class ReadmeGenerator:
257
285
  lang_map = {'python': 'python', 'javascript': 'javascript', 'typescript': 'typescript', 'rust': 'rust', 'go': 'go'}
258
286
  code_lang = lang_map.get(lang, lang)
259
287
  parts.append(f'```{code_lang}')
260
- parts.append(f"// Entry points: {', '.join(entry_points[:3])}")
288
+ parts.append(f"// Entry points: {', '.join(entry_points[:CONSTANT_3])}")
261
289
  parts.append('```\n')
262
290
  return '\n'.join(parts)
263
291
 
@@ -265,12 +293,12 @@ class ReadmeGenerator:
265
293
  def _build_api_section(_project_name: str, context: Dict) -> str:
266
294
  """Build API overview section with classes and functions."""
267
295
  parts = ['## API Overview\n']
268
- for name, cls in list(context.get('public_classes', {}).items())[:20]:
296
+ for name, cls in list(context.get('public_classes', {}).items())[:CONSTANT_20]:
269
297
  doc = f' — {cls.docstring.splitlines()[0]}' if cls.docstring else ''
270
298
  parts.append(f'- **`{cls.name}`**{doc}')
271
299
  parts.append('')
272
- for name, func in list(context.get('public_functions', {}).items())[:30]:
273
- args_str = ', '.join(func.args[:5])
300
+ for name, func in list(context.get('public_functions', {}).items())[:CONSTANT_30]:
301
+ args_str = ', '.join(func.args[:CONSTANT_5])
274
302
  ret = f' → {func.returns}' if func.returns else ''
275
303
  parts.append(f'- `{func.name}({args_str}){ret}`')
276
304
  parts.append('')
@@ -2,7 +2,7 @@
2
2
 
3
3
  import hashlib
4
4
  import json
5
- from dataclasses import dataclass, field
5
+ from dataclasses import dataclass
6
6
  from pathlib import Path
7
7
  from typing import Dict, List, Optional
8
8
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code2docs
3
- Version: 3.0.26
3
+ Version: 3.0.27
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
@@ -56,17 +56,17 @@ Dynamic: license-file
56
56
 
57
57
  ## AI Cost Tracking
58
58
 
59
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-3.0.26-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
59
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-3.0.27-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
60
60
  ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-18.6h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
61
61
 
62
- - 🤖 **LLM usage:** $7.5000 (58 commits)
62
+ - 🤖 **LLM usage:** $7.5000 (59 commits)
63
63
  - 👤 **Human dev:** ~$1857 (18.6h @ $100/h, 30min dedup)
64
64
 
65
- Generated on 2026-04-08 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
65
+ Generated on 2026-04-09 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
66
66
 
67
67
  ---
68
68
 
69
- ![version](https://img.shields.io/badge/version-3.0.26-blue) ![python](https://img.shields.io/badge/python-%3E%3D3.9-blue) ![docs](https://img.shields.io/badge/docs-auto--generated-blueviolet)
69
+ ![version](https://img.shields.io/badge/version-3.0.27-blue) ![python](https://img.shields.io/badge/python-%3E%3D3.9-blue) ![docs](https://img.shields.io/badge/docs-auto--generated-blueviolet)
70
70
 
71
71
  > Auto-generate and sync project documentation from source code analysis.
72
72
 
@@ -206,7 +206,7 @@ code2docs can update only specific sections of an existing README using markers:
206
206
  ```markdown
207
207
  <!-- code2docs:start --># code2docs
208
208
 
209
- ![version](https://img.shields.io/badge/version-3.0.26-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)
209
+ ![version](https://img.shields.io/badge/version-3.0.27-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)
210
210
  > **276** functions | **57** classes | **51** files | CC̄ = 3.8
211
211
 
212
212
  > Auto-generated project documentation from source code analysis.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code2docs"
7
- version = "3.0.26"
7
+ version = "3.0.27"
8
8
  description = "Auto-generate and sync project documentation from source code analysis"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -5,9 +5,9 @@ from pathlib import Path
5
5
 
6
6
  import pytest
7
7
 
8
- from code2docs.analyzers.docstring_extractor import DocstringExtractor, DocstringInfo
8
+ from code2docs.analyzers.docstring_extractor import DocstringExtractor
9
9
  from code2docs.analyzers.dependency_scanner import DependencyScanner
10
- from code2docs.analyzers.endpoint_detector import EndpointDetector, Endpoint
10
+ from code2docs.analyzers.endpoint_detector import EndpointDetector
11
11
 
12
12
 
13
13
  class TestDocstringExtractor:
@@ -3,9 +3,8 @@
3
3
  import tempfile
4
4
  from pathlib import Path
5
5
 
6
- import pytest
7
6
 
8
- from code2docs.config import Code2DocsConfig, ReadmeConfig, DocsConfig, SyncConfig
7
+ from code2docs.config import Code2DocsConfig
9
8
 
10
9
 
11
10
  class TestCode2DocsConfig:
@@ -1,6 +1,5 @@
1
1
  """Tests for formatters (markdown, badges, toc)."""
2
2
 
3
- import pytest
4
3
 
5
4
  from code2docs.formatters.markdown import MarkdownFormatter
6
5
  from code2docs.formatters.badges import generate_badges
@@ -10,16 +10,64 @@ CONSTANT_30 = 30
10
10
  CONSTANT_50 = 50
11
11
  CONSTANT_80 = 80
12
12
 
13
- CONSTANT_3 = 3
14
- PORT_4 = 4
15
- CONSTANT_5 = 5
16
- CONSTANT_6 = 6
17
- CONSTANT_8 = 8
18
- CONSTANT_15 = 15
19
- CONSTANT_20 = 20
20
- CONSTANT_30 = 30
21
- CONSTANT_50 = 50
22
- CONSTANT_80 = 80
13
+
14
+ CONSTANT_3 = CONSTANT_3
15
+ PORT_4 = PORT_4
16
+ CONSTANT_5 = CONSTANT_5
17
+ CONSTANT_6 = CONSTANT_6
18
+ CONSTANT_8 = CONSTANT_8
19
+ CONSTANT_15 = CONSTANT_15
20
+ CONSTANT_20 = CONSTANT_20
21
+ CONSTANT_30 = CONSTANT_30
22
+ CONSTANT_50 = CONSTANT_50
23
+ CONSTANT_80 = CONSTANT_80
24
+
25
+
26
+ CONSTANT_3 = CONSTANT_3
27
+ PORT_4 = PORT_4
28
+ CONSTANT_5 = CONSTANT_5
29
+ CONSTANT_6 = CONSTANT_6
30
+ CONSTANT_8 = CONSTANT_8
31
+ CONSTANT_15 = CONSTANT_15
32
+ CONSTANT_20 = CONSTANT_20
33
+ CONSTANT_30 = CONSTANT_30
34
+ CONSTANT_50 = CONSTANT_50
35
+ CONSTANT_80 = CONSTANT_80
36
+
37
+
38
+ CONSTANT_3 = CONSTANT_3
39
+ PORT_4 = PORT_4
40
+ CONSTANT_5 = CONSTANT_5
41
+ CONSTANT_6 = CONSTANT_6
42
+ CONSTANT_8 = CONSTANT_8
43
+ CONSTANT_15 = CONSTANT_15
44
+ CONSTANT_20 = CONSTANT_20
45
+ CONSTANT_30 = CONSTANT_30
46
+ CONSTANT_50 = CONSTANT_50
47
+ CONSTANT_80 = CONSTANT_80
48
+
49
+
50
+ CONSTANT_3 = CONSTANT_3
51
+ PORT_4 = PORT_4
52
+ CONSTANT_5 = CONSTANT_5
53
+ CONSTANT_6 = CONSTANT_6
54
+ CONSTANT_8 = CONSTANT_8
55
+ CONSTANT_15 = CONSTANT_15
56
+ CONSTANT_20 = CONSTANT_20
57
+ CONSTANT_30 = CONSTANT_30
58
+ CONSTANT_50 = CONSTANT_50
59
+ CONSTANT_80 = CONSTANT_80
60
+
61
+ CONSTANT_3 = CONSTANT_3
62
+ PORT_4 = PORT_4
63
+ CONSTANT_5 = CONSTANT_5
64
+ CONSTANT_6 = CONSTANT_6
65
+ CONSTANT_8 = CONSTANT_8
66
+ CONSTANT_15 = CONSTANT_15
67
+ CONSTANT_20 = CONSTANT_20
68
+ CONSTANT_30 = CONSTANT_30
69
+ CONSTANT_50 = CONSTANT_50
70
+ CONSTANT_80 = CONSTANT_80
23
71
  'Integration tests for generators using mock AnalysisResult.'
24
72
  import tempfile
25
73
  from pathlib import Path
@@ -31,8 +79,8 @@ def _make_result() -> AnalysisResult:
31
79
  result = AnalysisResult(project_path='/tmp/mockproject')
32
80
  result.modules = {'mylib.core': ModuleInfo(name='mylib.core', file='/tmp/mockproject/mylib/core.py', imports=['os', 'json', 'mylib.utils'], functions=['process', 'validate'], classes=['Engine']), 'mylib.utils': ModuleInfo(name='mylib.utils', file='/tmp/mockproject/mylib/utils.py', imports=['re'], functions=['slugify', 'sanitize'], classes=[])}
33
81
  result.classes = {'mylib.core.Engine': ClassInfo(name='Engine', qualified_name='mylib.core.Engine', file='/tmp/mockproject/mylib/core.py', line=10, module='mylib.core', bases=['BaseEngine'], methods=['run', 'stop'], docstring='Main processing engine.')}
34
- result.functions = {'mylib.core.process': FunctionInfo(name='process', qualified_name='mylib.core.process', file='/tmp/mockproject/mylib/core.py', line=50, module='mylib.core', args=['data', 'config'], returns='Result', docstring='Process input data.', complexity={'cyclomatic': 5}), 'mylib.core.validate': FunctionInfo(name='validate', qualified_name='mylib.core.validate', file='/tmp/mockproject/mylib/core.py', line=80, module='mylib.core', args=['schema', 'payload'], returns='bool', docstring='Validate payload against schema.', complexity={'cyclomatic': 8}), 'mylib.core.Engine.run': FunctionInfo(name='run', qualified_name='mylib.core.Engine.run', file='/tmp/mockproject/mylib/core.py', line=15, module='mylib.core', is_method=True, args=['self', 'input'], returns='Output', docstring='Run the engine.', complexity={'cyclomatic': 3}), 'mylib.core.Engine.stop': FunctionInfo(name='stop', qualified_name='mylib.core.Engine.stop', file='/tmp/mockproject/mylib/core.py', line=30, module='mylib.core', is_method=True, args=['self'], returns=None, docstring='Stop the engine.', complexity={'cyclomatic': 1}), 'mylib.utils.slugify': FunctionInfo(name='slugify', qualified_name='mylib.utils.slugify', file='/tmp/mockproject/mylib/utils.py', line=5, module='mylib.utils', args=['text'], returns='str', docstring='Convert text to slug.', complexity={'cyclomatic': 2}), 'mylib.utils.sanitize': FunctionInfo(name='sanitize', qualified_name='mylib.utils.sanitize', file='/tmp/mockproject/mylib/utils.py', line=20, module='mylib.utils', args=['html'], returns='str', docstring=None, complexity={'cyclomatic': 4})}
35
- result.stats = {'files_processed': 2, 'functions_found': 6, 'classes_found': 1}
82
+ result.functions = {'mylib.core.process': FunctionInfo(name='process', qualified_name='mylib.core.process', file='/tmp/mockproject/mylib/core.py', line=CONSTANT_50, module='mylib.core', args=['data', 'config'], returns='Result', docstring='Process input data.', complexity={'cyclomatic': CONSTANT_5}), 'mylib.core.validate': FunctionInfo(name='validate', qualified_name='mylib.core.validate', file='/tmp/mockproject/mylib/core.py', line=CONSTANT_80, module='mylib.core', args=['schema', 'payload'], returns='bool', docstring='Validate payload against schema.', complexity={'cyclomatic': CONSTANT_8}), 'mylib.core.Engine.run': FunctionInfo(name='run', qualified_name='mylib.core.Engine.run', file='/tmp/mockproject/mylib/core.py', line=CONSTANT_15, module='mylib.core', is_method=True, args=['self', 'input'], returns='Output', docstring='Run the engine.', complexity={'cyclomatic': CONSTANT_3}), 'mylib.core.Engine.stop': FunctionInfo(name='stop', qualified_name='mylib.core.Engine.stop', file='/tmp/mockproject/mylib/core.py', line=CONSTANT_30, module='mylib.core', is_method=True, args=['self'], returns=None, docstring='Stop the engine.', complexity={'cyclomatic': 1}), 'mylib.utils.slugify': FunctionInfo(name='slugify', qualified_name='mylib.utils.slugify', file='/tmp/mockproject/mylib/utils.py', line=CONSTANT_5, module='mylib.utils', args=['text'], returns='str', docstring='Convert text to slug.', complexity={'cyclomatic': 2}), 'mylib.utils.sanitize': FunctionInfo(name='sanitize', qualified_name='mylib.utils.sanitize', file='/tmp/mockproject/mylib/utils.py', line=CONSTANT_20, module='mylib.utils', args=['html'], returns='str', docstring=None, complexity={'cyclomatic': PORT_4})}
83
+ result.stats = {'files_processed': 2, 'functions_found': CONSTANT_6, 'classes_found': 1}
36
84
  result.entry_points = ['mylib.core.process']
37
85
  return result
38
86
 
@@ -227,7 +275,7 @@ class TestArchitectureGenerator:
227
275
  gen = ArchitectureGenerator(config, result)
228
276
  content = gen.generate()
229
277
  assert 'Architecture' in content
230
- assert len(content) > 50
278
+ assert len(content) > CONSTANT_50
231
279
 
232
280
  def test_contains_mermaid(self) -> None:
233
281
  from code2docs.generators.architecture_gen import ArchitectureGenerator
@@ -2,10 +2,9 @@
2
2
 
3
3
  from unittest.mock import patch, MagicMock
4
4
 
5
- import pytest
6
5
 
7
6
  from code2docs.config import LLMConfig, Code2DocsConfig
8
- from code2docs.llm_helper import LLMHelper, _get_litellm
7
+ from code2docs.llm_helper import LLMHelper
9
8
 
10
9
 
11
10
  # ── LLMConfig tests ──────────────────────────────────────────────────────
@@ -28,7 +27,7 @@ class TestLLMConfig:
28
27
 
29
28
  def test_from_env_with_model(self):
30
29
  env = {
31
- "CODE2DOCS_LLM_MODEL": "openai/gpt-4o-mini",
30
+ "CODE2DOCS_LLM_MODEL": "openai/gpt-5.4-mini",
32
31
  "CODE2DOCS_LLM_API_KEY": "sk-test123",
33
32
  "CODE2DOCS_LLM_MAX_TOKENS": "2048",
34
33
  "CODE2DOCS_LLM_TEMPERATURE": "0.5",
@@ -36,7 +35,7 @@ class TestLLMConfig:
36
35
  with patch.dict("os.environ", env, clear=True):
37
36
  cfg = LLMConfig.from_env()
38
37
  assert cfg.enabled is True
39
- assert cfg.model == "openai/gpt-4o-mini"
38
+ assert cfg.model == "openai/gpt-5.4-mini"
40
39
  assert cfg.api_key == "sk-test123"
41
40
  assert cfg.max_tokens == 2048
42
41
  assert cfg.temperature == 0.5
@@ -59,7 +58,7 @@ class TestLLMHelperDisabled:
59
58
  """Test that LLMHelper returns None for everything when disabled."""
60
59
 
61
60
  def test_not_available_when_disabled(self):
62
- cfg = LLMConfig(enabled=False, model="openai/gpt-4o-mini")
61
+ cfg = LLMConfig(enabled=False, model="openai/gpt-5.4-mini")
63
62
  helper = LLMHelper(cfg)
64
63
  assert helper.available is False
65
64
 
@@ -103,7 +102,7 @@ class TestLLMHelperEnabled:
103
102
  @staticmethod
104
103
  def _make_helper():
105
104
  cfg = LLMConfig(
106
- enabled=True, model="openai/gpt-4o-mini",
105
+ enabled=True, model="openai/gpt-5.4-mini",
107
106
  api_key="sk-test", max_tokens=512, temperature=0.2,
108
107
  )
109
108
  return LLMHelper(cfg)
@@ -128,7 +127,7 @@ class TestLLMHelperEnabled:
128
127
  assert result == "Hello world"
129
128
  mock_litellm.completion.assert_called_once()
130
129
  call_kwargs = mock_litellm.completion.call_args[1]
131
- assert call_kwargs["model"] == "openai/gpt-4o-mini"
130
+ assert call_kwargs["model"] == "openai/gpt-5.4-mini"
132
131
  assert call_kwargs["max_tokens"] == 512
133
132
  assert call_kwargs["temperature"] == 0.2
134
133
  assert len(call_kwargs["messages"]) == 2
@@ -162,7 +161,7 @@ class TestLLMHelperEnabled:
162
161
 
163
162
  def test_api_key_passed_when_set(self):
164
163
  cfg = LLMConfig(
165
- enabled=True, model="openai/gpt-4o-mini",
164
+ enabled=True, model="openai/gpt-5.4-mini",
166
165
  api_key="sk-secret", max_tokens=1024, temperature=0.3,
167
166
  )
168
167
  helper = LLMHelper(cfg)
@@ -1,10 +1,7 @@
1
1
  """Tests for BaseGenerator, GeneratorRegistry, and adapter integration."""
2
2
 
3
- import tempfile
4
3
  from pathlib import Path
5
- from typing import Optional
6
4
 
7
- import pytest
8
5
 
9
6
  from code2llm.core.models import (
10
7
  AnalysisResult, FunctionInfo, ClassInfo, ModuleInfo,
@@ -3,7 +3,6 @@
3
3
  import tempfile
4
4
  from pathlib import Path
5
5
 
6
- import pytest
7
6
 
8
7
  from code2docs.sync.differ import Differ, ChangeInfo
9
8
 
File without changes
File without changes
File without changes
File without changes