Skip to content

Commit 8fb9409

Browse files
CCP-AporiaBoboTiG
andauthored
[mac] Fix relative path handling for non-recursive watch in FSEventsObserver (#799)
* Add test for issue #797 This works for me locally, let's see what the CI machine says. * Use absolute watch path in recursive check A watch path may be relative, while an event path is always absolute. Since the check for recursive events simply compared the watch path with the event path then that would fail in case the watch was set to a relative folder. This resolves the issue. Fixes #797 * Add support for ~ in watch path Same problem as relative paths. * Update changelog.rst Co-authored-by: Mickaël Schoentgen <[email protected]>
1 parent 50af6eb commit 8fb9409

File tree

3 files changed

+59
-5
lines changed

3 files changed

+59
-5
lines changed

changelog.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ Changelog
88

99
2021-xx-xx • `full history <https://github.com/gorakhargosh/watchdog/compare/v2.1.2...master>`__
1010

11+
- [mac] Fix relative path handling for non-recursive watch. (`#797 <https://github.com/gorakhargosh/watchdog/pull/797>`_)
1112
- [windows] On PyPy, events happening right after ``start()`` were missed. Add a workaround for that. (`#796 <https://github.com/gorakhargosh/watchdog/pull/796>`_)
12-
- Thanks to our beloved contributors: @oprypin
13+
- Thanks to our beloved contributors: @oprypin, @CCP-Aporia
1314

1415
2.1.1
1516
~~~~~

src/watchdog/observers/fsevents.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ def __init__(self, event_queue, watch, timeout=DEFAULT_EMITTER_TIMEOUT, suppress
8484
self._start_time = 0.0
8585
self._starting_state = None
8686
self._lock = threading.Lock()
87+
self._absolute_watch_path = os.path.abspath(os.path.expanduser(self.watch.path))
8788

8889
def on_thread_stop(self):
8990
_fsevents.remove_watch(self.watch)
@@ -104,14 +105,14 @@ def queue_event(self, event):
104105

105106
def _is_recursive_event(self, event):
106107
src_path = event.src_path if event.is_directory else os.path.dirname(event.src_path)
107-
if src_path == self._watch.path:
108+
if src_path == self._absolute_watch_path:
108109
return False
109110

110111
if isinstance(event, (FileMovedEvent, DirMovedEvent)):
111112
# when moving something into the watch path we must always take the dirname,
112113
# otherwise we miss out on `DirMovedEvent`s
113114
dest_path = os.path.dirname(event.dest_path)
114-
if dest_path == self._watch.path:
115+
if dest_path == self._absolute_watch_path:
115116
return False
116117

117118
return True

tests/test_fsevents.py

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ def teardown_function(function):
4646
rm(p(""), recursive=True)
4747

4848

49-
def start_watching(path=None, use_full_emitter=False):
49+
def start_watching(path=None, recursive=True, use_full_emitter=False):
5050
global emitter
5151
path = p("") if path is None else path
52-
emitter = FSEventsEmitter(event_queue, ObservedWatch(path, recursive=True), suppress_history=True)
52+
emitter = FSEventsEmitter(event_queue, ObservedWatch(path, recursive=recursive), suppress_history=True)
5353
emitter.start()
5454

5555

@@ -247,6 +247,58 @@ def test_converting_cfstring_to_pyunicode():
247247
emitter.stop()
248248

249249

250+
def test_recursive_check_accepts_relative_paths():
251+
"""See https://github.com/gorakhargosh/watchdog/issues/797
252+
253+
The test code provided in the defect observes the current working directory
254+
using ".". Since the watch path wasn't normalized then that failed.
255+
This test emulates the scenario.
256+
"""
257+
from watchdog.events import (
258+
PatternMatchingEventHandler,
259+
FileCreatedEvent,
260+
FileModifiedEvent
261+
)
262+
263+
class TestEventHandler(PatternMatchingEventHandler):
264+
def __init__(self, *args, **kwargs):
265+
super().__init__(*args, **kwargs)
266+
# the TestEventHandler instance is set to ignore_directories,
267+
# as such we won't get a DirModifiedEvent(p()) here.
268+
self.expected_events = [
269+
FileCreatedEvent(p('foo.json')),
270+
FileModifiedEvent(p('foo.json'))
271+
]
272+
self.observed_events = set()
273+
274+
def on_any_event(self, event):
275+
self.expected_events.remove(event)
276+
self.observed_events.add(event)
277+
278+
def done(self):
279+
return not self.expected_events
280+
281+
cwd = os.getcwd()
282+
os.chdir(p())
283+
event_handler = TestEventHandler(patterns=["*.json"], ignore_patterns=[], ignore_directories=True)
284+
observer = Observer()
285+
observer.schedule(event_handler, ".")
286+
observer.start()
287+
time.sleep(0.1)
288+
289+
try:
290+
touch(p('foo.json'))
291+
timeout_at = time.time() + 5
292+
while not event_handler.done() and time.time() < timeout_at:
293+
time.sleep(0.1)
294+
295+
assert event_handler.done()
296+
finally:
297+
os.chdir(cwd)
298+
observer.stop()
299+
observer.join()
300+
301+
250302
def test_watchdog_recursive():
251303
""" See https://github.com/gorakhargosh/watchdog/issues/706
252304
"""

0 commit comments

Comments
 (0)