Skip to content
Closed
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
20 changes: 6 additions & 14 deletions src/oold/model/_descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 7 additions & 11 deletions tests/test_notation.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,22 +301,18 @@ 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
type: str | None = "ex:NBare"
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")
Loading