numpydantic/tests/test_linkml.py

120 lines
3.9 KiB
Python

"""
Test custom features of the pydantic generator
Note that since this is largely a subclass, we don't test all of the functionality of the generator
because it's tested in the base linkml package.
"""
import re
import sys
import typing
import numpy as np
import pytest
from pydantic import BaseModel
def test_arraylike(imported_schema):
"""
Arraylike classes are converted to slots that specify nptyping arrays
array: Optional[Union[
NDArray[Shape["* x, * y"], Number],
NDArray[Shape["* x, * y, 3 z"], Number],
NDArray[Shape["* x, * y, 3 z, 4 a"], Number]
]] = Field(None)
"""
# check that we have gotten an NDArray annotation and its shape is correct
array = imported_schema["core"].MainTopLevel.model_fields["array"].annotation
args = typing.get_args(array)
for i, shape in enumerate(("* x, * y", "* x, * y, 3 z", "* x, * y, 3 z, 4 a")):
assert isinstance(args[i], NDArrayMeta)
assert args[i].__args__[0].__args__
assert args[i].__args__[1] == np.number
# we shouldn't have an actual class for the array
assert not hasattr(imported_schema["core"], "MainTopLevel__Array")
assert not hasattr(imported_schema["core"], "MainTopLevelArray")
def test_inject_fields(imported_schema):
"""
Our root model should have the special fields we injected
"""
base = imported_schema["core"].ConfiguredBaseModel
assert "hdf5_path" in base.model_fields
assert "object_id" in base.model_fields
def test_linkml_meta(imported_schema):
"""
We should be able to store some linkml metadata with our classes
"""
meta = imported_schema["core"].LinkML_Meta
assert "tree_root" in meta.model_fields
assert imported_schema["core"].MainTopLevel.linkml_meta.default.tree_root == True
assert imported_schema["core"].OtherClass.linkml_meta.default.tree_root == False
def test_skip(linkml_schema):
"""
We can skip slots and classes
"""
modules = generate_and_import(
linkml_schema,
split=False,
generator_kwargs={
"SKIP_SLOTS": ("SkippableSlot",),
"SKIP_CLASSES": ("Skippable", "skippable"),
},
)
assert not hasattr(modules["core"], "Skippable")
assert "SkippableSlot" not in modules["core"].MainTopLevel.model_fields
def test_inline_with_identifier(imported_schema):
"""
By default, if a class has an identifier attribute, it is inlined
as a string rather than its class. We overrode that to be able to make dictionaries of collections
"""
main = imported_schema["core"].MainTopLevel
inline = main.model_fields["inline_dict"].annotation
assert typing.get_origin(typing.get_args(inline)[0]) == dict
# god i hate pythons typing interface
otherclass, stillanother = typing.get_args(
typing.get_args(typing.get_args(inline)[0])[1]
)
assert otherclass is imported_schema["core"].OtherClass
assert stillanother is imported_schema["core"].StillAnotherClass
def test_namespace(imported_schema):
"""
Namespace schema import all classes from the other schema
Returns:
"""
ns = imported_schema["namespace"]
for classname, modname in (
("MainThing", "test_schema.imported"),
("Arraylike", "test_schema.imported"),
("MainTopLevel", "test_schema.core"),
("Skippable", "test_schema.core"),
("OtherClass", "test_schema.core"),
("StillAnotherClass", "test_schema.core"),
):
assert hasattr(ns, classname)
if imported_schema["split"]:
assert getattr(ns, classname).__module__ == modname
def test_get_set_item(imported_schema):
"""We can get and set without explicitly addressing array"""
cls = imported_schema["core"].MainTopLevel(array=np.array([[1, 2, 3], [4, 5, 6]]))
cls[0] = 50
assert (cls[0] == 50).all()
assert (cls.array[0] == 50).all()
cls[1, 1] = 100
assert cls[1, 1] == 100
assert cls.array[1, 1] == 100