Issue 33249: Unpickling objects with recursive references and partials fail due to incomplete state passed to setstate (original) (raw)

The code below causes a

AttributeError: 'Event' object has no attribute 'name'

It seems like the entries of Machine.events are not available during __setstate__. However, the behaviour depends on the Python version.

For Python 3.6.4 it's always the 'first' (as in it's always 'to_A' which has been added first) element in Machine.events that is incomplete. For Python 3.4.8 and 3.5.5 it looks like a racing condition since sometimes there is no error, sometimes its the first or the second element which is incomplete. Letting Machine serve as its own model and pickling that does work for all three versions.

Tested on MacOS High Sierra.

import pickle
from functools import partial


class Event:

    def __init__(self, name, machine):
        self.name = name
        self.machine = machine

    def trigger(self):
        pass


class Machine:

    def __init__(self, model, states):
        self.events = {}
        self.model = model if model != 'self' else self

        for state in states:
            trigger = 'to_%s' % state
            self.events[trigger] = Event(trigger, self)
            trig_func = partial(self.events[trigger].trigger)
            setattr(self.model, trigger, trig_func)

    def __getstate__(self):
        return self.__dict__

    def __setstate__(self, state):
        self.__dict__.update(state)
        print(self.events['to_B'].name)
        print(self.events['to_A'].name)


class Model:

    def __init__(self):
        self.machine = Machine(self, states=['A', 'B'])

model = Model()
dump = pickle.dumps(model)
model2 = pickle.loads(dump)