def __init__(self, fs, ignore_config=None): self.listener_map = {} self.log = logging.getLogger('ubuntuone.SyncDaemon.EQ') self.fs = fs self.monitor = FilesystemMonitor(self, fs, ignore_config) self.dispatching = False self.dispatch_queue = collections.deque() self._have_empty_eq_cback = False self.empty_event_queue_callbacks = set() self.ignored_base_exception = Exception
class EventQueue(object): """Manages the events from different sources and distributes them.""" def __init__(self, fs, ignore_config=None): self.listener_map = {} self.log = logging.getLogger('ubuntuone.SyncDaemon.EQ') self.fs = fs self.monitor = FilesystemMonitor(self, fs, ignore_config) self.dispatching = False self.dispatch_queue = collections.deque() self._have_empty_eq_cback = False self.empty_event_queue_callbacks = set() self.ignored_base_exception = Exception def add_to_mute_filter(self, event, **info): """Add info to mute filter in the processor.""" self.monitor.add_to_mute_filter(event, **info) def rm_from_mute_filter(self, event, **info): """Remove info to mute filter in the processor.""" self.monitor.rm_from_mute_filter(event, **info) def add_empty_event_queue_callback(self, callback): """Add a callback for when the even queue has no more events.""" self._have_empty_eq_cback = True self.empty_event_queue_callbacks.add(callback) if not self.dispatching and not self.dispatch_queue: if callable(callback): callback() def remove_empty_event_queue_callback(self, callback): """Remove the callback.""" self.empty_event_queue_callbacks.remove(callback) if not self.empty_event_queue_callbacks: self._have_empty_eq_cback = False @defer.inlineCallbacks def shutdown(self): """Make the monitor shutdown.""" yield self.monitor.shutdown() # clean up all registered listeners if len(self.listener_map.items()) > 0: for k, v in self.listener_map.items(): v.clear() del self.listener_map[k] def rm_watch(self, dirpath): """Remove watch from a dir.""" self.monitor.rm_watch(dirpath) def add_watch(self, dirpath): """Add watch to a dir.""" return self.monitor.add_watch(dirpath) def add_watches_to_udf_ancestors(self, volume): """Add a inotify watch to volume's ancestors if it's an UDF.""" # This is a platform dependent operation since there are cases in # which the watches do not have to be added (On windows we do not # have to add them since we have an opened handle.) # finally, check that UDF is ok in disk if not access(volume.path): # if we cannot access the UDF lets return false and do # nothing return defer.succeed(False) return self.monitor.add_watches_to_udf_ancestors(volume) def unsubscribe(self, obj): """Remove the callback object from the listener queue. @param obj: the callback object to remove from the queue. """ for k, v in self.listener_map.items(): v.pop(obj, None) if not v: del self.listener_map[k] def subscribe(self, obj): """Store the callback object to whom push the events when received. @param obj: the callback object to add to the listener queue. These objects should provide a 'handle_FOO' to receive the FOO events (replace FOO with the desired event). """ for event_name in EVENTS.keys(): meth_name = "handle_" + event_name method = self._get_listener_method(obj, meth_name, event_name) if method is not None: self.listener_map.setdefault(event_name, {})[obj] = method def push(self, event_name, **kwargs): """Receives a push for all events. The signature for each event is forced on each method, not in this 'push' arguments. """ log_msg = "push_event: %s, kwargs: %s" if event_name.endswith('DELETE'): # log every DELETE in INFO level self.log.info(log_msg, event_name, kwargs) elif event_name == 'SYS_USER_CONNECT': self.log.debug(log_msg, event_name, '*') else: self.log.debug(log_msg, event_name, kwargs) # check if we are currently dispatching an event self.dispatch_queue.append((event_name, kwargs)) if not self.dispatching: self.dispatching = True while True: try: event_name, kwargs = self.dispatch_queue.popleft() self._dispatch(event_name, **kwargs) except IndexError: self.dispatching = False if self._have_empty_eq_cback: for cback in self.empty_event_queue_callbacks.copy(): cback() break def _dispatch(self, event_name, **kwargs): """ push the event to all listeners. """ try: listeners = self.listener_map[event_name] except KeyError: # no listener for this return # check listeners to see if have the proper method, and call it for listener, method in listeners.items(): try: method(**kwargs) except self.ignored_base_exception: self.log.exception("Error encountered while handling: %s " "in %s", event_name, listener) def _get_listener_method(self, listener, method_name, event_name): """ returns the method named method_name or hanlde_default from the listener. Or None if the methods are not defined in the listener. """ method = getattr(listener, method_name, None) if method is None: method = getattr(listener, DEFAULT_HANDLER, None) if method is not None: method = functools.partial(method, event_name) return method def is_frozen(self): """Checks if there's something frozen.""" return self.monitor.is_frozen() def freeze_begin(self, path): """Puts in hold all the events for this path.""" self.monitor.freeze_begin(path) def freeze_rollback(self): """Unfreezes the frozen path, reseting to idle state.""" self.monitor.freeze_rollback() def freeze_commit(self, events): """Unfreezes the frozen path, sending received events if not dirty. If events for that path happened: - return True else: - push the here received events, return False """ return self.monitor.freeze_commit(events)