I crawled through the code for a couple of hours with the following result. I think I could identify the problem as described in the "Consequences" section below.
Analysis
- The document starts reading from the XML file in void Document::Restore(Base::XMLReader &reader).
- In there, features are created with addObject(type.c_str(), name.c_str(), /*isNew=*/ false). In the case of an Arch section plane, the type is App::FeaturePython. For an Arch drawing view, the type is Drawing::FeatureViewPython.
- Later, the document's restore function calls the restore the function of each feature: pObj->Restore(reader). In both cases, thus for both feature types above, this simply restores the dynamic properties.
Code: Select all
void Restore(Base::XMLReader &reader) {
props->Restore(reader);
}
...
DynamicProperty* props;
- In turn, the dynamic property class create the property objects and calls the restore function of each property object.
Code: Select all
prop = addDynamicProperty(TypeName, PropName, group, doc, attribute, readonly, hidden);
...
prop->Restore(reader);
- Of special interest is the property Proxy for Python features. It refers to the Python implementation of the feature/view. This property is defined in FeaturePythonT as
The save and restore functions of this property type use the Python pickle module, which features sophisticated serialization/deserialization with, e.g., __getstate__ and __setstate__. In this way, the developers has full control over the serialization and deserialization.
Consequences
- A property has full control to decide about its serialization/deserialization.
- A feature nearly has full control over serialization/deserialization too. The tricky thing is, when a property is renamed or substituted by, e.g., two connected new properties. But even this is realizable. For example, Part::Box does something like this. The comment for the restore function explains it well.
This method was added for backward-compatibility. In former versions of Box we had the properties x,y,z and l,h,w which have changed to Location -- as replacement for x,y and z and Length, Height and Width.
- However, when implementing a feature in Python, you create properties in FeaturePython objects. These objects do not know the properties but are responsible for the serialization/deserialization. The Python code has no influence on the property creation. This is in contrast to Python properties, which solved the issue cleanly.
The last point looks like a design bug to me.
FeaturePython should (optionally) delegate the serialization/deserialization into the Python code like the
PropertyPythonObject does. There are many many
hasattr branches in the Python code to get around this problem. They would not be necessary, when serialization/deserialization would be robust across versions of a feature.
In another project, we had a similar problem. It impeded continuous refactoring and code improvement, until we removed the impediment (broke backwards compatibility once). After that, we could evolve with backwards compatibility.
What do you think?
Simon