Ian Bicking: the old part of his blog

Metaclass fun

I swear, this isn't a superfluous use of metaclasses.

There's a problem with reloading modules or in general with dealing with Python classes in long-running systems. You can reload the module, recreate the class, but all the old instances are still out there. What to do? Bind classes tightly to their names, turn (re)creation of a class in to modification of the old class. What better than a metaclass:

registry = {}

class Reloadable(type):

    def __new__(me, className, bases, d):
        moduleName = d['__module__']
        try:
            current = registry[moduleName][className]
        except KeyError:
            current = None
        if current:
            regenerateClass(current, bases, d)
            return current
        cls = type.__new__(me, className, bases, d)
        registry.setdefault(moduleName, {})[className] = cls
        return cls

def regenerateClass(current, bases, d):
    #print "Reloading class %s" % current
    for name in current.__dict__.keys():
        if name in ['__name__', '__metaclass__',
                    '__module__', '__dict__',
                    '__weakref__', '__bases__']:
            continue
        delattr(current, name)
    for name, value in d.items():
        setattr(current, name, value)
    # Hmm... fails:
    # current.__bases__ = bases
    return current

if __name__ == '__main__':
    class C(object):
        __metaclass__ = Reloadable
        def test(self):
            print 'first'
    c = C()
    c.test()

    class C(object):
        __metaclass__ = Reloadable
        def test(self):
            print 'second'
    c.test()
I'm not sure how to handle a class whose inheritance changes -- __bases__ seems resistent to change. And who knows what else is flawed here; this is totally off the top of my head. But there are some compelling aspects to this.

I've put this in the Webware Sandbox, Sandbox/ianbicking/misc/Reloadable.py

Created 15 Sep '03
Modified 14 Dec '04

Comments:

Hmm, this looks familiar... have you seen my Python Cookbook recipe that does more-or-less the same thing?

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

You can't (realistically) do anything about __bases__ in 2.2.X. You can in 2.3 because I wrote __bases__ changing code to support exactly this...
# Michael Hudson