IsPython really O-O?

Thomas Heller thomas.heller at ion-tof.com
Tue Nov 13 21:51:53 CET 2001


> "AD" == Andrew Dalke <dalke at dalkescientific.com> writes:
AD> but I've never really needed it.  Better I think would be if
AD> Python's IDE's offered a reload() which was closer to
AD> Smalltalks's, where a modified class definition propagates
AD> even to existing instances of that class.  So someone could
AD> edit a module and manipulate things interactively without
AD> having to reload and rerun everything.

Here's a script I'm sometimes working on which does this.
It's not bulletproof, but it kindof works.
It runs a thread in the background (once started), which checks
the timestamp of all modules loaded from the filesystem.

It it's out of date, is is reloaded automatically.
Classes and functions in the reloaded module are
updated automatically to the new version.

Hopefully sometime I will complete it and post it to the Python Cookbook.

Thomas

---- autoreload.py ----
#
# autoreload.py - automatically reload changed source
# code into a running program
#
# You may want to place the following two lines
# into your sitecustomize.py:
#
# import autoreload
# autoreload.run()
#
# Created: Thomas Heller, 2000-04-17
#
# $Id: autoreload.py,v 1.8 2001/11/13 20:44:51 thomas Exp $
#
# $Log: autoreload.py,v $
# Revision 1.8  2001/11/13 20:44:51  thomas
# Clean up a little...
#
# Revision 1.7  2001/10/05 16:38:35  thomas
# Assure a clean shutdown of the thread with atexit
#
# Revision 1.6  2001/10/04 19:21:46  thomas
# Only some comments added and a typo fixed.
#
# Revision 1.3  2001/10/04 18:37:54  thomas
# Stores now old objects with (modulename, objectname) as keys.
#
# Revision 1.1  2001/10/04 16:54:04  thomas
# Discovered this old module on my machine, it didn't work too well,
# but seems worth to continue with it...
# Checked in as a first step.

version = "$Revision: 1.8 $".split()[1]

# ToDo:
#
#  Cannot reload __main__ - explain why this cannot work
#
#  Optimize - the number of watches objects (in old_objects)
#  grows without limits. Think if this is really necessary...


import time, os, threading, sys, types, imp

def _get_compiled_ext():
    for ext, mode, typ in imp.get_suffixes():
        if typ == imp.PY_COMPILED:
            return ext

PY_COMPILED_EXT = _get_compiled_ext()

class ModuleWatcher:
    running = 0
    def __init__(self):
        # If we don't do this, there may be tracebacks
        # when shutting down python.
        import atexit
        atexit.register(self.stop)

    def run(self):
        if self.running:
            print "# autoreload already running"
            return
        self.running = 1
        self.thread = threading.Thread(target=self._check_modules)
        self.thread.setDaemon(1)
        self.thread.start()

    def stop(self):
        if not self.running:
            print "# autoreload not running"
            return
        self.running = 0
        self.thread.join()
##        print "# autoreload stopped"

    def _check_modules(self):
##        print "# autoreload running"
        while self.running:
            time.sleep(0.01)
            for m in sys.modules.values():
                if not hasattr(m, '__file__'):
                    continue
                if m.__name__ == '__main__':

                    # we cannot reload(__main__) First I thought we
                    # could use mod = imp.load_module() and then
                    # reload(mod) to simulate reload(main), but this
                    # would execute the code in __main__ a second
                    # time.

                    continue
                file = m.__file__
                dirname = os.path.dirname(file)
                path, ext = os.path.splitext(file)

                if ext.lower() == '.py':
                    ext = PY_COMPILED_EXT
                    file = os.path.join(dirname, path + PY_COMPILED_EXT)

                if ext != PY_COMPILED_EXT:
                    continue

                try:
                    if os.stat(file[:-1])[8] <= os.stat(file)[8]:
                        continue
                except OSError:
                    continue

                try:
                    superreload(m)
                except:
                    import traceback
                    traceback.print_exc(0)

def superreload(module,
                reload=reload,
                _old_objects={}):
    """superreload (module) -> module

    Enhanced version of the builtin reload function.
    superreload replaces the class dictionary of every top-level
    class in the module with the new one automatically,
    as well as every function's code object.
    """
    start = time.clock()
    # retrieve the attributes from the module before the reload,
    # and remember them in _old_objects.
    for name, object in module.__dict__.items():
        key = (module.__name__, name)
        _old_objects[key] = _old_objects.get(key, []) + [object]

    module = reload(module)

    # iterate over all objects and update them
    count = 0
    # XXX We should optimize here:
    # It may be that no references to the objects are present
    # except those from our _old_objects dictionary.
    # We should remove those. I have to learn about weak-refs!
    for name, new_obj in module.__dict__.items():
        key = (module.__name__, name)
        if _old_objects.has_key(key):
            for old_obj in _old_objects[key]:
##                if sys.getrefcount(old_obj) == 3:
##                    _old_objects[key].remove(old_obj)
##                    continue
                if type(new_obj) == types.ClassType:
                    old_obj.__dict__ = new_obj.__dict__.copy()
                    count += 1
                    print sys.getrefcount(old_obj), name
                elif type(new_obj) == types.FunctionType:
                    old_obj.func_code = new_obj.func_code
                    count += 1
                    print sys.getrefcount(old_obj), name
    stop = time.clock()
    print "# updated %d objects from %s" % (count, module)
    print "# This took %.3f seconds" % (stop - start)
    print
    return module

_watcher = ModuleWatcher()

run = _watcher.run
stop = _watcher.stop
---- EOF ----





More information about the Python-list mailing list