test named slot, add skeletons for basemodel methods

This commit is contained in:
sneakers-the-rat 2024-09-30 23:14:50 -07:00
parent 016b81a5c5
commit 198ed3bcea
Signed by untrusted user who does not match committer: jonny
GPG key ID: 6DCB96EF1E4D232D
5 changed files with 84 additions and 4 deletions

View file

@ -25,7 +25,7 @@ from linkml_runtime.utils.schemaview import SchemaView
from nwb_linkml.includes.base import ( from nwb_linkml.includes.base import (
BASEMODEL_CAST_WITH_VALUE, BASEMODEL_CAST_WITH_VALUE,
BASEMODEL_COERCE_CHILD, BASEMODEL_COERCE_SUBCLASS,
BASEMODEL_COERCE_VALUE, BASEMODEL_COERCE_VALUE,
BASEMODEL_EXTRA_TO_VALUE, BASEMODEL_EXTRA_TO_VALUE,
BASEMODEL_GETITEM, BASEMODEL_GETITEM,
@ -56,7 +56,7 @@ class NWBPydanticGenerator(PydanticGenerator):
BASEMODEL_GETITEM, BASEMODEL_GETITEM,
BASEMODEL_COERCE_VALUE, BASEMODEL_COERCE_VALUE,
BASEMODEL_CAST_WITH_VALUE, BASEMODEL_CAST_WITH_VALUE,
BASEMODEL_COERCE_CHILD, BASEMODEL_COERCE_SUBCLASS,
BASEMODEL_EXTRA_TO_VALUE, BASEMODEL_EXTRA_TO_VALUE,
) )
split: bool = True split: bool = True

View file

@ -44,7 +44,7 @@ BASEMODEL_CAST_WITH_VALUE = """
raise e1 raise e1
""" """
BASEMODEL_COERCE_CHILD = """ BASEMODEL_COERCE_SUBCLASS = """
@field_validator("*", mode="before") @field_validator("*", mode="before")
@classmethod @classmethod
def coerce_subclass(cls, v: Any, info) -> Any: def coerce_subclass(cls, v: Any, info) -> Any:

View file

@ -101,6 +101,15 @@ def linkml_schema_bare() -> TestSchemas:
inlined_as_list=False, inlined_as_list=False,
any_of=[{"range": "OtherClass"}, {"range": "StillAnotherClass"}], any_of=[{"range": "OtherClass"}, {"range": "StillAnotherClass"}],
), ),
SlotDefinition(
name="named_slot",
description=(
"A slot that should use the Named[] generic to set the name param"
),
annotations=[{"named": True}],
range="OtherClass",
inlined=True,
),
SlotDefinition( SlotDefinition(
name="value", name="value",
description="Main class's array", description="Main class's array",

View file

@ -17,6 +17,7 @@ import pytest
from linkml_runtime.utils.compile_python import compile_python from linkml_runtime.utils.compile_python import compile_python
from numpydantic.dtype import Float from numpydantic.dtype import Float
from numpydantic.ndarray import NDArrayMeta from numpydantic.ndarray import NDArrayMeta
from pydantic import ValidationError
from nwb_linkml.generators.pydantic import NWBPydanticGenerator from nwb_linkml.generators.pydantic import NWBPydanticGenerator
@ -86,7 +87,9 @@ def imported_schema(linkml_schema, request) -> TestModules:
def test_array(imported_schema): def test_array(imported_schema):
""" """
Arraylike classes are converted to slots that specify nptyping arrays Arraylike classes are converted to slots that specify nptyping arrays.
Test that we can use any_of with the array slot (unlike the upstream generator, currently)
array: Optional[Union[ array: Optional[Union[
NDArray[Shape["* x, * y"], Number], NDArray[Shape["* x, * y"], Number],
@ -155,3 +158,26 @@ def test_get_item(imported_schema):
"""We can get without explicitly addressing array""" """We can get without explicitly addressing array"""
cls = imported_schema["core"].MainTopLevel(value=np.array([[1, 2, 3], [4, 5, 6]], dtype=float)) cls = imported_schema["core"].MainTopLevel(value=np.array([[1, 2, 3], [4, 5, 6]], dtype=float))
assert np.array_equal(cls[0], np.array([1, 2, 3], dtype=float)) assert np.array_equal(cls[0], np.array([1, 2, 3], dtype=float))
def test_named_slot(imported_schema):
"""
Slots that have a ``named`` annotation should get their ``name`` attribute set automatically
"""
OtherClass = imported_schema["core"].OtherClass
MainClass = imported_schema["core"].MainTopLevel
# We did in fact get the outer annotation
# this is a wild ass way to get the function name but hey
annotation = MainClass.model_fields["named_slot"].annotation.__args__[0]
validation_fn_name = annotation.__metadata__[0].func.__name__
assert validation_fn_name == "_get_name"
# we can't instantiate OtherClass without the ``name``
with pytest.raises(ValidationError, match=".*name.*"):
_ = OtherClass()
# but when we instantiate MainClass the name gets set automatically
instance = MainClass(named_slot={})
assert isinstance(instance.named_slot, OtherClass)
assert instance.named_slot.name == "named_slot"

View file

@ -0,0 +1,45 @@
"""
Base includes
"""
import pytest
@pytest.mark.skip
def test_basemodel_getitem(imported_schema):
"""
We can get a value from ``value`` if we have it
"""
pass
@pytest.mark.skip
def test_basemodel_coerce_value(imported_schema):
"""
We can instantiate something by trying to grab it's "value" item
"""
pass
@pytest.mark.skip
def test_basemodel_cast_with_value(imported_schema):
"""
Opposite of above, we try to cast **into** the ``value`` field
"""
pass
@pytest.mark.skip
def test_basemodel_coerce_subclass(imported_schema):
"""
We try to rescue by coercing to a child class if possible
"""
pass
@pytest.mark.skip
def test_basemodel_extra_to_value(imported_schema):
"""
We gather extra fields and put them into a value dict when it's present
"""
pass