pythonocc-core
pythonocc-core copied to clipboard
Copying PythonOCC objects via copy.deepcopy() and __repr__ issue
I have two related things, and apologies in advance for the long post.
First thing
I have a structure that has multiple PythonOCC instances attached to it[^1] - specifically Quantity_Color, TDF_Label and TopLoc_Location - and I've been trying to perform a copy.deepcopy() on it as a whole. I was getting this: TypeError: cannot pickle 'SwigPyObject' object, because deepcopy uses Pickle, and SWIG objects aren't serialisable. It seemed to have been easily fixed by creating custom classes with the following mixin, adapted from this answer at SO:
class PickalableSWIG:
def __setstate__(self, state):
self.__init__(*state['args'])
def __getstate__(self):
return {'args': self.args}
and then for each PythonOCC class in question:
class PickalableClass(SomeClass, PickalableSWIG):
def __init__(self, *args):
self.args = args
SomeClass.__init__(self)
This approach has the advantage that I don't need to change the underlying classes, but it leads to two problems: the "second thing" below (which seems to be a __repr__ issue); and a more particular problem, which is that TopLoc_LocationSWIG.Multiplied(loc) creates a new instance of TopLoc_Location and not TopLoc_LocationSWIG, and it is beyond my abilities to come up with a workaround.
Second thing
Having tried the above solution, I then tried new_instance.__dict__ = copy.deepcopy(old_instance.__dict__)) to copy everything from one instance of my object to another, but this fails silently: the data are not copied. Instead, I tried to debug via for k,v in new_instance.items(): print(k,v) but got the error below[^2].
File "<rest of path>\OCC\Core\Quantity.py", line 142, in _dumps_object
klass_name = str(klass.__class__).split(".")[3].split("'")[0]
IndexError: list index out of range
After a bit of digging, this was also relatively easily fixed by replacing an index in one line, as follows:
klass_name = str(klass.__class__).split(".")[-1].split("'")[0]
I tried doing this manually for _dumps_object in each of Quantity.py, TDF.py and TopLoc.py, but I realise it is a crude hack. A general way of doing it (by overriding that function) would be better, but my knowledge of PythonOCC isn't good enough. It works fine for those three PythonOCC classes, but I don't know if it's a universal solution. What is the logic for the [3] index, and is it possible to replace it with [-1] (or something) everywhere?
Final (very hacky) solution
The only solution I could get to work was to add self.args = args to __init__ in TDF_Label, Quantity_Color and TopLoc_Location (as in the SO solution in the "first thing") then adding the __get_state__ and __set_state__ methods, but with the latter modified slightly - as I was getting the following: AttributeError: 'TopLoc_Location' object has no attribute 'args' - such that:
def __getstate__(self):
if not hasattr(self, 'args'):
self.args = {}
return {'args': self.args}
Again, this is a horrible hack, but instances can now be deep-copied. I hope these are useful suggestions and happy to help if you think these are good additions to PythonOCC.
[^1]: The structure of my class is probably not relevant as the issue is more general, but I can provide full details if necessary.
[^2]: This is for Quantity_Color, but the other two classes give very similar errors.
Bumping this in case anyone has any ideas.