Today a link annotation with no default means required at construction, matching what "no default" means everywhere else in Python (_descriptor.py#L190-L197):
father: Link["Person"] # required
father: Link["Person"] = OoldField() # optional
father: Link["Person"] = OoldField(required=True) # required, explicit
The consequence is that the terse form is the one you almost never want for a link. Dropping OoldField() in examples/wiki_data.py breaks the ancestry walk:
ValueError: father is required but not set
interface.resolve -> model_cls.from_jsonld -> import_jsonld -> model_cls(**jsonld_dict)
It fails five generations up, while resolving an ancestor whose Wikidata record has no P22 - not on the object you asked for. Resolution constructs target objects, so construction-requiredness propagates into every read, and a self-referential link can never be satisfied by a real dataset.
Link[T] is a promise about reading (it raises rather than returning a None the type denies, which is what makes person.father.father guard-free). Requiredness is about supplying. Two axes, and OoldField() currently exists to opt out of the second.
Options:
- keep it - consistent with Python, at the cost of
= OoldField() on nearly every link
- invert it - a bare
Link[T] is optional, OoldField(required=True) opts in. Reads better for links, diverges from Python
father: Link["Person"] # optional
father: Link["Person"] = OoldField() # optional
father: Link["Person"] = OoldField(required=True) # required, explicit
- something else
@raederan @LukasOro @MatPoppFHG - which reads better to you in generated code?
Today a link annotation with no default means required at construction, matching what "no default" means everywhere else in Python (_descriptor.py#L190-L197):
The consequence is that the terse form is the one you almost never want for a link. Dropping
OoldField()in examples/wiki_data.py breaks the ancestry walk:It fails five generations up, while resolving an ancestor whose Wikidata record has no
P22- not on the object you asked for. Resolution constructs target objects, so construction-requiredness propagates into every read, and a self-referential link can never be satisfied by a real dataset.Link[T]is a promise about reading (it raises rather than returning aNonethe type denies, which is what makesperson.father.fatherguard-free). Requiredness is about supplying. Two axes, andOoldField()currently exists to opt out of the second.Options:
= OoldField()on nearly every linkLink[T]is optional,OoldField(required=True)opts in. Reads better for links, diverges from Python@raederan @LukasOro @MatPoppFHG - which reads better to you in generated code?