Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --smoothing parameter for watchmedo --command to smooth over / ignore multiple triggering events. #231

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion src/watchdog/observers/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@

from __future__ import with_statement
import threading
import time

try:
import queue # IGNORE:F0401
Expand All @@ -62,6 +63,7 @@

DEFAULT_EMITTER_TIMEOUT = 1 # in seconds.
DEFAULT_OBSERVER_TIMEOUT = 1 # in seconds.
DEFAULT_SMOOTHING_TIMEOUT = 0 # in seconds.


# Collection classes
Expand Down Expand Up @@ -247,14 +249,15 @@ class BaseObserver(EventDispatcher):

"""Base observer."""

def __init__(self, emitter_class, timeout=DEFAULT_OBSERVER_TIMEOUT):
def __init__(self, emitter_class, timeout=DEFAULT_OBSERVER_TIMEOUT, smoothing=DEFAULT_SMOOTHING_TIMEOUT):
EventDispatcher.__init__(self, timeout)
self._emitter_class = emitter_class
self._lock = threading.Lock()
self._watches = set()
self._handlers = dict()
self._emitters = set()
self._emitter_for_watch = dict()
self._smoothing = smoothing

def _add_emitter(self, emitter):
self._emitter_for_watch[emitter.watch] = emitter
Expand Down Expand Up @@ -404,6 +407,18 @@ def _dispatch_event(self, event, watch):

def dispatch_events(self, event_queue, timeout):
event, watch = event_queue.get(block=True, timeout=timeout)

# Sleep for the specified amount of time, then clean out the
# event queue, and then dispatch the event.
if self._smoothing > 0:
time.sleep(self._smoothing)

if not event_queue.empty():
print("Smoothing over / ignoring {0} filesystem events that arrived after the triggering event.".format(event_queue.qsize()))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

possible race condition here


while not event_queue.empty():
event_queue.get_nowait()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here.


try:
self._dispatch_event(event, watch)
except KeyError:
Expand Down
8 changes: 5 additions & 3 deletions src/watchdog/observers/fsevents.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@
BaseObserver,
EventEmitter,
DEFAULT_EMITTER_TIMEOUT,
DEFAULT_OBSERVER_TIMEOUT
DEFAULT_OBSERVER_TIMEOUT,
DEFAULT_SMOOTHING_TIMEOUT
)


Expand Down Expand Up @@ -143,9 +144,10 @@ def callback(pathnames, flags, emitter=self):

class FSEventsObserver(BaseObserver):

def __init__(self, timeout=DEFAULT_OBSERVER_TIMEOUT):
def __init__(self, timeout=DEFAULT_OBSERVER_TIMEOUT, smoothing=DEFAULT_SMOOTHING_TIMEOUT):
BaseObserver.__init__(self, emitter_class=FSEventsEmitter,
timeout=timeout)
timeout=timeout,
smoothing=smoothing)

def schedule(self, event_handler, path, recursive=False):
# Python 2/3 compat
Expand Down
8 changes: 5 additions & 3 deletions src/watchdog/observers/inotify.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@
EventEmitter,
BaseObserver,
DEFAULT_EMITTER_TIMEOUT,
DEFAULT_OBSERVER_TIMEOUT
DEFAULT_OBSERVER_TIMEOUT,
DEFAULT_SMOOTHING_TIMEOUT
)

