Files
roboco/tests/unit/test_regenerate_verb_tables.py
T

70 lines
2.3 KiB
Python
Raw Normal View History

2026-06-30 08:08:35 +02:00
"""regenerate_verb_tables._annot_str strips Annotated metadata (#199).
The flow schemas type coercion-hardened list fields as
``StrList = Annotated[list[str], BeforeValidator(coerce_str_list)]``. The old
``_annot_str`` walked ``get_args(Annotated[list[str], BeforeValidator(...)])``
and rendered the ``BeforeValidator(...)`` repr into the generated
``verbs.md`` / per-role prompts — leaking an internal validator object (with
a memory address) into agent-facing prompt text. The fix unwraps
``Annotated[T, ...]`` to ``T`` before rendering.
"""
from __future__ import annotations
import importlib.util
from pathlib import Path
from typing import TYPE_CHECKING, Annotated
from pydantic import BaseModel, BeforeValidator, Field
if TYPE_CHECKING:
from types import ModuleType
_SCRIPT = Path(__file__).resolve().parents[2] / "scripts" / "regenerate_verb_tables.py"
def _load_module() -> ModuleType:
spec = importlib.util.spec_from_file_location("_regen_verb_tables", _SCRIPT)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def _coerce(x: object) -> list[str]:
return [str(x)]
_StrList = Annotated[list[str], BeforeValidator(_coerce)]
def test_annot_str_strips_before_validator_metadata() -> None:
mod = _load_module()
assert mod._annot_str(_StrList) == "list[str]"
def test_annot_str_strips_metadata_in_optional_union() -> None:
# The schemas use ``StrList | None``; the metadata must drop on both arms.
mod = _load_module()
assert mod._annot_str(_StrList | None) == "list[str] | None"
def test_annot_str_plain_types_unchanged() -> None:
mod = _load_module()
assert mod._annot_str(list[str]) == "list[str]"
assert mod._annot_str(str | None) == "str | None"
assert mod._annot_str(None) == "None"
def test_signature_for_schema_has_no_beforevalidator_repr() -> None:
# End-to-end: a schema with a StrList | None field renders a clean
# signature, no ``BeforeValidator(func=...)`` leak.
class _Sample(BaseModel):
ac_verdicts: _StrList | None = Field(default=None)
notes: str
mod = _load_module()
sig = mod._signature_for_schema(_Sample)
assert "BeforeValidator" not in sig
assert "list[str] | None" in sig