(automatically) reloading changed modules into a running program
Thomas Heller
thomas.heller at ion-tof.com
Thu Nov 15 13:55:16 EST 2001
I wrote a script which
- automatically reloads changed modules depending on their timestamp
- updates existing objects in a running program: classes,
functions, bound and unbound methods
The purpose is to speed up the development process, not to
update long running programs on the fly.
The script is not yet finished, however, I believe it is working.
Eventually I plan to submit it into the Python Cookbook.
I post it here to get early comments and suggestions for improvements.
Cheers,
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()
#
# or you can use the superreload() function
# instead of the standard reload() function.
#
# Created: Thomas Heller, 2000-04-17
#
# $Id: autoreload.py,v 1.9 2001/11/15 18:41:18 thomas Exp $
#
# $Log: autoreload.py,v $
# Revision 1.9 2001/11/15 18:41:18 thomas
# Cleaned up and made working again before posting to c.l.p.
# Added code to update bound (or unbound) methods as suggested
# by Just van Rossum. Thanks!
#
# ...
#
# 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.9 $".split()[1]
# ToDo:
#
# Cannot reload __main__ - explain why this cannot work,
# and explain a workaround.
#
# 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
# the official way to get the extension of compiled files (.pyc or .pyo)
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
print "# starting autoreload"
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):
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 update_function(old, new, attrnames):
for name in attrnames:
setattr(old, name, getattr(new, name))
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.setdefault(key, []).append(object)
# print the refcount of old objects:
## if type(object) in (types.FunctionType, types.ClassType):
## print name, map(sys.getrefcount, _old_objects[key])
## print "# reloading module %r" % module
module = reload(module)
# XXX We have a problem here if importing the module fails!
# iterate over all objects and update them
count = 0
# XXX Can we 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 type(new_obj) == types.ClassType:
old_obj.__dict__.update(new_obj.__dict__)
count += 1
elif type(new_obj) == types.FunctionType:
update_function(old_obj,
new_obj,
"func_code func_defaults func_doc".split())
count += 1
elif type(new_obj) == types.MethodType:
update_function(old_obj.im_func,
new_obj.im_func,
"func_code func_defaults func_doc".split())
count += 1
## stop = time.clock()
## print "# updated %d objects from %s" % (count, module)
## print "# This took %.3f seconds" % (stop - start)
return module
_watcher = ModuleWatcher()
run = _watcher.run
stop = _watcher.stop
__all__ = ['run', 'stop', 'superreload']
---- EOF ----
More information about the Python-list
mailing list