From 131d18eec7460436796f15832bd8892c13602c44 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 5 Jun 2015 12:59:48 +0530 Subject: [PATCH] Auto-reload for OS X --- src/calibre/srv/auto_reload.py | 42 ++++++++++++++++++++++++++++++---- src/calibre/test_build.py | 8 ++++++- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/calibre/srv/auto_reload.py b/src/calibre/srv/auto_reload.py index d278e2696f..498e439ee7 100644 --- a/src/calibre/srv/auto_reload.py +++ b/src/calibre/srv/auto_reload.py @@ -9,7 +9,7 @@ __copyright__ = '2015, Kovid Goyal ' import os, sys, subprocess, signal, time from threading import Thread -from calibre.constants import islinux, iswindows +from calibre.constants import islinux, iswindows, isosx class NoAutoReload(EnvironmentError): pass @@ -17,6 +17,9 @@ class NoAutoReload(EnvironmentError): EXTENSIONS_TO_WATCH = frozenset('py pyj'.split()) BOUNCE_INTERVAL = 2 # seconds +def file_is_watched(fname): + return fname and fname.rpartition('.')[-1] in EXTENSIONS_TO_WATCH + class WatcherBase(object): def __init__(self, server, log): @@ -42,7 +45,7 @@ if islinux: from calibre.utils.inotify import INotifyTreeWatcher def ignore_event(path, name): - return name and name.rpartition('.')[-1] not in EXTENSIONS_TO_WATCH + return not file_is_watched(name) class Watcher(WatcherBase): @@ -101,7 +104,7 @@ elif iswindows: None, None ) for action, filename in results: - if filename and filename.rpartition('.')[-1] in EXTENSIONS_TO_WATCH: + if file_is_watched(filename): self.modified_queue.put(os.path.join(self.path_to_watch, filename)) except Exception: import traceback @@ -124,8 +127,37 @@ elif iswindows: path = self.modified_queue.get() if path is None: break - modified = {path} - self.handle_modified(modified) + self.handle_modified({path}) + +elif isosx: + from fsevents import Observer, Stream + + class Watcher(WatcherBase): + + def __init__(self, root_dirs, server, log): + WatcherBase.__init__(self, server, log) + self.stream = Stream(self.notify, *(x.encode('utf-8') for x in root_dirs), file_events=True) + + def loop(self): + observer = Observer() + observer.schedule(self.stream) + observer.daemon = True + observer.start() + try: + while True: + # Cannot use observer.join() as it is not interrupted by + # Ctrl-C + time.sleep(10000) + finally: + observer.unschedule(self.stream) + observer.stop() + + def notify(self, ev): + name = ev.name + if isinstance(name, bytes): + name = name.decode('utf-8') + if file_is_watched(name): + self.handle_modified({name}) else: Watcher = None diff --git a/src/calibre/test_build.py b/src/calibre/test_build.py index 597135b4e3..fa8805577c 100644 --- a/src/calibre/test_build.py +++ b/src/calibre/test_build.py @@ -13,7 +13,7 @@ Test a binary calibre build to ensure that all needed binary images/libraries ha ''' import cStringIO, os, ctypes -from calibre.constants import plugins, iswindows, islinux +from calibre.constants import plugins, iswindows, islinux, isosx def test_dlls(): import win32api @@ -79,6 +79,10 @@ def test_certgen(): from calibre.utils.certgen import create_key_pair create_key_pair() +def test_fsevents(): + from fsevents import Observer, Stream + del Observer, Stream + def test_winutil(): from calibre.devices.scanner import win_pnp_drives from calibre.constants import plugins @@ -245,6 +249,8 @@ def test(): test_terminal() if islinux: test_dbus() + if isosx: + test_fsevents() if __name__ == '__main__': test()