From e2730fdc8fa0dd5ca97f7e3098e4ebf777e44580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Klein?= <34026167+LKleinNux@users.noreply.github.com> Date: Wed, 19 Sep 2018 14:40:10 +0200 Subject: [PATCH] Allow to call FSEventsEmitter.stop() twice without error --- AUTHORS | 1 + src/watchdog/observers/fsevents.py | 6 ++- tests/test_fsevents.py | 64 ++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 tests/test_fsevents.py diff --git a/AUTHORS b/AUTHORS index b34e56d1d..7330412ae 100644 --- a/AUTHORS +++ b/AUTHORS @@ -13,6 +13,7 @@ Gary van der Merwe Gora Khargosh Hannu Valtonen Jesse Printz +Léa Klein Luke McCarthy Lukáš Lalinský Malthe Borch diff --git a/src/watchdog/observers/fsevents.py b/src/watchdog/observers/fsevents.py index 5edfebec5..a3739d2e3 100644 --- a/src/watchdog/observers/fsevents.py +++ b/src/watchdog/observers/fsevents.py @@ -73,8 +73,10 @@ def __init__(self, event_queue, watch, timeout=DEFAULT_EMITTER_TIMEOUT): self.snapshot = DirectorySnapshot(watch.path, watch.is_recursive) def on_thread_stop(self): - _fsevents.remove_watch(self.watch) - _fsevents.stop(self) + if self.watch: + _fsevents.remove_watch(self.watch) + _fsevents.stop(self) + self._watch = None def queue_events(self, timeout): with self._lock: diff --git a/tests/test_fsevents.py b/tests/test_fsevents.py new file mode 100644 index 000000000..bad885165 --- /dev/null +++ b/tests/test_fsevents.py @@ -0,0 +1,64 @@ +# coding: utf-8 +import logging +import os +from functools import partial + +import pytest + +from watchdog.observers.api import ObservedWatch +from tests import Queue + +try: + from watchdog.observers.fsevents import FSEventsEmitter +except ImportError: + pytestmark = pytest.mark.skip("macOS only.") + +from .shell import mkdtemp, rm + +logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger(__name__) + + +def setup_function(function): + global p, event_queue + tmpdir = os.path.realpath(mkdtemp()) + p = partial(os.path.join, tmpdir) + event_queue = Queue() + + +def teardown_function(function): + emitter.stop() + emitter.join(5) + rm(p(""), recursive=True) + assert not emitter.is_alive() + + +def start_watching(path=None, use_full_emitter=False): + global emitter + path = p("") if path is None else path + emitter = FSEventsEmitter(event_queue, ObservedWatch(path, recursive=True)) + emitter.start() + + +def test_remove_watch_twice(): + """ +ValueError: PyCapsule_GetPointer called with invalid PyCapsule object +The above exception was the direct cause of the following exception: + +src/watchdog/utils/__init__.py:92: in stop + self.on_thread_stop() + +src/watchdog/observers/fsevents.py:73: SystemError + def on_thread_stop(self): +> _fsevents.remove_watch(self.watch) +E SystemError: returned a result with an error set + +(FSEvents.framework) FSEventStreamStop(): failed assertion 'streamRef != NULL' +(FSEvents.framework) FSEventStreamInvalidate(): failed assertion 'streamRef != NULL' +(FSEvents.framework) FSEventStreamRelease(): failed assertion 'streamRef != NULL' + """ + start_watching() + # This one must work + emitter.stop() + # This is allowed to call several times .stop() + emitter.stop()