PageRenderTime 695ms CodeModel.GetById 115ms app.highlight 210ms RepoModel.GetById 352ms app.codeStats 0ms

/silverlining/paste-reloader.py

https://bitbucket.org/ianb/silverlining/
Python | 133 lines | 123 code | 0 blank | 10 comment | 1 complexity | 49574b54c92ef9b43ab3eb0abc986fc5 MD5 | raw file
  1# Copied from paste.reloader:
  2"""
  3Use this like::
  4
  5    import reloader
  6    reloader.install()
  7
  8Then make sure your server is installed with a shell script like::
  9
 10    err=3
 11    while test "$err" -eq 3 ; do
 12        python server.py
 13        err="$?"
 14    done
 15
 16or restart in Python (server.py does this).  Use the watch_file(filename)
 17function to cause a reload/restart for other other non-Python files (e.g.,
 18configuration files).
 19"""
 20
 21import os
 22import sys
 23import time
 24import threading
 25import atexit
 26
 27# Copied from paste.util.classinstance:
 28class classinstancemethod(object):
 29    """
 30    Acts like a class method when called from a class, like an
 31    instance method when called by an instance.  The method should
 32    take two arguments, 'self' and 'cls'; one of these will be None
 33    depending on how the method was called.
 34    """
 35
 36    def __init__(self, func):
 37        self.func = func
 38
 39    def __get__(self, obj, type=None):
 40        return _methodwrapper(self.func, obj=obj, type=type)
 41
 42class _methodwrapper(object):
 43
 44    def __init__(self, func, obj, type):
 45        self.func = func
 46        self.obj = obj
 47        self.type = type
 48
 49    def __call__(self, *args, **kw):
 50        assert not kw.has_key('self') and not kw.has_key('cls'), (
 51            "You cannot use 'self' or 'cls' arguments to a "
 52            "classinstancemethod")
 53        return self.func(*((self.obj, self.type) + args), **kw)
 54
 55    def __repr__(self):
 56        if self.obj is None:
 57            return ('<bound class method %s.%s>'
 58                    % (self.type.__name__, self.func.func_name))
 59        else:
 60            return ('<bound method %s.%s of %r>'
 61                    % (self.type.__name__, self.func.func_name, self.obj))
 62
 63def install(poll_interval=1, raise_keyboard_interrupt=True):
 64    mon = Monitor(poll_interval=poll_interval,
 65                  raise_keyboard_interrupt=raise_keyboard_interrupt)
 66    t = threading.Thread(target=mon.periodic_reload)
 67    t.start()
 68    
 69class Monitor:
 70
 71    instances = []
 72    global_extra_files = []
 73
 74    def __init__(self, poll_interval, raise_keyboard_interrupt):
 75        self.module_mtimes = {}
 76        atexit.register(self.atexit)
 77        self.keep_running = True
 78        self.poll_interval = poll_interval
 79        self.raise_keyboard_interrupt = raise_keyboard_interrupt
 80        self.extra_files = self.global_extra_files[:]
 81        self.instances.append(self)
 82
 83    def atexit(self):
 84        self.keep_running = False
 85        if self.raise_keyboard_interrupt:
 86            # This exception is somehow magic, because it applies
 87            # to more threads and situations (like socket.accept)
 88            # that a mere SystemExit will not.
 89            raise KeyboardInterrupt("Exiting process")
 90
 91    def periodic_reload(self):
 92        while 1:
 93            if not self.keep_running:
 94                break
 95            if not self.check_reload():
 96                os._exit(3)
 97                break
 98            time.sleep(self.poll_interval)
 99
100    def check_reload(self):
101        filenames = self.extra_files[:]
102        for module in sys.modules.values():
103            try:
104                filenames.append(module.__file__)
105            except AttributeError:
106                continue
107        for filename in filenames:
108            try:
109                mtime = os.stat(filename).st_mtime
110            except (OSError, IOError):
111                continue
112            if filename.endswith('.pyc') and os.path.exists(filename[:-1]):
113                mtime = max(os.stat(filename[:-1]).st_mtime, mtime)
114            if not self.module_mtimes.has_key(filename):
115                self.module_mtimes[filename] = mtime
116            elif self.module_mtimes[filename] < mtime:
117                print >> sys.stderr, (
118                    "%s changed; reloading..." % filename)
119                return False
120        return True
121
122    def watch_file(self, cls, filename):
123        filename = os.path.abspath(filename)
124        if self is None:
125            for instance in cls.instances:
126                instance.watch_file(filename)
127            cls.global_extra_files.append(filename)
128        else:
129            self.extra_files.append(filename)
130
131    watch_file = classinstancemethod(watch_file)
132
133watch_file = Monitor.watch_file