Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Dynamically Update Class Definitions?

7 views
Skip to first unread message

chri...@gmail.com

unread,
Nov 11, 2005, 1:11:48 PM11/11/05
to
Is there a way to loop through all instantiated objects and update
their classes when a source file changes? I know about Michael Hudson's
method
(http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/160164), but
you have to modify all your classes to subclass AutoReloader. Is there
something less intrusive (visitor pattern?) that you can use like
update(old_class, new_class) to automagically do the work?

Chris

Alex Martelli

unread,
Nov 11, 2005, 10:36:46 PM11/11/05
to
<chri...@gmail.com> 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.


Alex

Chris Spencer

unread,
Nov 12, 2005, 12:21:00 AM11/12/05
to

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

Chris Spencer

unread,
Nov 12, 2005, 1:24:57 AM11/12/05
to
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()

Jean-Paul Calderone

unread,
Nov 12, 2005, 1:55:50 AM11/12/05
to pytho...@python.org
On Sat, 12 Nov 2005 06:24:57 GMT, Chris Spencer <usenet.20...@spamgourmet.com> wrote:
>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:

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

Chris Spencer

unread,
Nov 12, 2005, 11:11:09 AM11/12/05
to
Jean-Paul Calderone wrote:

> 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

0 new messages