diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseLink.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseLink.cpp index 73708d4e..101b88f8 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseLink.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseLink.cpp @@ -26,6 +26,7 @@ using sofa::core::objectmodel::BaseObject; #include +#include #include #include #include @@ -67,6 +68,29 @@ std::string getPathName(BaseLink& self) return (n ? n->getPathName() : o->getPathName()) + "." + self.getName(); } +namespace { +py::object __getattr__(py::object self, const std::string& s) +{ + py::object base = getLinkedBase(py::cast(self), 0); + if(!base.is_none()) + { + return BindingBase::__getattr__(base, s); + } + throw std::runtime_error("Unable to find attribute on an empty link."); +} + +void __setattr__(py::object self, const std::string& s, py::object value) +{ + py::object base = getLinkedBase(py::cast(self), 0); + if(!base.is_none()) + { + BindingBase::__setattr__(base, s, value); + return; + } + throw std::runtime_error("Unable to find and set an attribute on an empty link."); +} +} + void moduleAddBaseLink(py::module& m) { py::class_ link(m, "Link", sofapython3::doc::baseLink::baseLinkClass); @@ -89,11 +113,12 @@ void moduleAddBaseLink(py::module& m) link.def("getLinkedBase", getLinkedBase, "index"_a = 0, sofapython3::doc::baseLink::getLinkedBase); link.def("setLinkedBase", &BaseLink::setLinkedBase, sofapython3::doc::baseLink::getLinkedBase); - link.def("getLinkedPath", &BaseLink::getLinkedPath, "index"_a = 0, sofapython3::doc::baseLink::getLinkedPath); link.def("getPathName", getPathName, sofapython3::doc::baseLink::getLinkedPath); link.def("read", &BaseLink::read, sofapython3::doc::baseLink::read); + link.def("__getattr__", &__getattr__); + link.def("__setattr__", &__setattr__); } } /// namespace sofapython3 diff --git a/bindings/Sofa/tests/Core/BaseLink.py b/bindings/Sofa/tests/Core/BaseLink.py index b932b580..2eff52d7 100644 --- a/bindings/Sofa/tests/Core/BaseLink.py +++ b/bindings/Sofa/tests/Core/BaseLink.py @@ -7,6 +7,7 @@ def create_scene(rootName="root"): root = Sofa.Core.Node(rootName) root.addObject("RequiredPlugin", name="SofaBaseMechanics") + root.addObject("RequiredPlugin", name="SofaDeformable") return root class Test(unittest.TestCase): @@ -95,6 +96,14 @@ def test_getLinked_and_Owner(self): self.assertEqual(link_input.getOwnerBase().getName(), "BarycentricMapping") self.assertEqual(link_output.getOwnerBase().getName(), "BarycentricMapping") + def test_baselink_attributes_forwarding(self): + root = create_scene("root") + c1 = root.addObject("MechanicalObject", name="t1", position=[[-1,-2,-3],[-4,-5,-6]]) + f1 = root.addObject("RestShapeSpringsForceField", name="forcefield", mstate="@t1") + self.assertEqual(len(f1.mstate.position), 2) + f1.mstate.position = [[1.0,2.0,3.0]] + self.assertEqual(len(f1.mstate.position), 1) + @unittest.skip # Segmentation fault on MacOS def test_read(self): root = create_scene("root")