standardize packaging, tooling, docs, CI, and licensing

This commit is contained in:
2026-09-10 18:49:04 +05:30
parent 8253c25928
commit 0129268cd3
61 changed files with 429 additions and 273 deletions

View File

@@ -1,6 +1,8 @@
from pathlib import Path
from docforge.cli.main import cli
def test_mcp_build(cli_runner):
with cli_runner.isolated_filesystem():
cwd = Path.cwd()

View File

@@ -1,6 +1,8 @@
from pathlib import Path
from docforge.cli.main import cli
def test_mkdocs_build_full_flow(
cli_runner,
mock_mkdocs_build,
@@ -17,11 +19,11 @@ def test_mkdocs_build_full_flow(
nav_file = cwd / "docforge.nav.yml"
nav_file.write_text("home: testpkg/index.md\ngroups: {}\n")
# We need to create a dummy testpkg/index.md for nav resolution if it's there
# But generate_sources will create it.
# But generate_sources will create it.
# Wait, the current logic runs generate_sources first, THEN generate_config.
result = cli_runner.invoke(
cli,
[
@@ -43,11 +45,13 @@ def test_mkdocs_build_full_flow(
assert (cwd / "mkdocs.yml").exists()
assert (cwd / "docs" / "testpkg" / "mod.md").exists()
def test_mkdocs_build_missing_module_fails(cli_runner):
result = cli_runner.invoke(cli, ["build", "--mkdocs", "--site-name", "Test"])
assert result.exit_code != 0
assert "--module is required" in result.output
def test_mkdocs_build_without_site_name_uses_module_as_default_full_flow(
cli_runner,
mock_mkdocs_build,

View File

@@ -1,5 +1,6 @@
from docforge.cli.main import cli
def test_mcp_serve(
cli_runner,
fake_mcp_docs,

View File

@@ -1,5 +1,6 @@
from docforge.cli.main import cli
def test_mkdocs_serve(
cli_runner,
fake_mkdocs_yml,

View File

@@ -1,8 +1,8 @@
import sys
import json
import pytest
import sys
from pathlib import Path
import pytest
from click.testing import CliRunner
@@ -45,6 +45,7 @@ plugins:
@pytest.fixture
def mock_mkdocs_load_config(monkeypatch):
"""Mock mkdocs.config.load_config."""
def fake_load_config(path):
return object() # dummy config object
@@ -81,6 +82,7 @@ def mock_mkdocs_serve(monkeypatch):
)
return lambda: called["value"]
@pytest.fixture
def fake_mcp_docs(tmp_path: Path) -> Path:
"""

View File

@@ -2,11 +2,9 @@ from docforge import GriffeLoader
def test_alias_does_not_crash(temp_package):
(temp_package / "alias.py").write_text(
'''from typing import List
(temp_package / "alias.py").write_text("""from typing import List
Alias = List[int]
'''
)
""")
loader = GriffeLoader()
project = loader.load_project(["testpkg.alias"])

View File

@@ -2,15 +2,13 @@ from docforge import GriffeLoader
def test_class_and_methods(temp_package):
(temp_package / "cls.py").write_text(
'''class MyClass:
(temp_package / "cls.py").write_text('''class MyClass:
"""Class doc."""
def method(self, x: int) -> int:
"""Method doc."""
return x
'''
)
''')
loader = GriffeLoader()
project = loader.load_project(["testpkg.cls"])

View File

@@ -2,12 +2,10 @@ from docforge import GriffeLoader
def test_function_signature(temp_package):
(temp_package / "fn.py").write_text(
'''def add(a: int, b: int = 1) -> int:
(temp_package / "fn.py").write_text('''def add(a: int, b: int = 1) -> int:
"""Adds numbers."""
return a + b
'''
)
''')
loader = GriffeLoader()
project = loader.load_project(["testpkg.fn"])

View File

@@ -1,4 +1,5 @@
import pytest
from docforge import GriffeLoader
@@ -6,9 +7,8 @@ def test_load_project_raises_on_missing_module_by_default():
loader = GriffeLoader()
with pytest.raises(ImportError):
loader.load_project(
["nonexistent.module", "sys"]
)
loader.load_project(["nonexistent.module", "sys"])
def test_load_project_skips_missing_modules_when_enabled():
loader = GriffeLoader()
@@ -18,4 +18,4 @@ def test_load_project_skips_missing_modules_when_enabled():
skip_import_errors=True,
)
assert "sys" in project.modules
assert "sys" in project.modules

View File

@@ -2,9 +2,7 @@ from docforge import GriffeLoader
def test_missing_docstrings(temp_package):
(temp_package / "nodoc.py").write_text(
'''def f(): pass'''
)
(temp_package / "nodoc.py").write_text("""def f(): pass""")
loader = GriffeLoader()
project = loader.load_project(["testpkg.nodoc"])

View File

@@ -2,11 +2,9 @@ from docforge import GriffeLoader
def test_private_members_excluded(temp_package):
(temp_package / "priv.py").write_text(
'''def _hidden(): pass
(temp_package / "priv.py").write_text("""def _hidden(): pass
def visible(): pass
'''
)
""")
loader = GriffeLoader()
project = loader.load_project(["testpkg.priv"])

View File

@@ -2,13 +2,11 @@ from docforge import GriffeLoader
def test_load_single_module(temp_package):
(temp_package / "mod.py").write_text(
'''"""Module docstring."""\n
(temp_package / "mod.py").write_text('''"""Module docstring."""\n
def foo():
"""Foo docstring."""
pass
'''
)
''')
loader = GriffeLoader()
project = loader.load_project(["testpkg.mod"])

View File

@@ -1,7 +1,6 @@
from pathlib import Path
from docforge.nav import ResolvedNav
from docforge.nav import MkDocsNavEmitter
from docforge.nav import MkDocsNavEmitter, ResolvedNav
def test_emit_mkdocs_nav():

View File

@@ -2,8 +2,7 @@ from pathlib import Path
import pytest
from docforge.nav import NavSpec
from docforge.nav import resolve_nav
from docforge.nav import NavSpec, resolve_nav
def _write_docs(root: Path, paths: list[str]) -> None:

View File

@@ -2,7 +2,7 @@ import json
from pathlib import Path
from docforge import MCPRenderer
from docforge.models import Project, Module
from docforge.models import Module, Project
def test_mcp_file_content(tmp_path: Path):

View File

@@ -1,7 +1,7 @@
from pathlib import Path
from docforge import MCPRenderer
from docforge.models import Project, Module
from docforge.models import Module, Project
def test_mcp_idempotent(tmp_path: Path):

View File

@@ -1,7 +1,7 @@
from pathlib import Path
from docforge.loaders import GriffeLoader, discover_module_paths
from docforge import MCPRenderer
from docforge.loaders import GriffeLoader, discover_module_paths
def test_mcp_emits_all_modules(tmp_path: Path) -> None:
@@ -22,10 +22,7 @@ def test_mcp_emits_all_modules(tmp_path: Path) -> None:
for p in (tmp_path / "modules").rglob("*.json")
}
expected = {
f"modules/{m.path}.json"
for m in project.get_all_modules()
}
expected = {f"modules/{m.path}.json" for m in project.get_all_modules()}
missing = expected - emitted
assert not missing, f"Missing MCP module JSON files: {missing}"

View File

@@ -1,7 +1,7 @@
from pathlib import Path
from docforge import MCPRenderer
from docforge.models import Project, Module
from docforge.models import Module, Project
def test_mcp_directory_structure(tmp_path: Path):

View File

@@ -1,7 +1,7 @@
from pathlib import Path
from docforge import MkDocsRenderer
from docforge.models import Project, Module
from docforge.models import Module, Project
def test_mkdocs_file_content(tmp_path: Path):
@@ -47,4 +47,4 @@ def test_generate_readme_source_root(tmp_path: Path):
content = readme.read_text()
assert "# testpkg" in content
assert "Test package documentation." in content
assert "Test package documentation." in content

View File

@@ -1,7 +1,7 @@
from pathlib import Path
from docforge import MkDocsRenderer
from docforge.models import Project, Module
from docforge.models import Module, Project
def test_mkdocs_idempotent(tmp_path: Path):
@@ -50,4 +50,4 @@ def test_generate_readme_idempotent(tmp_path: Path):
second = readme.read_text()
assert first == second
assert first == second

View File

@@ -1,7 +1,7 @@
from pathlib import Path
from docforge.loaders import GriffeLoader, discover_module_paths
from docforge import MkDocsRenderer
from docforge.loaders import GriffeLoader, discover_module_paths
def test_mkdocs_emits_all_modules(tmp_path: Path) -> None:
@@ -23,10 +23,7 @@ def test_mkdocs_emits_all_modules(tmp_path: Path) -> None:
module_is_source=True,
)
emitted = {
p.relative_to(tmp_path).as_posix()
for p in tmp_path.rglob("*.md")
}
emitted = {p.relative_to(tmp_path).as_posix() for p in tmp_path.rglob("*.md")}
module_paths = [m.path for m in project.get_all_modules()]
@@ -34,8 +31,7 @@ def test_mkdocs_emits_all_modules(tmp_path: Path) -> None:
for path in module_paths:
parts = path.split(".")
is_package = any(
other != path and other.startswith(path + ".")
for other in module_paths
other != path and other.startswith(path + ".") for other in module_paths
)
if is_package:

View File

@@ -1,7 +1,7 @@
from pathlib import Path
from docforge import MkDocsRenderer
from docforge.models import Project, Module
from docforge.models import Module, Project
def test_mkdocs_directory_structure(tmp_path: Path):