"Super" reload and a problem.
Jeremy Fincher
tweedgeezer at hotmail.com
Fri Aug 30 08:08:28 EDT 2002
I need a more powerful reload() for the program I'm writing, a reload
that updates all instances of a class to the newly reloaded class
automatically. Until earlier today I'd been using a modified form of
Thomas Heller's "autoreload.py," but it was (very obviously) written
prior to 2.2, and thus didn't mix well with the dict-proxies of
new-style class objects.
So I wrote one of my own, based on ideas I got from 'exarkun' in
#python on OPN, and apparently similar to Twisted's
twisted.python.rebuild function (at least in theory; in practice, I
believe they differ significantly).
def superReload(oldmodule):
###
# So here's how this baby works:
# Reload the module.
# Iterate through the old module, finding classes and functions
that are
# also in the new module.
# Add an __getattribute__ or __getattr__ method to those classes
that are
# present in the new module. This method will, when called,
change
# the instance's __class__ to point to the new class.
# Change the func_code, func_defaults, and func_doc of any
functions or
# methods or generators we run across, just in case someone's
holding
# a reference to them instead of calling them by name.
###
"""Reload a module and make objects auto-update."""
# reload(module) modifies module in-place, so we need a copy of
its
# __dict__ to iterate through.
olddict = copy.copy(oldmodule.__dict__)
newmodule = reload(oldmodule)
newdict = newmodule.__dict__
for (name, oldvalue) in olddict.iteritems():
if name in newdict:
newvalue = newdict[name]
oldtype = type(oldvalue)
# We have to pass in newvalue because of Python's scoping.
def updater(self, s, newvalue=newvalue):
# This function is to be an __getattr__ or
__getattribute__.
try:
self.__class__ = newvalue
except:
debug.recoverableException()
try:
del self.__class__.__getattribute__
except AttributeError:
del self.__class__.__getattr__
return getattr(self, s)
if oldtype == types.TypeType and \
oldvalue.__module__ == newmodule.__name__:
# New-style classes support __getattribute__, which is
called
# on *any* attribute access, so they get updated the
first
# time they're used after a reload.
oldvalue.__getattribute__ = updater
elif oldtype == types.ClassType and \
oldvalue.__module__ == newmodule.__name__:
# Old-style classes can only use getattr, so they
might not
# update right away. Hopefully they will, but to
solve this
# problem I just use new-style classes.
oldvalue.__getattr__ = updater
elif oldtype == type(newvalue):
if oldtype == types.FunctionType or\
oldtype == types.GeneratorType:
oldvalue.func_code = newvalue.func_code
oldvalue.func_defaults = newvalue.func_defaults
oldvalue.func_doc = newvalue.func_doc
elif oldtype == types.MethodType:
oldfunc = oldvalue.im_func
newfunc = newvalue.im_func
oldfunc.func_code = newfunc.func_code
oldfunc.func_defaults = newfunc.func_defaults
oldfunc.func_doc = newfunc.func_doc
# Update the linecache, so tracebacks show the proper lines.
linecache.checkcache()
return newmodule
I ran into several problems while implementing this. reload()
apparently modifies the module's __dict__ in place, but then returns
the "new" module. Also, I apparently misunderstood Python's nested
scopes, and thus had difficulties with "updater" until I put the
default "newvalue" argument in there. I also learned that an
instance's __class__ attribute can't be changed if the class defines
__slots__, but that wasn't such a huge deal.
The problem I'm having now, however, is when this class is reloaded:
class nick(str):
"""This class does case-insensitive comparisons of nicks."""
def __init__(self, s):
self.lowered = nickToLower(s)
def __eq__(self, s):
try:
return nickToLower(s) == self.lowered
except:
return False
def __hash__(self):
return hash(self.lowered)
But it doesn't define __slots__! I can't figure out why I get this
error when the module containing that class is reloaded, despite the
fact that the code hadn't been changed:
Traceback (most recent call last):
File "src/world.py", line 109, in updater
self.__class__ = newvalue
TypeError: __class__ assignment: 'nick' object layout differs from
'nick'
Not only do I get that error, but it seems to loop infinitely with
that error. I put checks in updater, to delete the class attribute I
added (either __getattribute__ or __getattr__), but it doesn't appear
to be being called.
Any ideas, or comments on how to make superReload more robust?
Thanks,
Jeremy
More information about the Python-list
mailing list