SofaPython3 icon indicating copy to clipboard operation
SofaPython3 copied to clipboard

Can't expose properties of objects deriving from BaseObject

Open jjcasmar opened this issue 4 years ago • 1 comments

I am creating my own custom types for SOFA, mainly some forcefields but also other stuff. In order to add those types to an object, they must inherit from Sofa.Core.Object on python. This is problematic, since Sofa.Core.Base overrides how properties are read and written. In the end, this breaks adding new properties to custom defined types that are exposed to python.

    using Inherit = sofa::core::objectmodel::BaseObject;
    using CG = VNCS::ConjugateGradientSolver;

    ::py::class_<CG, Inherit, sofa::core::sptr<CG>>(m, "ConjugateGradient")
        .def(::py::init([]() { return sofa::core::objectmodel::New<CG>(); }))
        .def_property(
            "iterations",
            [](const CG &cg) { return cg.iterations(); },
            [](CG &cg, int iterations) { cg.setIterations(iterations); })
        .def("setIterations", [](CG &cg, int iterations) { cg.setIterations(iterations); })
        .def_property("tolerance", &CG::toleranceThreshold, &CG::setToleranceThreshold);

With that class exposed to python, it is not possible to do

cgSolver = myModule.ConjugateGradient
cgSolver.iterations = 50 # Doesn't work
print(cgSolver.iterations) # prints 25, the default value in the C++ class

This is the output for some tests

cgSolver = PyVNCS.ConjugateGradient()
dir(cgSolver)
['bbox', 'componentState', 'listening', 'name', 'printLog', 'tags']
cgSolver.iterations = 60
cgSolver.iterations
25
cgSolver.setIterations(40)
None
cgSolver.iterations
40
getattr(cgSolver, 'iterations')
40
setattr(cgSolver, 'iterations', 60)
None
getattr(cgSolver, 'iterations')
40

If ConjugateGradient doesn't inherit from Sofa.Core.Object, then the properties work fine, but I can't add it to a node.

jjcasmar avatar May 10 '21 22:05 jjcasmar

https://github.com/sofa-framework/SofaPython3/blob/a427fa05f9dd91c3f58f532083778d4e0a7dea92/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Base.cpp#L126

Actually, what is happening is that BaseBinding reimplements the _setattr__ function and if it doesn't find the property as a Data, it relies on adding a new dynamic one the __dict__. Before adding it as a dynamic one, it should check if the property is a python property.

jjcasmar avatar May 10 '21 22:05 jjcasmar