Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/adr/0012-settings-as-a-seam.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@ for Python tooling.
- `resolve_docs_dir()` (ADR-0007's four-tier, `docs_dir`-only function) stays in `settings.py` unused by the CLI;
nothing currently calls it outside tests.
It is not removed by this decision, since removing it is out of scope for the settings-seam work.
(Removed later by issue #166.)
92 changes: 1 addition & 91 deletions tests/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,102 +7,12 @@

import pytest

from wiki_toolkit.settings import build_context, resolve_docs_dir
from wiki_toolkit.settings import build_context

if TYPE_CHECKING:
from pathlib import Path


def test_resolve_docs_dir_defaults_to_cwd_docs(tmp_path: Path) -> None:
"""With nothing else set, docs_dir is cwd/docs, source is 'default'."""
result = resolve_docs_dir(cwd=tmp_path)

assert result.docs_dir == tmp_path / "docs"
assert result.source == "default"


def test_resolve_docs_dir_reads_pyproject_table(tmp_path: Path) -> None:
"""A [tool.wiki_toolkit] docs_dir in pyproject.toml is used when no flag/env is set."""
(tmp_path / "pyproject.toml").write_text('[tool.wiki_toolkit]\ndocs_dir = "custom-docs"\n')

result = resolve_docs_dir(cwd=tmp_path)

assert result.docs_dir == tmp_path / "custom-docs"
assert result.source == "pyproject"


def test_resolve_docs_dir_walks_upward_for_pyproject(tmp_path: Path) -> None:
"""The nearest pyproject.toml is found from a nested cwd, same convention as ruff/mypy."""
(tmp_path / "pyproject.toml").write_text('[tool.wiki_toolkit]\ndocs_dir = "custom-docs"\n')
nested = tmp_path / "a" / "b"
nested.mkdir(parents=True)

result = resolve_docs_dir(cwd=nested)

assert result.docs_dir == tmp_path / "custom-docs"
assert result.source == "pyproject"


def test_resolve_docs_dir_env_overrides_pyproject(tmp_path: Path, monkeypatch) -> None:
"""WIKI_TOOLKIT_DOCS_DIR wins over a pyproject.toml table."""
(tmp_path / "pyproject.toml").write_text('[tool.wiki_toolkit]\ndocs_dir = "custom-docs"\n')
monkeypatch.setenv("WIKI_TOOLKIT_DOCS_DIR", str(tmp_path / "env-docs"))

result = resolve_docs_dir(cwd=tmp_path)

assert result.docs_dir == tmp_path / "env-docs"
assert result.source == "env"


def test_resolve_docs_dir_flag_overrides_env(tmp_path: Path, monkeypatch) -> None:
"""A CLI flag wins over both env and pyproject.toml."""
monkeypatch.setenv("WIKI_TOOLKIT_DOCS_DIR", str(tmp_path / "env-docs"))

result = resolve_docs_dir(flag=tmp_path / "flag-docs", cwd=tmp_path)

assert result.docs_dir == tmp_path / "flag-docs"
assert result.source == "flag"


def test_resolve_docs_dir_missing_env_falls_through(tmp_path: Path, monkeypatch) -> None:
"""No env var set falls through to pyproject/default, doesn't error."""
monkeypatch.delenv("WIKI_TOOLKIT_DOCS_DIR", raising=False)

result = resolve_docs_dir(cwd=tmp_path)

assert result.source == "default"


def test_resolve_docs_dir_missing_table_falls_through_to_default(tmp_path: Path) -> None:
"""A pyproject.toml with no [tool.wiki_toolkit] table falls through to default."""
(tmp_path / "pyproject.toml").write_text('[tool.other]\nfoo = "bar"\n')

result = resolve_docs_dir(cwd=tmp_path)

assert result.docs_dir == tmp_path / "docs"
assert result.source == "default"


def test_resolve_docs_dir_wrong_type_docs_dir_falls_through_to_default(tmp_path: Path) -> None:
"""A [tool.wiki_toolkit] table whose docs_dir isn't a valid path falls through to default."""
(tmp_path / "pyproject.toml").write_text("[tool.wiki_toolkit]\ndocs_dir = 5\n")

result = resolve_docs_dir(cwd=tmp_path)

assert result.docs_dir == tmp_path / "docs"
assert result.source == "default"


def test_resolve_docs_dir_malformed_toml_falls_through_to_default(tmp_path: Path) -> None:
"""A pyproject.toml that fails to parse falls through to default rather than raising."""
(tmp_path / "pyproject.toml").write_text("not [ valid toml")

result = resolve_docs_dir(cwd=tmp_path)

assert result.docs_dir == tmp_path / "docs"
assert result.source == "default"


def test_build_context_defaults(tmp_path: Path) -> None:
"""With nothing else set, every field resolves to its built-in default."""
context, sources = build_context(cwd=tmp_path)
Expand Down
51 changes: 0 additions & 51 deletions wiki_toolkit/settings.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
"""Resolves wiki_toolkit configuration.

`resolve_docs_dir()` resolves just `docs_dir` (precedence: CLI flag >
`WIKI_TOOLKIT_DOCS_DIR` env var > nearest `pyproject.toml`'s `[tool.wiki_toolkit]`
table > built-in default).

`build_context()` resolves the full `Context` (docs_dir, repo_root, branch_prefix,
batch_byte_cap, batch_file_cap) through a five-tier precedence chain: CLI flag >
`WIKI_TOOLKIT_<UPPER_SNAKE>` env var > nearest `.wiki-toolkit.toml` (dedicated file,
Expand All @@ -29,7 +25,6 @@
TomlConfigSettingsSource,
)

ConfigSource = Literal["flag", "env", "pyproject", "default"]
ContextConfigSource = Literal["flag", "env", "dedicated_file", "pyproject", "default"]

DEDICATED_FILENAME = ".wiki-toolkit.toml"
Expand All @@ -39,52 +34,6 @@
_DEFAULT_BATCH_FILE_CAP = 20


class _EnvSettings(BaseSettings):
"""Reads `docs_dir` from the `WIKI_TOOLKIT_DOCS_DIR` environment variable."""

model_config = SettingsConfigDict(env_prefix="WIKI_TOOLKIT_")

docs_dir: Path | None = None


@dataclass
class ResolvedConfig:
"""The resolved `docs_dir` and which source produced it."""

docs_dir: Path
source: ConfigSource


def _find_pyproject_docs_dir(start: Path) -> Path | None:
"""Walk upward from `start` for the nearest pyproject.toml's `[tool.wiki_toolkit].docs_dir`."""
directory = _find_upward(start, "pyproject.toml")
if directory is None:
return None
result = _pyproject_context_fields(directory)
docs_dir = result[0].docs_dir if result is not None else None
if docs_dir is None:
return None
return docs_dir if docs_dir.is_absolute() else directory / docs_dir


def resolve_docs_dir(flag: Path | None = None, cwd: Path | None = None) -> ResolvedConfig:
"""Resolve `docs_dir` per precedence: flag > env > pyproject > default."""
if flag is not None:
return ResolvedConfig(docs_dir=flag, source="flag")

cwd = cwd or Path.cwd()

env_docs_dir = _EnvSettings().docs_dir
if env_docs_dir is not None:
return ResolvedConfig(docs_dir=env_docs_dir, source="env")

pyproject_docs_dir = _find_pyproject_docs_dir(cwd)
if pyproject_docs_dir is not None:
return ResolvedConfig(docs_dir=pyproject_docs_dir, source="pyproject")

return ResolvedConfig(docs_dir=cwd / "docs", source="default")


class Context(BaseModel):
"""The full resolved wiki_toolkit configuration."""

Expand Down
Loading