diff --git a/src/oold/model/_descriptor.py b/src/oold/model/_descriptor.py index 94b5314..c31a49a 100644 --- a/src/oold/model/_descriptor.py +++ b/src/oold/model/_descriptor.py @@ -189,20 +189,12 @@ def _neutralised(info: Any) -> Any: namespace[field_name] = _neutralised(info) continue if field_name not in namespace and _is_link_annotation(annotation): - # `manager: Link[Org]` with nothing assigned. It still needs a field - # carrying default=None - a link cannot be required at the pydantic - # level, because its value never reaches validation, and left alone - # every construction failed with a misleading "Field required" about - # a value that had in fact been supplied. - # - # It is *not* required in the OO-LD sense: that is what - # OoldField(required=True) says. Requiredness propagates into - # resolution - resolving a link constructs the target - so a - # required link makes every stored document lacking it - # unconstructible, and a self-referential link could never be - # satisfied by a real dataset. Links are declared far more often - # than they are required, so the terse form is the common case. - namespace[field_name] = OoldField() + # `manager: Link[Org]` with nothing assigned. Read as Python reads + # it - no default means required - but a link cannot be required as + # a pydantic field, because its value never reaches validation. + # Left alone, every construction failed with a misleading + # "Field required" about a value that had in fact been supplied. + namespace[field_name] = OoldField(required=True) continue # A Field() living in Annotated metadata rather than as the assigned # value was never seen here, so its default survived and was evaluated diff --git a/tests/test_notation.py b/tests/test_notation.py index 421aa1c..1fde8da 100644 --- a/tests/test_notation.py +++ b/tests/test_notation.py @@ -301,15 +301,11 @@ class Old(OoldModel): assert Old.__link_fields__["manager"].required_iri is True -def test_a_link_annotation_without_a_default_is_optional(): - """Links are declared far more often than they are required, so the terse - form is the common case. - - Requiredness is explicit because it propagates into resolution: resolving a - link constructs the target, so a required link makes every stored document - lacking it unconstructible - and a self-referential link, `father`, could - then never be satisfied by a real dataset. - """ +def test_a_link_annotation_without_a_default_is_required(): + """No default means required, as it does anywhere else in Python. A link is + never required at the pydantic level - its value is routed out before + validation - so this used to fail with a misleading "Field required" about + a value that had in fact been supplied.""" class Bare(OoldModel): id: str @@ -317,6 +313,6 @@ class Bare(OoldModel): manager: Link["Org"] Bare.model_rebuild() - assert Bare(id="ex:b").link_iris("manager") is None # constructs unset assert Bare(id="ex:b", manager="ex:acme").link_iris("manager") == "ex:acme" - assert Bare.__link_fields__["manager"].required_iri is False + with pytest.raises(ValueError, match="manager is required"): + Bare(id="ex:b")