Implement auto reload on windows

This commit is contained in:
Kovid Goyal 2015-06-05 11:33:01 +05:30
parent 74c50bf11a
commit 6ac01f3560

View File

@ -9,41 +9,24 @@ __copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
import os, sys, subprocess, signal, time
from threading import Thread
from calibre.constants import islinux
from calibre.constants import islinux, iswindows
class NoAutoReload(EnvironmentError):
pass
EXTENSIONS_TO_WATCH = frozenset('py pyj css js xml'.split())
EXTENSIONS_TO_WATCH = frozenset('py pyj'.split())
BOUNCE_INTERVAL = 2 # seconds
if islinux:
import select
from calibre.utils.inotify import INotifyTreeWatcher
class WatcherBase(object):
def ignore_event(path, name):
return name and name.rpartition('.')[-1] not in EXTENSIONS_TO_WATCH
class Watcher(object):
def __init__(self, root_dirs, server, log):
def __init__(self, server, log):
self.server, self.log = server, log
self.fd_map = {}
for d in frozenset(root_dirs):
w = INotifyTreeWatcher(d, ignore_event)
self.fd_map[w._inotify_fd] = w
self.last_restart_time = time.time()
fpath = os.path.abspath(__file__)
d = os.path.dirname
self.base = d(d(d(d(fpath))))
self.last_restart_time = time.time()
def loop(self):
while True:
r = select.select(list(self.fd_map.iterkeys()), [], [])[0]
modified = set()
for fd in r:
w = self.fd_map[fd]
modified |= w()
def handle_modified(self, modified):
if modified:
if time.time() - self.last_restart_time > BOUNCE_INTERVAL:
modified = {os.path.relpath(x, self.base) if x.startswith(self.base) else x for x in modified if x}
@ -53,6 +36,97 @@ if islinux:
self.log('')
self.server.restart()
self.last_restart_time = time.time()
if islinux:
import select
from calibre.utils.inotify import INotifyTreeWatcher
def ignore_event(path, name):
return name and name.rpartition('.')[-1] not in EXTENSIONS_TO_WATCH
class Watcher(WatcherBase):
def __init__(self, root_dirs, server, log):
WatcherBase.__init__(self, server, log)
self.fd_map = {}
for d in frozenset(root_dirs):
w = INotifyTreeWatcher(d, ignore_event)
self.fd_map[w._inotify_fd] = w
def loop(self):
while True:
r = select.select(list(self.fd_map.iterkeys()), [], [])[0]
modified = set()
for fd in r:
w = self.fd_map[fd]
modified |= w()
self.handle_modified()
elif iswindows:
import win32file, win32con
from Queue import Queue
FILE_LIST_DIRECTORY = 0x0001
from calibre.srv.utils import HandleInterrupt
class TreeWatcher(Thread):
daemon = True
def __init__(self, path_to_watch, modified_queue):
Thread.__init__(self, name='TreeWatcher')
self.modified_queue = modified_queue
self.path_to_watch = path_to_watch
self.dir_handle = win32file.CreateFile(
path_to_watch,
FILE_LIST_DIRECTORY,
win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE | win32con.FILE_SHARE_DELETE,
None,
win32con.OPEN_EXISTING,
win32con.FILE_FLAG_BACKUP_SEMANTICS,
None
)
def run(self):
try:
while True:
results = win32file.ReadDirectoryChangesW(
self.dir_handle,
8192, # Buffer size for storing events
True, # Watch sub-directories as well
win32con.FILE_NOTIFY_CHANGE_FILE_NAME |
win32con.FILE_NOTIFY_CHANGE_DIR_NAME |
win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES |
win32con.FILE_NOTIFY_CHANGE_SIZE |
win32con.FILE_NOTIFY_CHANGE_LAST_WRITE |
win32con.FILE_NOTIFY_CHANGE_SECURITY,
None, None
)
for action, filename in results:
if filename and filename.rpartition('.')[-1] in EXTENSIONS_TO_WATCH:
self.modified_queue.put(os.path.join(self.path_to_watch, filename))
except Exception:
import traceback
traceback.print_exc()
class Watcher(WatcherBase):
def __init__(self, root_dirs, server, log):
WatcherBase.__init__(self, server, log)
self.watchers = []
self.modified_queue = Queue()
for d in frozenset(root_dirs):
self.watchers.append(TreeWatcher(d, self.modified_queue))
def loop(self):
for w in self.watchers:
w.start()
with HandleInterrupt(lambda : self.modified_queue.put(None)):
while True:
path = self.modified_queue.get()
if path is None:
break
modified = {path}
self.handle_modified(modified)
else:
Watcher = None