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
8 changes: 5 additions & 3 deletions milestones/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ repositories that install `docgen` and maintain their own demo bundle. The
library no longer ships an in-repo dogfood; consumers are the integration test
of record.

**Active:** **[visual-map-field-strings.md](visual-map-field-strings.md)** —
`visual_map` type/scene/source and `segment_names` values must be YAML
strings at config load.
**Active:** **[path-config-strings.md](path-config-strings.md)** —
`env_file` / `repo_root` / `dirs.*` must be YAML strings at config load.

**Shipped:**
- **[visual-map-field-strings.md](visual-map-field-strings.md)** —
`visual_map` type/scene/source and `segment_names` values must be YAML
strings (#108).
- **[generation-model-strings.md](generation-model-strings.md)** —
`narration_from_source` / `manim_scene_generation` model and prompt
keys must be YAML strings (#107).
Expand Down
38 changes: 38 additions & 0 deletions milestones/path-config-strings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Milestone: env_file / repo_root / dirs paths must be strings

**Status:** Active
**PR:** [#109](https://github.com/jmjava/documentation-generator/pull/109)
**Depends on:** `milestones/visual-map-field-strings.md` (PR #108)

## Problem

`manim.manim_path` is already a string at config load. These path keys
were not:

1. **`env_file` / `repo_root`** — a YAML list loaded successfully, then
`Path / list` TypeError’d when CLI applied the env file or the wizard
resolved `repo_root`.
2. **`dirs.narration` / `audio` / `animations` / `recordings` / `hints`**
— a list TypeError’d during `Config.__post_init__` Path joins instead
of raising `ConfigError`.

## Goal

Fail closed at `Config.from_yaml` with `ConfigError`. Missing keys still
use defaults (`narration/`, git-discovered repo root, no `env_file`).

## Done when

- [x] Present `env_file` / `repo_root` must be non-empty YAML strings.
- [x] Present `dirs.narration` / `audio` / `animations` / `recordings` /
`hints` must be non-empty YAML strings.
- [x] Tests for list values of those keys.
- [x] `ruff check src/ tests/`
- [x] `pytest tests/`
- [x] `docgen benchmark` (no clock change)

## Out of scope

- `wizard.default_guidance` type gating is separate.
- Per-segment generation `system_prompt` / `class_name` are separate.
- `pages` output directory keys are separate.
4 changes: 2 additions & 2 deletions milestones/visual-map-field-strings.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Milestone: visual_map type/scene/source and segment_names values must be strings

**Status:** Active
**PR:** [#108](https://github.com/jmjava/documentation-generator/pull/108)
**Status:** Shipped
**PR:** #108
**Depends on:** `milestones/generation-model-strings.md` (PR #107),
`milestones/visual-map-row-types.md` (PR #86),
`milestones/segment-id-strings.md` (PR #87)
Expand Down
8 changes: 8 additions & 0 deletions src/docgen/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,11 @@ class Config:
hints_dir: Path = field(init=False)

def __post_init__(self) -> None:
src = self._source_label()
dirs = self._block("dirs")
for dkey in ("narration", "audio", "animations", "recordings", "hints"):
if dirs.get(dkey) is not None:
require_yaml_string(dirs[dkey], label=f"dirs.{dkey}", source=src)
self.narration_dir = self.base_dir / dirs.get("narration", "narration")
self.audio_dir = self.base_dir / dirs.get("audio", "audio")
self.animations_dir = self.base_dir / dirs.get("animations", "animations")
Expand Down Expand Up @@ -347,6 +351,10 @@ def __post_init__(self) -> None:
f"{src}: manim_scene_generation.scene_spec_system_prompt must be a "
f"YAML string, not {type(msg['scene_spec_system_prompt']).__name__}"
)
if self.raw.get("env_file") is not None:
require_yaml_string(self.raw["env_file"], label="env_file", source=src)
if self.raw.get("repo_root") is not None:
require_yaml_string(self.raw["repo_root"], label="repo_root", source=src)
ocr = self._sub_block(validation, "ocr", label="validation.ocr")
if ocr.get("error_patterns") is not None:
string_list_block(
Expand Down
21 changes: 21 additions & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -631,3 +631,24 @@ def test_from_yaml_list_segment_names_value_raises(tmp_path: Path) -> None:
p.write_text('segment_names:\n "01":\n - 01-intro\n', encoding="utf-8")
with pytest.raises(ConfigError, match="segment_names.01 must be a YAML string"):
Config.from_yaml(p)


def test_from_yaml_list_env_file_raises(tmp_path: Path) -> None:
p = tmp_path / "docgen.yaml"
p.write_text("env_file:\n - .env\n", encoding="utf-8")
with pytest.raises(ConfigError, match="env_file must be a YAML string"):
Config.from_yaml(p)


def test_from_yaml_list_repo_root_raises(tmp_path: Path) -> None:
p = tmp_path / "docgen.yaml"
p.write_text("repo_root:\n - ..\n", encoding="utf-8")
with pytest.raises(ConfigError, match="repo_root must be a YAML string"):
Config.from_yaml(p)


def test_from_yaml_list_dirs_narration_raises(tmp_path: Path) -> None:
p = tmp_path / "docgen.yaml"
p.write_text("dirs:\n narration:\n - narration\n", encoding="utf-8")
with pytest.raises(ConfigError, match="dirs.narration must be a YAML string"):
Config.from_yaml(p)