from watchdog.events import (
Expand Down Expand Up @@ -183,6 +184,7 @@ class InotifyObserver(BaseObserver):
calls to event handlers.
"""

def __init__(self, timeout=DEFAULT_OBSERVER_TIMEOUT):
def __init__(self, timeout=DEFAULT_OBSERVER_TIMEOUT, smoothing=DEFAULT_SMOOTHING_TIMEOUT):
BaseObserver.__init__(self, emitter_class=InotifyEmitter,
timeout=timeout)
timeout=timeout,
smoothing=smoothing)
7 changes: 4 additions & 3 deletions src/watchdog/observers/kqueue.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@
BaseObserver,
EventEmitter,
DEFAULT_OBSERVER_TIMEOUT,
DEFAULT_EMITTER_TIMEOUT
DEFAULT_EMITTER_TIMEOUT,
DEFAULT_SMOOTHING_TIMEOUT
)

from watchdog.utils.dirsnapshot import DirectorySnapshot
Expand Down Expand Up @@ -722,5 +723,5 @@ class KqueueObserver(BaseObserver):
calls to event handlers.
"""

def __init__(self, timeout=DEFAULT_OBSERVER_TIMEOUT):
BaseObserver.__init__(self, emitter_class=KqueueEmitter, timeout=timeout)
def __init__(self, timeout=DEFAULT_OBSERVER_TIMEOUT, smoothing=DEFAULT_SMOOTHING_TIMEOUT):
BaseObserver.__init__(self, emitter_class=KqueueEmitter, timeout=timeout, smoothing=DEFAULT_SMOOTHING_TIMEOUT)
7 changes: 4 additions & 3 deletions src/watchdog/observers/polling.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
EventEmitter,
BaseObserver,
DEFAULT_OBSERVER_TIMEOUT,
DEFAULT_EMITTER_TIMEOUT
DEFAULT_EMITTER_TIMEOUT,
DEFAULT_SMOOTHING_TIMEOUT
)

from watchdog.events import (
Expand Down Expand Up @@ -119,8 +120,8 @@ class PollingObserver(BaseObserver):
system changes.
"""

def __init__(self, timeout=DEFAULT_OBSERVER_TIMEOUT):
BaseObserver.__init__(self, emitter_class=PollingEmitter, timeout=timeout)
def __init__(self, timeout=DEFAULT_OBSERVER_TIMEOUT, smoothing=DEFAULT_SMOOTHING_TIMEOUT):
BaseObserver.__init__(self, emitter_class=PollingEmitter, timeout=timeout, smoothing=smoothing)


class PollingObserverVFS(BaseObserver):
Expand Down
8 changes: 5 additions & 3 deletions src/watchdog/observers/read_directory_changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
EventEmitter,
BaseObserver,
DEFAULT_OBSERVER_TIMEOUT,
DEFAULT_EMITTER_TIMEOUT
DEFAULT_EMITTER_TIMEOUT,
DEFAULT_SMOOTHING_TIMEOUT
)

from watchdog.events import (
Expand Down Expand Up @@ -138,10 +139,11 @@ class WindowsApiObserver(BaseObserver):
calls to event handlers.
"""

def __init__(self, timeout=DEFAULT_OBSERVER_TIMEOUT):
def __init__(self, timeout=DEFAULT_OBSERVER_TIMEOUT, smoothing=DEFAULT_SMOOTHING_TIMEOUT):
BaseObserver.__init__(self,
emitter_class=WindowsApiEmitter,
timeout=timeout)
timeout=timeout,
smoothing=smoothing)


def _generate_sub_created_events_for(src_dir_path):
Expand Down
6 changes: 5 additions & 1 deletion src/watchdog/watchmedo.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,10 @@ def log(args):
action='store_true',
default=False,
help="Ignore events that happen while the previous process is still running")
@arg('-s', '--smoothing',
dest='smoothing',
default=0,
help='gather and ignore this many seconds worth of filesystem events after the initial event before running the shell command')
def shell_command(args):
"""
Subcommand to execute shell commands in response to file system events.
Expand All @@ -438,7 +442,7 @@ def shell_command(args):
ignore_directories=args.ignore_directories,
wait_for_process=args.wait_for_process,
no_parallel_processes=args.no_parallel_processes)
observer = Observer(timeout=args.timeout)
observer = Observer(timeout=args.timeout, smoothing=args.smoothing)
observe_with(observer, handler, args.directories, args.recursive)


Expand Down