Dynamically Update Class Definitions?

Chris Spencer usenet.20.evilspam at spamgourmet.com
Sat Nov 12 01:24:57 EST 2005


Chris Spencer wrote:
> Alex Martelli wrote:

>> 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()



More information about the Python-list mailing list