-
Notifications
You must be signed in to change notification settings - Fork 58
Description
Affecting pyGSTi 0.9.14.1 (develop), and probably also the penultimate release of 0.9.13.
Here's a minimal snippet that can always reproduce the issue.
from copy import deepcopy
from pygsti.modelpacks import smq1Q_XYI
tm = smq1Q_XYI.target_model('full TP')
op = tm.operations[()]
deepcopy(op) # infinite recursion in DenseOperatorInterface.__getattr__I encountered this issue just when copying lists of ExplicitOpModels: ExplicitOpModel.copy() triggers calls to ModelMember.copy(), which triggers calls to copy.deepcopy() on ModelMember objects. As copy.deepcopy()'s magic starts to recurse back up, we end up calling ExplicitOpModel.__setstate__(), which triggers calls to the ModelMember.relink_parent, which tries to evaluate the expression self._parent (as an accessor, not a setter). Depending on the context, this can trigger calls to custom __getattr__ functions that inadvertently break (with an infinite recursion) when '_parent' in self.__dict__ is False.
Here's a corrected implementation for ModelMember.relink_parent:
def relink_parent(self, parent):
for subm in self.submembers():
subm.relink_parent(parent)
if '_parent' in self.__dict__:
if self._parent is parent:
return # OK to relink multiple times
assert(self._parent is None), "Cannot relink parent: current parent is not None!"
self._parent = parent # assume no dependent objectsInterestingly, the possibility of infinite recursion was noted in the implementation of DenseOperatorInterface.__getattr__.
