Chris
If you're in no hurry, you COULD loop over all of gc.get_objects(),
identify all those which are instances of old_class and "somehow" change
their classes to new_class -- of course, x.__class__ = new_class may
well not be sufficient, in which case you'll have to pass to update a
callable to do the instance-per-instance job.
Alex
Couldn't I just loop over gc.get_referrers(cls), checking for instances
of the class object? Since class instances refer to their class, the gc
seems to be doing the exact same thing as Hudson's fancy metaclass. Or
am I missing something?
Chris
>> If you're in no hurry, you COULD loop over all of gc.get_objects(),
>> identify all those which are instances of old_class and "somehow" change
>> their classes to new_class -- of course, x.__class__ = new_class may
>> well not be sufficient, in which case you'll have to pass to update a
>> callable to do the instance-per-instance job.
>
>
> Couldn't I just loop over gc.get_referrers(cls), checking for instances
> of the class object? Since class instances refer to their class, the gc
> seems to be doing the exact same thing as Hudson's fancy metaclass. Or
> am I missing something?
>
> Chris
In fact, the following code seems to work, and doesn't require any
modification to new-style class based code:
import os
import time
import threading
import inspect
import gc
import copy
class ModuleUpdater(object):
'''
This will constantly check a module's source file for updates, reload
if any are detected, and update all class instances.
Only works for new-style classes.
Use like:
checker = ModuleUpdater(module=mymod)
checker.start()
'''
def __init__(self, module):
self.module = module
self.lastloaded = time.time()
self.running = 0
self.t = None
def __call__(self):
self.running = 1
while self.running:
self.check()
time.sleep(1)
def check(self):
lastmodified = os.stat(inspect.getsourcefile(self.module))[8]
if lastmodified > self.lastloaded:
print 'update detected for',self.module.__name__
oldmod = copy.copy(self.module.__dict__)
newmod = reload(self.module)
try:
for name,obj in oldmod.items():
if isinstance(obj,type) and name in newmod.__dict__:
newobj = newmod.__dict__[name]
referrers = gc.get_referrers(obj)
for referrer in referrers:
if isinstance(referrer,obj):
# update old class instances to use new
class
referrer.__class__ = newobj
print 'update loaded for',self.module.__name__
except Exception, e:
print 'unable to load update for %s: %s' %
(self.module.__name__, str(e))
self.lastloaded = lastmodified
return 1
return 0
def start(self):
t = threading.Thread(target=self)
t.setDaemon(1)
t.start()
self.t = t
return t
def stop(self, blocking=0):
self.running = 0
if blocking:
self.t.join()
if __name__ == '__main__':
import testmod # any module containing class Bar with method meth
uc = ModuleUpdater(testmod)
uc.start()
b=testmod.Bar(1)
while 1: # meanwhile, modify the source to testmod.py
time.sleep(1)
print b.meth()
There are lots of cases where you cannot rebind the __class__ attribute. For a comprehensive treatment of this idea (but still not a completely functionality implementation), take a look at <http://cvs.twistedmatrix.com/cvs/trunk/twisted/python/rebuild.py?view=markup&rev=11450>. On another note, the usage of threads in this code is totally insane and unsafe. Even for strictly development purposes, I would expect it to introduce so many non-deterministic and undebuggable failures as to make it cost more time than it saves. You really want to stop the rest of the program, then update things, then let everything get going again.
Jean-Paul
> There are lots of cases where you cannot rebind the __class__
> attribute. For a comprehensive treatment of this idea (but still not a
> completely functionality implementation), take a look at
> <http://cvs.twistedmatrix.com/cvs/trunk/twisted/python/rebuild.py?view=markup&rev=11450>.
> On another note, the usage of threads in this code is totally insane and
> unsafe. Even for strictly development purposes, I would expect it to
> introduce so many non-deterministic and undebuggable failures as to make
> it cost more time than it saves. You really want to stop the rest of
> the program, then update things, then let everything get going again.
I used a thread to quickly detect changes in the source code, but you're
absolutely right. In any non-toy application you'll definitely need
control over when the upgrade process occurs. Thanks for the help.
Chris