Skip to content

Commit

Permalink
test named slot, add skeletons for basemodel methods
Browse files Browse the repository at this point in the history
  • Loading branch information
sneakers-the-rat committed Oct 1, 2024
1 parent 016b81a commit 198ed3b
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 4 deletions.
4 changes: 2 additions & 2 deletions nwb_linkml/src/nwb_linkml/generators/pydantic.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

from nwb_linkml.includes.base import (
BASEMODEL_CAST_WITH_VALUE,
BASEMODEL_COERCE_CHILD,
BASEMODEL_COERCE_SUBCLASS,
BASEMODEL_COERCE_VALUE,
BASEMODEL_EXTRA_TO_VALUE,
BASEMODEL_GETITEM,
Expand Down Expand Up @@ -56,7 +56,7 @@ class NWBPydanticGenerator(PydanticGenerator):
BASEMODEL_GETITEM,
BASEMODEL_COERCE_VALUE,
BASEMODEL_CAST_WITH_VALUE,
BASEMODEL_COERCE_CHILD,
BASEMODEL_COERCE_SUBCLASS,
BASEMODEL_EXTRA_TO_VALUE,
)
split: bool = True
Expand Down
2 changes: 1 addition & 1 deletion nwb_linkml/src/nwb_linkml/includes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def cast_with_value(cls, v: Any, handler, info) -> Any:
raise e1
"""

BASEMODEL_COERCE_CHILD = """
BASEMODEL_COERCE_SUBCLASS = """
@field_validator("*", mode="before")
@classmethod
def coerce_subclass(cls, v: Any, info) -> Any:
Expand Down
9 changes: 9 additions & 0 deletions nwb_linkml/tests/fixtures/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ def linkml_schema_bare() -> TestSchemas:
inlined_as_list=False,
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(
name="value",
description="Main class's array",
Expand Down
28 changes: 27 additions & 1 deletion nwb_linkml/tests/test_generators/test_generator_pydantic.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from linkml_runtime.utils.compile_python import compile_python
from numpydantic.dtype import Float
from numpydantic.ndarray import NDArrayMeta
from pydantic import ValidationError

from nwb_linkml.generators.pydantic import NWBPydanticGenerator

Expand Down Expand Up @@ -86,7 +87,9 @@ def imported_schema(linkml_schema, request) -> TestModules:

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[
NDArray[Shape["* x, * y"], Number],
Expand Down Expand Up @@ -155,3 +158,26 @@ def test_get_item(imported_schema):
"""We can get without explicitly addressing array"""
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))


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"
45 changes: 45 additions & 0 deletions nwb_linkml/tests/test_includes/test_base.py
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 198ed3b

Please sign in to comment.