From ce40f75a619f11dc305d5d8ebc2575fd0fc98bca Mon Sep 17 00:00:00 2001 From: Renat Zaripov Date: Wed, 4 Jul 2018 19:01:13 +0300 Subject: [PATCH 1/5] Generate sub created events only if recursive=True When directory with some files will be copied in to observed directory, watchdog emits events about that files created, even if recursive switched off. --- src/watchdog/observers/read_directory_changes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/watchdog/observers/read_directory_changes.py b/src/watchdog/observers/read_directory_changes.py index 623a23563..a2d037f03 100644 --- a/src/watchdog/observers/read_directory_changes.py +++ b/src/watchdog/observers/read_directory_changes.py @@ -112,7 +112,7 @@ def queue_events(self, timeout): isdir = os.path.isdir(src_path) cls = DirCreatedEvent if isdir else FileCreatedEvent self.queue_event(cls(src_path)) - if isdir: + if isdir and self.watch.is_recursive: # If a directory is moved from outside the watched folder to inside it # we only get a created directory event out of it, not any events for its children # so use the same hack as for file moves to get the child events From aa032994aa212af0e08013b1768c9beae5cc6777 Mon Sep 17 00:00:00 2001 From: Renat Zaripov Date: Tue, 18 Sep 2018 12:58:01 +0300 Subject: [PATCH 2/5] Add appveyor.yml for running test on Windows --- appveyor.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..9bed8dc63 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,19 @@ +build: off + +cache: + - '%LOCALAPPDATA%\pip\Cache' + +environment: + matrix: + - PYTHON: 'c:\Python27' + - PYTHON: 'c:\Python27-x64' + - PYTHON: 'c:\Python36' + - PYTHON: 'c:\Python36-x64' + +install: + - "SET PATH=%PYTHON%;%PYTHON%\Scripts;%PATH%" + - "python --version" + - "pip install tox" + +test_script: + tox From aed8bd21b2969c5e7a47f6fb227471f372bf186d Mon Sep 17 00:00:00 2001 From: Renat Zaripov Date: Tue, 18 Sep 2018 12:59:58 +0300 Subject: [PATCH 3/5] Fix appveyor.yml --- appveyor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 9bed8dc63..9600dab92 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,9 +11,9 @@ environment: - PYTHON: 'c:\Python36-x64' install: - - "SET PATH=%PYTHON%;%PYTHON%\Scripts;%PATH%" - - "python --version" - - "pip install tox" + - SET PATH=%PYTHON%;%PYTHON%\Scripts;%PATH% + - python --version + - pip install tox test_script: tox From e7cc57aadb6a63730d0c656d87057fdfe7668edd Mon Sep 17 00:00:00 2001 From: Renat Zaripov Date: Thu, 20 Dec 2018 16:12:39 +0300 Subject: [PATCH 4/5] Add Windows support in emitter tests, fix some PEP-8 issues. --- tests/test_emitter.py | 146 ++++++++++++++++++++++++++++++++---------- 1 file changed, 111 insertions(+), 35 deletions(-) diff --git a/tests/test_emitter.py b/tests/test_emitter.py index ce0d4c6e4..f58e4929a 100644 --- a/tests/test_emitter.py +++ b/tests/test_emitter.py @@ -19,7 +19,7 @@ import time import pytest import logging -from tests import Queue +from tests import Queue, Empty from functools import partial from .shell import mkdir, touch, mv, rm, mkdtemp from watchdog.utils import platform @@ -43,8 +43,12 @@ elif platform.is_darwin(): pytestmark = pytest.mark.skip("FIXME: It is a matter of bad comparisons between bytes and str.") from watchdog.observers.fsevents2 import FSEventsEmitter as Emitter +elif platform.is_windows(): + from watchdog.observers.read_directory_changes import ( + WindowsApiEmitter as Emitter + ) else: - pytestmark = pytest.mark.skip("GNU/Linux and macOS only.") + pytestmark = pytest.mark.skip("GNU/Linux, macOS and Windows only.") logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) @@ -57,16 +61,16 @@ def setup_function(function): event_queue = Queue() -def start_watching(path=None, use_full_emitter=False): +def start_watching(path=None, use_full_emitter=False, recursive=True): path = p('') if path is None else path global emitter if platform.is_linux() and use_full_emitter: - emitter = InotifyFullEmitter(event_queue, ObservedWatch(path, recursive=True)) + emitter = InotifyFullEmitter(event_queue, ObservedWatch(path, recursive=recursive)) else: - emitter = Emitter(event_queue, ObservedWatch(path, recursive=True)) + emitter = Emitter(event_queue, ObservedWatch(path, recursive=recursive)) if platform.is_darwin(): - # FSEvents will report old evens (like create for mkdtemp in test + # FSEvents will report old events (like create for mkdtemp in test # setup. Waiting for a considerable time seems to 'flush' the events. time.sleep(10) emitter.start() @@ -87,9 +91,10 @@ def test_create(): assert event.src_path == p('a') assert isinstance(event, FileCreatedEvent) - event = event_queue.get(timeout=5)[0] - assert os.path.normpath(event.src_path) == os.path.normpath(p('')) - assert isinstance(event, DirModifiedEvent) + if not platform.is_windows(): + event = event_queue.get(timeout=5)[0] + assert os.path.normpath(event.src_path) == os.path.normpath(p('')) + assert isinstance(event, DirModifiedEvent) def test_delete(): @@ -101,9 +106,10 @@ def test_delete(): assert event.src_path == p('a') assert isinstance(event, FileDeletedEvent) - event = event_queue.get(timeout=5)[0] - assert os.path.normpath(event.src_path) == os.path.normpath(p('')) - assert isinstance(event, DirModifiedEvent) + if not platform.is_windows(): + event = event_queue.get(timeout=5)[0] + assert os.path.normpath(event.src_path) == os.path.normpath(p('')) + assert isinstance(event, DirModifiedEvent) def test_modify(): @@ -121,21 +127,29 @@ def test_move(): mkdir(p('dir2')) touch(p('dir1', 'a')) start_watching() - mv(p('dir1', 'a'), p('dir2', 'b')) - event = event_queue.get(timeout=5)[0] - assert event.src_path == p('dir1', 'a') - assert event.dest_path == p('dir2', 'b') - assert isinstance(event, FileMovedEvent) + if not platform.is_windows(): + event = event_queue.get(timeout=5)[0] + assert event.src_path == p('dir1', 'a') + assert event.dest_path == p('dir2', 'b') + assert isinstance(event, FileMovedEvent) + else: + event = event_queue.get(timeout=5)[0] + assert event.src_path == p('dir1', 'a') + assert isinstance(event, FileDeletedEvent) + event = event_queue.get(timeout=5)[0] + assert event.src_path == p('dir2', 'b') + assert isinstance(event, FileCreatedEvent) event = event_queue.get(timeout=5)[0] - assert event.src_path == p('dir1') + assert event.src_path in [p('dir1'), p('dir2')] assert isinstance(event, DirModifiedEvent) - event = event_queue.get(timeout=5)[0] - assert event.src_path == p('dir2') - assert isinstance(event, DirModifiedEvent) + if not platform.is_windows(): + event = event_queue.get(timeout=5)[0] + assert event.src_path in [p('dir1'), p('dir2')] + assert isinstance(event, DirModifiedEvent) def test_move_to(): @@ -143,76 +157,107 @@ def test_move_to(): mkdir(p('dir2')) touch(p('dir1', 'a')) start_watching(p('dir2')) - mv(p('dir1', 'a'), p('dir2', 'b')) + event = event_queue.get(timeout=5)[0] - assert isinstance(event, FileCreatedEvent) assert event.src_path == p('dir2', 'b') + assert isinstance(event, FileCreatedEvent) + if not platform.is_windows(): + event = event_queue.get(timeout=5)[0] + assert event.src_path == p('dir2') + assert isinstance(event, DirModifiedEvent) + + +@pytest.mark.skipif(platform.is_windows(), reason="InotifyFullEmitter not supported by Windows") def test_move_to_full(): mkdir(p('dir1')) mkdir(p('dir2')) touch(p('dir1', 'a')) start_watching(p('dir2'), use_full_emitter=True) mv(p('dir1', 'a'), p('dir2', 'b')) + event = event_queue.get(timeout=5)[0] assert isinstance(event, FileMovedEvent) assert event.dest_path == p('dir2', 'b') - assert event.src_path == None #Should equal none since the path was not watched + assert event.src_path is None # Should equal None since the path was not watched + def test_move_from(): mkdir(p('dir1')) mkdir(p('dir2')) touch(p('dir1', 'a')) start_watching(p('dir1')) - mv(p('dir1', 'a'), p('dir2', 'b')) + event = event_queue.get(timeout=5)[0] assert isinstance(event, FileDeletedEvent) assert event.src_path == p('dir1', 'a') - + + if not platform.is_windows(): + event = event_queue.get(timeout=5)[0] + assert event.src_path == p('dir1') + assert isinstance(event, DirModifiedEvent) + + +@pytest.mark.skipif(platform.is_windows(), reason="InotifyFullEmitter not supported by Windows") def test_move_from_full(): mkdir(p('dir1')) mkdir(p('dir2')) touch(p('dir1', 'a')) start_watching(p('dir1'), use_full_emitter=True) mv(p('dir1', 'a'), p('dir2', 'b')) + event = event_queue.get(timeout=5)[0] assert isinstance(event, FileMovedEvent) assert event.src_path == p('dir1', 'a') - assert event.dest_path == None #Should equal None since path not watched + assert event.dest_path == None # Should equal None since path not watched + def test_separate_consecutive_moves(): mkdir(p('dir1')) touch(p('dir1', 'a')) touch(p('b')) - start_watching(p('dir1')) - mv(p('dir1', 'a'), p('c')) mv(p('b'), p('dir1', 'd')) event = event_queue.get(timeout=5)[0] - assert isinstance(event, FileDeletedEvent) assert event.src_path == p('dir1', 'a') + assert isinstance(event, FileDeletedEvent) - assert isinstance(event_queue.get(timeout=5)[0], DirModifiedEvent) + if not platform.is_windows(): + event = event_queue.get(timeout=5)[0] + assert event.src_path == p('dir1') + assert isinstance(event, DirModifiedEvent) event = event_queue.get(timeout=5)[0] - assert isinstance(event, FileCreatedEvent) assert event.src_path == p('dir1', 'd') + assert isinstance(event, FileCreatedEvent) - assert isinstance(event_queue.get(timeout=5)[0], DirModifiedEvent) + if not platform.is_windows(): + event = event_queue.get(timeout=5)[0] + assert event.src_path == p('dir1') + assert isinstance(event, DirModifiedEvent) @pytest.mark.skipif(platform.is_linux(), reason="bug. inotify will deadlock") +@pytest.mark.skipif(platform.is_windows(), reason=""" +WindowsError: [Error 5] +access denied when trying delete directory dir1, because them opened by test +via start_watching.""") def test_delete_self(): mkdir(p('dir1')) start_watching(p('dir1')) rm(p('dir1'), True) - event_queue.get(timeout=5)[0] + + event = event_queue.get(timeout=5)[0] + assert event.src_path == p('dir1') + assert isinstance(event, FileDeletedEvent) +@pytest.mark.skipif(platform.is_windows(), + reason="Windows create another set of events for this test") def test_fast_subdirectory_creation_deletion(): root_dir = p('dir1') sub_dir = p('dir1', 'subdir1') @@ -248,8 +293,39 @@ def test_passing_unicode_should_give_unicode(): assert isinstance(event.src_path, str_cls) +@pytest.mark.skipif(platform.is_windows(), + reason="Windows ReadDirectoryChangesW supports only" + " unicode for paths.") def test_passing_bytes_should_give_bytes(): start_watching(p('').encode()) touch(p('a')) event = event_queue.get(timeout=5)[0] - assert isinstance(event.src_path, bytes) \ No newline at end of file + assert isinstance(event.src_path, bytes) + + +def test_recursive_on(): + mkdir(p('dir1', 'dir2', 'dir3'), True) + start_watching() + touch(p('dir1', 'dir2', 'dir3', 'a')) + + event = event_queue.get(timeout=5)[0] + assert event.src_path == p('dir1', 'dir2', 'dir3', 'a') + assert isinstance(event, FileCreatedEvent) + + if not platform.is_windows(): + event = event_queue.get(timeout=5)[0] + assert event.src_path == p('dir1', 'dir2', 'dir3') + assert isinstance(event, DirModifiedEvent) + + event = event_queue.get(timeout=5)[0] + assert event.src_path == p('dir1', 'dir2', 'dir3', 'a') + assert isinstance(event, FileModifiedEvent) + + +def test_recursive_off(): + mkdir(p('dir1')) + start_watching(recursive=False) + touch(p('dir1', 'a')) + + with pytest.raises(Empty): + event_queue.get(timeout=5) From dbc7a7b609cf3bbda9311b026a9198f2bcf9f1b3 Mon Sep 17 00:00:00 2001 From: Renat Zaripov Date: Thu, 20 Dec 2018 16:17:58 +0300 Subject: [PATCH 5/5] Add Empty for handling exceptions in tests --- tests/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index 10f0434df..85faa709b 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -17,8 +17,8 @@ import unittest try: - from Queue import Queue # Python 2 + from Queue import Queue, Empty # Python 2 except ImportError: - from queue import Queue # Python 3 + from queue import Queue, Empty # Python 3 -__all__ = ["unittest", "Queue"] +__all__ = ["unittest", "Queue", "Empty"]