diff --git a/python/sdist/amici/numpy.py b/python/sdist/amici/numpy.py index 93b04603be..b259aca2a0 100644 --- a/python/sdist/amici/numpy.py +++ b/python/sdist/amici/numpy.py @@ -80,7 +80,10 @@ def __getattr__(self, item) -> Union[np.ndarray, float]: :returns: value """ - return self.__getitem__(item) + try: + return self.__getitem__(item) + except KeyError as e: + raise AttributeError(item) from e def __init__(self, swigptr): """ diff --git a/python/tests/test_swig_interface.py b/python/tests/test_swig_interface.py index 3eabafd49b..ffec95b77b 100644 --- a/python/tests/test_swig_interface.py +++ b/python/tests/test_swig_interface.py @@ -6,6 +6,8 @@ import copy import numbers +import pytest + import amici import numpy as np @@ -500,3 +502,26 @@ def test_model_is_deepcopyable(pysb_example_presimulation_module): assert model1.t0() == model2.t0() model2.setT0(100 + model2.t0()) assert model1.t0() != model2.t0() + + +def test_rdataview(sbml_example_presimulation_module): + """Test some SwigPtrView functionality via ReturnDataView.""" + model_module = sbml_example_presimulation_module + model = model_module.getModel() + rdata = amici.runAmiciSimulation(model, model.getSolver()) + assert isinstance(rdata, amici.ReturnDataView) + + # fields are accessible via dot notation and [] operator, + # __contains__ and __getattr__ are implemented correctly + with pytest.raises(AttributeError): + _ = rdata.nonexisting_attribute + + with pytest.raises(KeyError): + _ = rdata["nonexisting_attribute"] + + assert not hasattr(rdata, "nonexisting_attribute") + assert "x" in rdata + assert rdata.x == rdata["x"] + + # field names are included by dir() + assert "x" in dir(rdata)