Automatic reloading, metaclasses, and pickle

Ziga Seilnacht ziga.seilnacht at gmail.com
Tue Feb 27 17:30:26 EST 2007


Andrew Felch wrote:
>
> Thanks Ziga.  I use pickle protocol 2 and binary file types with the
> command: "cPickle.dump(obj, file, 2)"
>
> I did your suggestion, i commented out the "__call__" function of
> MetaInstanceTracker and copied the text to the __new__ function of
> AutoReloader (code appended).  I got a crazy recursive error message
> (also appended below).  In my code, I am creating a new instance,
> rather than using the pickled object (it needs to work in both modes).
>
> Thanks very much for helping me get through this.  With my development
> approach, finding a solution to this problem is really important to
> me.

Here is a version that should work. It should work with all protocols,
see InstanceTracker.__reduce_ex__. Note that all subclasses of
InstanceTracker and AutoReloader should be cautious when overriding
the
__new__ method. They must call their base class' __new__ method,
preferably by using super(), or the tracking won't work.


# adapted from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/160164

import weakref, inspect


class MetaInstanceTracker(type):

    def __init__(cls, name, bases, ns):
        super(MetaInstanceTracker, cls).__init__(name, bases, ns)
        cls.__instance_refs__ = []

    def __instances__(cls):
        instances = []
        validrefs = []
        for ref in cls.__instance_refs__:
            instance = ref()
            if instance is not None:
                instances.append(instance)
                validrefs.append(ref)
        cls.__instance_refs__ = validrefs
        return instances


class InstanceTracker(object):

    __metaclass__ = MetaInstanceTracker

    def __new__(*args, **kwargs):
        cls = args[0]
        self = super(InstanceTracker, cls).__new__(*args, **kwargs)
        cls.__instance_refs__.append(weakref.ref(self))
        return self

    def __reduce_ex__(self, proto):
        return super(InstanceTracker, self).__reduce_ex__(2)


class MetaAutoReloader(MetaInstanceTracker):

    def __init__(cls, name, bases, ns):
        super(MetaAutoReloader, cls).__init__(name, bases, ns)
        f = inspect.currentframe().f_back
        for d in [f.f_locals, f.f_globals]:
            if name in d:
                old_class = d[name]
                for instance in old_class.__instances__():
                    instance.change_class(cls)
 
cls.__instance_refs__.append(weakref.ref(instance))
                for subcls in old_class.__subclasses__():
                    newbases = []
                    for base in subcls.__bases__:
                        if base is old_class:
                            newbases.append(cls)
                        else:
                            newbases.append(base)
                    subcls.__bases__ = tuple(newbases)
                break


class AutoReloader(InstanceTracker):

    __metaclass__ = MetaAutoReloader

    def change_class(self, new_class):
        self.__class__ = new_class


Ziga




More information about the Python-list mailing list