From fc9b03ade3641bd70335f33b697c829a65daa7ab Mon Sep 17 00:00:00 2001 From: GreatBahram Date: Tue, 18 May 2021 11:08:01 +0430 Subject: [PATCH 01/39] add fileattrib and dirattrib event --- src/watchdog/events.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/watchdog/events.py b/src/watchdog/events.py index 8b9bca83f..da15723a3 100755 --- a/src/watchdog/events.py +++ b/src/watchdog/events.py @@ -100,6 +100,7 @@ EVENT_TYPE_CREATED = 'created' EVENT_TYPE_MODIFIED = 'modified' EVENT_TYPE_CLOSED = 'closed' +EVENT_TYPE_ATTRIB = 'attrib' class FileSystemEvent: @@ -201,6 +202,10 @@ class FileDeletedEvent(FileSystemEvent): event_type = EVENT_TYPE_DELETED +class FileAttribEvent(FileSystemEvent): + event_type = EVENT_TYPE_ATTRIB + + class FileModifiedEvent(FileSystemEvent): """File system event representing file modification on the file system.""" @@ -255,6 +260,11 @@ class DirMovedEvent(FileSystemMovedEvent): is_directory = True +class DirAttribEvent(FileSystemEvent): + event_type = EVENT_TYPE_ATTRIB + is_directory = True + + class FileSystemEventHandler: """ Base file system event handler that you can override methods from. @@ -275,6 +285,7 @@ def dispatch(self, event): EVENT_TYPE_MODIFIED: self.on_modified, EVENT_TYPE_MOVED: self.on_moved, EVENT_TYPE_CLOSED: self.on_closed, + EVENT_TYPE_ATTRIB: self.on_attrib, }[event.event_type](event) def on_any_event(self, event): @@ -331,6 +342,13 @@ def on_closed(self, event): :class:`FileClosedEvent` """ + def on_attrib(self, event): + """Called when a file or directory metadata is modified. + :param event: + Event representing file/directory metadata modification. + :type event: + :class:`FileAttribEvent` or :class:`DirAttribEvent` + """ class PatternMatchingEventHandler(FileSystemEventHandler): """ From e693eb1db7ac008ce59bb5252419c041d01cf01f Mon Sep 17 00:00:00 2001 From: GreatBahram Date: Tue, 18 May 2021 11:08:32 +0430 Subject: [PATCH 02/39] use fileattrib and dirattrib event in inotify module --- src/watchdog/observers/inotify.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/watchdog/observers/inotify.py b/src/watchdog/observers/inotify.py index ff0193843..734ad057e 100644 --- a/src/watchdog/observers/inotify.py +++ b/src/watchdog/observers/inotify.py @@ -87,6 +87,8 @@ FileMovedEvent, FileCreatedEvent, FileClosedEvent, + FileAttribEvent, + DirAttribEvent, generate_sub_moved_events, generate_sub_created_events, ) @@ -155,7 +157,7 @@ def queue_events(self, timeout, full_events=False): for sub_event in generate_sub_created_events(src_path): self.queue_event(sub_event) elif event.is_attrib: - cls = DirModifiedEvent if event.is_directory else FileModifiedEvent + cls = DirAttribEvent if event.is_directory else FileAttribEvent self.queue_event(cls(src_path)) elif event.is_modify: cls = DirModifiedEvent if event.is_directory else FileModifiedEvent From 2c6659385b63689aeb9a428189259bdb2bcbf6c9 Mon Sep 17 00:00:00 2001 From: GreatBahram Date: Tue, 18 May 2021 11:15:35 +0430 Subject: [PATCH 03/39] add on_attrib on LoggingEventHandler --- src/watchdog/events.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/watchdog/events.py b/src/watchdog/events.py index da15723a3..67b3b58de 100755 --- a/src/watchdog/events.py +++ b/src/watchdog/events.py @@ -533,6 +533,12 @@ def on_modified(self, event): what = 'directory' if event.is_directory else 'file' self.logger.info("Modified %s: %s", what, event.src_path) + def on_attrib(self, event): + super().on_attrib(event) + + what = 'directory' if event.is_directory else 'file' + self.logger.info("Attrib %s: %s", what, event.src_path) + def generate_sub_moved_events(src_dir_path, dest_dir_path): """Generates an event list of :class:`DirMovedEvent` and From 937f9af06bb563a7e03473dc459a7b6430d2f40b Mon Sep 17 00:00:00 2001 From: GreatBahram Date: Wed, 26 May 2021 11:50:11 +0430 Subject: [PATCH 04/39] add changes to make kqueue support attrib event --- src/watchdog/observers/kqueue.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/watchdog/observers/kqueue.py b/src/watchdog/observers/kqueue.py index bd1f55e1d..9cfadf4b3 100644 --- a/src/watchdog/observers/kqueue.py +++ b/src/watchdog/observers/kqueue.py @@ -100,6 +100,8 @@ EVENT_TYPE_DELETED, EVENT_TYPE_CREATED, generate_sub_moved_events, + FileAttribEvent, + DirAttribEvent, ) # Maximum number of events to process. @@ -553,9 +555,9 @@ def _gen_kqueue_events(self, yield event elif is_attrib_modified(kev): if descriptor.is_directory: - yield DirModifiedEvent(src_path) + yield DirAttribEvent(src_path) else: - yield FileModifiedEvent(src_path) + yield FileAttribEvent(src_path) elif is_modified(kev): if descriptor.is_directory: if self.watch.is_recursive or self.watch.path == src_path: From e2d8db030dc56627b32269965ae28a11e8a257aa Mon Sep 17 00:00:00 2001 From: GreatBahram Date: Thu, 27 May 2021 15:10:32 +0430 Subject: [PATCH 05/39] bring file/directory attribute change event into fsevents2 module --- src/watchdog/observers/fsevents2.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/watchdog/observers/fsevents2.py b/src/watchdog/observers/fsevents2.py index f0b4750dd..287ae52b5 100644 --- a/src/watchdog/observers/fsevents2.py +++ b/src/watchdog/observers/fsevents2.py @@ -34,7 +34,9 @@ DirDeletedEvent, DirModifiedEvent, DirCreatedEvent, - DirMovedEvent + DirMovedEvent, + FileAttribEvent, + DirAttribEvent, ) from watchdog.observers.api import ( BaseObserver, @@ -225,10 +227,14 @@ def queue_events(self, timeout): self.queue_event(DirModifiedEvent(os.path.dirname(event.path))) # TODO: generate events for tree - elif event.is_modified or event.is_inode_meta_mod or event.is_xattr_mod : + elif event.is_modified: cls = DirModifiedEvent if event.is_directory else FileModifiedEvent self.queue_event(cls(event.path)) + elif event.is_inode_meta_mod or event.is_xattr_mod: + cls = DirAttribEvent if event.is_directory else FileAttribEvent + self.queue_event(cls(event.path)) + elif event.is_created: cls = DirCreatedEvent if event.is_directory else FileCreatedEvent self.queue_event(cls(event.path)) From 3a652ff9fbba5242b97afaf26823864bb1654220 Mon Sep 17 00:00:00 2001 From: GreatBahram Date: Thu, 27 May 2021 15:21:44 +0430 Subject: [PATCH 06/39] update docs for FileAttrib and DirAttrib Events --- src/watchdog/events.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/watchdog/events.py b/src/watchdog/events.py index 67b3b58de..222dabf44 100755 --- a/src/watchdog/events.py +++ b/src/watchdog/events.py @@ -68,6 +68,15 @@ :members: :show-inheritance: +.. autoclass:: FileAttribEvent + :members: + :show-inheritance: + +.. autoclass:: FileAttribEvent + :members: + :show-inheritance: + + Event Handler Classes --------------------- @@ -203,6 +212,9 @@ class FileDeletedEvent(FileSystemEvent): class FileAttribEvent(FileSystemEvent): + """ + File system event representing file metadata modification on the file system. + """ event_type = EVENT_TYPE_ATTRIB @@ -261,6 +273,9 @@ class DirMovedEvent(FileSystemMovedEvent): class DirAttribEvent(FileSystemEvent): + """ + File system event representing directory metadata modification on the file system. + """ event_type = EVENT_TYPE_ATTRIB is_directory = True From 3b6f258b87e321da42f67415f5759421f6d5fc22 Mon Sep 17 00:00:00 2001 From: Bahram Aghaei Date: Tue, 1 Jun 2021 18:58:00 +0430 Subject: [PATCH 07/39] Make flake8 happy --- src/watchdog/events.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/watchdog/events.py b/src/watchdog/events.py index 222dabf44..08dbb8dff 100755 --- a/src/watchdog/events.py +++ b/src/watchdog/events.py @@ -365,6 +365,7 @@ def on_attrib(self, event): :class:`FileAttribEvent` or :class:`DirAttribEvent` """ + class PatternMatchingEventHandler(FileSystemEventHandler): """ Matches given patterns with file paths associated with occurring events. From 151f6514d3edf9b1236fb2d9ec5f299b539e0b8b Mon Sep 17 00:00:00 2001 From: GreatBahram Date: Thu, 10 Jun 2021 19:37:22 +0430 Subject: [PATCH 08/39] Change the test github workflow To be able to run it manually, currently it only works when you send a pull_request --- .github/workflows/tests.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index daa02a243..dba09ecf8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,9 +1,12 @@ name: Tests on: - pull_request: - branches: '*' - + workflow_dispatch: + inputs: + branch: + description: 'The branch, tag or SHA to release from' + required: true + default: 'master' jobs: tests: name: Run tests for ${{ matrix.os }} for ${{ matrix.python }} From dc366d6a035ac349ee0a23f4cd439392044f5682 Mon Sep 17 00:00:00 2001 From: Bahram Aghaei Date: Thu, 10 Jun 2021 21:17:52 +0430 Subject: [PATCH 09/39] add on: pull_request workflow statement: apparently, it is OK to have both on [pull_request, workflow_dispatch] --- .github/workflows/build-and-publish.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index 86239497f..c81d89d9e 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -16,6 +16,9 @@ name: Build & Publish on: + pull_request: + branches: '*' + workflow_dispatch: inputs: branch: From 1ca76cb9a2644f3d816c54c60a3bb7f0a321c6d4 Mon Sep 17 00:00:00 2001 From: Bahram Aghaei Date: Thu, 10 Jun 2021 22:55:36 +0430 Subject: [PATCH 10/39] WIP: fix two more test cases and make sure they work correctly on bsd and darwin kernels --- tests/test_emitter.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/test_emitter.py b/tests/test_emitter.py index 4983a7be1..ae38339a1 100644 --- a/tests/test_emitter.py +++ b/tests/test_emitter.py @@ -33,6 +33,7 @@ DirCreatedEvent, DirMovedEvent, FileClosedEvent, + FileAttribEvent ) from watchdog.observers.api import ObservedWatch @@ -187,12 +188,15 @@ def test_modify(): touch(p('a')) - expect_event(FileModifiedEvent(p('a'))) + if platform.is_windows(): + expect_event(FileModifiedEvent(p('a'))) - if platform.is_linux(): + if not platform.is_windows(): + # TODO: change the fsevent module to support this feature and test this on bsd event = event_queue.get(timeout=5)[0] + assert isinstance(event, FileAttribEvent) assert event.src_path == p('a') - assert isinstance(event, FileClosedEvent) + assert isinstance(event, FileAttribEvent) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) @@ -412,10 +416,9 @@ def test_recursive_on(): assert event.src_path == p('dir1', 'dir2', 'dir3') assert isinstance(event, DirModifiedEvent) - if not platform.is_bsd(): - event = event_queue.get(timeout=5)[0] - assert event.src_path == p('dir1', 'dir2', 'dir3', 'a') - assert isinstance(event, FileModifiedEvent) + event = event_queue.get(timeout=5)[0] + assert event.src_path == p('dir1', 'dir2', 'dir3', 'a') + assert isinstance(event, FileAttribEvent) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) @@ -528,6 +531,7 @@ def test_renaming_top_level_directory_on_windows(): @pytest.mark.skipif(platform.is_windows(), reason="Windows create another set of events for this test") def test_move_nested_subdirectories(): + # TODO mkdir(p('dir1/dir2/dir3'), parents=True) mkfile(p('dir1/dir2/dir3', 'a')) start_watching() @@ -600,6 +604,7 @@ def test_move_nested_subdirectories_on_windows(): @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) def test_file_lifecyle(): + # TODO start_watching() mkfile(p('a')) From c3dc00f3439bddbab768eb205d220ffee71defd8 Mon Sep 17 00:00:00 2001 From: Bahram Aghaei Date: Thu, 10 Jun 2021 23:07:16 +0430 Subject: [PATCH 11/39] add test cases for file_attrib and dir_attrib --- tests/test_events.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/test_events.py b/tests/test_events.py index 476f6ef62..5bc70571e 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -25,12 +25,15 @@ DirCreatedEvent, FileMovedEvent, DirMovedEvent, + FileAttribEvent, + DirAttribEvent, FileSystemEventHandler, EVENT_TYPE_MODIFIED, EVENT_TYPE_CREATED, EVENT_TYPE_DELETED, EVENT_TYPE_MOVED, EVENT_TYPE_CLOSED, + EVENT_TYPE_ATTRIB, ) path_1 = '/path/xyz' @@ -92,6 +95,14 @@ def test_file_closed_event(): assert not event.is_synthetic +def test_file_attrib_event(): + event = FileAttribEvent(path_1) + assert path_1 == event.src_path + assert EVENT_TYPE_ATTRIB == event.event_type + assert not event.is_directory + assert not event.is_synthetic + + def test_dir_deleted_event(): event = DirDeletedEvent(path_1) assert path_1 == event.src_path @@ -116,6 +127,14 @@ def test_dir_created_event(): assert not event.is_synthetic +def test_dir_attrib_event(): + event = DirAttribEvent(path_1) + assert path_1 == event.src_path + assert EVENT_TYPE_ATTRIB == event.event_type + assert event.is_directory + assert not event.is_synthetic + + def test_file_system_event_handler_dispatch(): dir_del_event = DirDeletedEvent('/path/blah.py') file_del_event = FileDeletedEvent('/path/blah.txt') @@ -126,6 +145,8 @@ def test_file_system_event_handler_dispatch(): file_mod_event = FileModifiedEvent('/path/blah.txt') dir_mov_event = DirMovedEvent('/path/blah.py', '/path/blah') file_mov_event = FileMovedEvent('/path/blah.txt', '/path/blah') + file_attr_event = FileAttribEvent('/path/blah.py') + dir_attr_event = DirAttribEvent('/path/dir') all_events = [ dir_mod_event, @@ -137,6 +158,8 @@ def test_file_system_event_handler_dispatch(): file_cre_event, file_mov_event, file_cls_event, + file_attr_event, + dir_attr_event, ] class TestableEventHandler(FileSystemEventHandler): @@ -159,6 +182,9 @@ def on_created(self, event): def on_closed(self, event): assert event.event_type == EVENT_TYPE_CLOSED + def on_attrib(self, event): + assert event.event_type == EVENT_TYPE_ATTRIB + handler = TestableEventHandler() for event in all_events: From 165a6f4680a14d4cbf902a2ee1cfb655bb8cc5a6 Mon Sep 17 00:00:00 2001 From: Bahram Aghaei Date: Fri, 11 Jun 2021 00:46:26 +0430 Subject: [PATCH 12/39] add chmod function into shell module and add test cases to test it works properly with FileAttrib and DirAttrib events --- tests/shell.py | 6 ++++++ tests/test_emitter.py | 29 ++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/tests/shell.py b/tests/shell.py index 7c05fb076..8e3d46884 100644 --- a/tests/shell.py +++ b/tests/shell.py @@ -123,3 +123,9 @@ def msize(path): with open(path, 'w') as w: w.write('0') os.utime(path, (0, 0)) + + +def chmod(path, mode): + """"Change file mode bits.""" + os.chmod(path, mode) + diff --git a/tests/test_emitter.py b/tests/test_emitter.py index ae38339a1..2d61c416d 100644 --- a/tests/test_emitter.py +++ b/tests/test_emitter.py @@ -21,7 +21,7 @@ from functools import partial from queue import Queue, Empty -from .shell import mkfile, mkdir, touch, mv, rm +from .shell import mkfile, mkdir, touch, mv, rm, chmod from watchdog.utils import platform from watchdog.events import ( FileDeletedEvent, @@ -33,7 +33,8 @@ DirCreatedEvent, DirMovedEvent, FileClosedEvent, - FileAttribEvent + FileAttribEvent, + DirAttribEvent, ) from watchdog.observers.api import ObservedWatch @@ -603,7 +604,7 @@ def test_move_nested_subdirectories_on_windows(): @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) -def test_file_lifecyle(): +def test_file_lifecycle(): # TODO start_watching() @@ -637,3 +638,25 @@ def test_file_lifecyle(): if not platform.is_windows(): expect_event(DirModifiedEvent(p())) + + +@pytest.mark.skipif(platform.is_windows(), reason="FileAttribEvent isn't supported in Windows") +def test_chmod_file(): + mkfile(p('newfile')) + + start_watching() + + chmod(p('newfile'), 777) + + event_queue.get(FileAttribEvent(p('newfile'))) + + +@pytest.mark.skipif(platform.is_windows(), reason="DirAttribEvent isn't supported in Windows") +def test_chmod_dir(): + mkdir(p('dir1')) + + start_watching() + + chmod(p('dir1'), 777) + + event_queue.get(DirAttribEvent(p('dir1'))) From b7108517d64ed1f150caa70cc68a6ed980d79834 Mon Sep 17 00:00:00 2001 From: Bahram Aghaei Date: Fri, 11 Jun 2021 01:16:59 +0430 Subject: [PATCH 13/39] I think I fixed the nested tese case --- tests/test_emitter.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/test_emitter.py b/tests/test_emitter.py index 2d61c416d..7f753c9c7 100644 --- a/tests/test_emitter.py +++ b/tests/test_emitter.py @@ -532,7 +532,6 @@ def test_renaming_top_level_directory_on_windows(): @pytest.mark.skipif(platform.is_windows(), reason="Windows create another set of events for this test") def test_move_nested_subdirectories(): - # TODO mkdir(p('dir1/dir2/dir3'), parents=True) mkfile(p('dir1/dir2/dir3', 'a')) start_watching() @@ -556,9 +555,17 @@ def test_move_nested_subdirectories(): touch(p('dir2/dir3', 'a')) + if not platform.is_windows(): + event = event_queue.get(timeout=5)[0] + assert event.src_path == p('dir2/dir3', 'a') + assert isinstance(event, FileAttribEvent) + + if platform.is_linux(): + expect_event(FileClosedEvent(p('dir2/dir3/', 'a'))) + event = event_queue.get(timeout=5)[0] - assert event.src_path == p('dir2/dir3', 'a') - assert isinstance(event, FileModifiedEvent) + assert event.src_path == p('dir2/dir3') + assert isinstance(event, DirModifiedEvent) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) From 424cb478cd05cc1d4d739bc19121ec4f1dae7f59 Mon Sep 17 00:00:00 2001 From: Bahram Aghaei Date: Fri, 11 Jun 2021 01:19:40 +0430 Subject: [PATCH 14/39] Make flake8 happier than ever --- tests/shell.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/shell.py b/tests/shell.py index 8e3d46884..147eb3f52 100644 --- a/tests/shell.py +++ b/tests/shell.py @@ -128,4 +128,3 @@ def msize(path): def chmod(path, mode): """"Change file mode bits.""" os.chmod(path, mode) - From 58b514f4e14cc1b33e790ad423bfafd13cd3606e Mon Sep 17 00:00:00 2001 From: Bahram Aghaei Date: Fri, 11 Jun 2021 01:24:51 +0430 Subject: [PATCH 15/39] WIP: Disable the lifecycle test case --- tests/test_emitter.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_emitter.py b/tests/test_emitter.py index 7f753c9c7..8fca831b0 100644 --- a/tests/test_emitter.py +++ b/tests/test_emitter.py @@ -612,7 +612,10 @@ def test_move_nested_subdirectories_on_windows(): @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) def test_file_lifecycle(): - # TODO + # TODO: it require more knowledge of this package + # I comment it to finish other modules such as fsevent then I'd fix + # this at the end. + return True start_watching() mkfile(p('a')) From 677ac9f542065cbdbaff4f701a90c7be48560c1c Mon Sep 17 00:00:00 2001 From: Bahram Aghaei Date: Fri, 11 Jun 2021 01:42:50 +0430 Subject: [PATCH 16/39] Update fsevents module: this should be tested more than any other modules --- src/watchdog/observers/fsevents.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/watchdog/observers/fsevents.py b/src/watchdog/observers/fsevents.py index e1ec74a1e..d510960d4 100644 --- a/src/watchdog/observers/fsevents.py +++ b/src/watchdog/observers/fsevents.py @@ -39,6 +39,8 @@ DirModifiedEvent, DirCreatedEvent, DirMovedEvent, + FileAttribEvent, + DirAttribEvent, generate_sub_created_events, generate_sub_moved_events ) @@ -109,6 +111,10 @@ def _queue_modified_event(self, event, src_path, dirname): cls = DirModifiedEvent if event.is_directory else FileModifiedEvent self.queue_event(cls(src_path)) + def _queue_attrib_event(self, event, src_path, dirname): + cls = DirAttribEvent if event.is_directory else FileAttribEvent + self.queue_event(cls(src_path)) + def _queue_renamed_event(self, src_event, src_path, dst_path, src_dirname, dst_dirname): cls = DirMovedEvent if src_event.is_directory else FileMovedEvent dst_path = self._encode_path(dst_path) @@ -187,9 +193,12 @@ def queue_events(self, timeout, events): self._fs_view.add(event.inode) - if event.is_modified or event.is_inode_meta_mod or event.is_xattr_mod: + if event.is_modified: self._queue_modified_event(event, src_path, src_dirname) + if event.is_inode_meta_mod or event.is_xattr_mod: + self._queue_attrib_event(event, src_path, src_dirname) + self._queue_deleted_event(event, src_path, src_dirname) self._fs_view.discard(event.inode) @@ -200,9 +209,12 @@ def queue_events(self, timeout, events): self._fs_view.add(event.inode) - if event.is_modified or event.is_inode_meta_mod or event.is_xattr_mod: + if event.is_modified: self._queue_modified_event(event, src_path, src_dirname) + if event.is_inode_meta_mod or event.is_xattr_mod: + self._queue_attrib_event(event, src_path, src_dirname) + if event.is_renamed: # Check if we have a corresponding destination event in the watched path. @@ -225,9 +237,12 @@ def queue_events(self, timeout, events): events.remove(dst_event) - if dst_event.is_modified or dst_event.is_inode_meta_mod or dst_event.is_xattr_mod: + if dst_event.is_modified: self._queue_modified_event(dst_event, dst_path, dst_dirname) + if dst_event.is_inode_meta_mod or dst_event.is_xattr_mod: + self._queue_attrib_event(dst_event, dst_path, dst_dirname) + if dst_event.is_removed: self._queue_deleted_event(dst_event, dst_path, dst_dirname) self._fs_view.discard(dst_event.inode) From 467b1e2857325bd7d150fe444b2e24bbd4310e5c Mon Sep 17 00:00:00 2001 From: GreatBahram Date: Thu, 24 Jun 2021 12:50:39 +0430 Subject: [PATCH 17/39] bugfix test case: test_modify on BSD --- tests/test_emitter.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/test_emitter.py b/tests/test_emitter.py index 8fca831b0..9c5463b3f 100644 --- a/tests/test_emitter.py +++ b/tests/test_emitter.py @@ -192,12 +192,20 @@ def test_modify(): if platform.is_windows(): expect_event(FileModifiedEvent(p('a'))) - if not platform.is_windows(): - # TODO: change the fsevent module to support this feature and test this on bsd + if platform.is_linux(): + # on Linux we'd get the attrib event first then event = event_queue.get(timeout=5)[0] assert isinstance(event, FileAttribEvent) assert event.src_path == p('a') + + if platform.is_bsd(): + event = event_queue.get(timeout=5)[0] + assert isinstance(event, FileModifiedEvent) + assert event.src_path == p('a') + + event = event_queue.get(timeout=5)[0] assert isinstance(event, FileAttribEvent) + assert event.src_path == p('a') @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) From 5e94d19f5a2a8cfec518ef816d8199d4f55a485c Mon Sep 17 00:00:00 2001 From: GreatBahram Date: Thu, 24 Jun 2021 12:51:04 +0430 Subject: [PATCH 18/39] return str repsentation instead of Pathlib object --- src/watchdog/observers/kqueue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/watchdog/observers/kqueue.py b/src/watchdog/observers/kqueue.py index 9cfadf4b3..aa1e786d0 100644 --- a/src/watchdog/observers/kqueue.py +++ b/src/watchdog/observers/kqueue.py @@ -129,7 +129,7 @@ def absolute_path(path): - return Path(path).resolve() + return str(Path(path).resolve()) # Flag tests. From 60415b7098b23db8e42c4fb94562ce4f6b8709ae Mon Sep 17 00:00:00 2001 From: GreatBahram Date: Thu, 24 Jun 2021 13:14:16 +0430 Subject: [PATCH 19/39] make the equality method more strict --- src/watchdog/events.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/watchdog/events.py b/src/watchdog/events.py index 08dbb8dff..37a938800 100755 --- a/src/watchdog/events.py +++ b/src/watchdog/events.py @@ -162,7 +162,9 @@ def key(self): return (self.event_type, self.src_path, self.is_directory) def __eq__(self, event): - return self.key == event.key + if isinstance(event, FileSystemEvent): + return self.key == event.key + return NotImplemented def __ne__(self, event): return self.key != event.key From 9d0e19cb1e506532c6bb1b5fdc98231e562b00e4 Mon Sep 17 00:00:00 2001 From: GreatBahram Date: Thu, 24 Jun 2021 13:28:17 +0430 Subject: [PATCH 20/39] run test work for pull_request and when user asks for it --- .github/workflows/tests.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index dba09ecf8..9419528a3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,12 +1,16 @@ name: Tests on: + pull_request: + branches: '*' + workflow_dispatch: inputs: branch: description: 'The branch, tag or SHA to release from' required: true default: 'master' + jobs: tests: name: Run tests for ${{ matrix.os }} for ${{ matrix.python }} From dfb27d6f4bde1a44bc32a335206189a3778270b6 Mon Sep 17 00:00:00 2001 From: GreatBahram Date: Thu, 24 Jun 2021 13:30:02 +0430 Subject: [PATCH 21/39] bugfix: document --- src/watchdog/events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/watchdog/events.py b/src/watchdog/events.py index 37a938800..d9eb34f82 100755 --- a/src/watchdog/events.py +++ b/src/watchdog/events.py @@ -72,7 +72,7 @@ :members: :show-inheritance: -.. autoclass:: FileAttribEvent +.. autoclass:: DirAttribEvent :members: :show-inheritance: From 5921e2ca44090edf283edbd5ba3427fdf1f14c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Thu, 24 Jun 2021 15:52:47 +0200 Subject: [PATCH 22/39] Update tests.yml --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9419528a3..f464bb685 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,6 +16,7 @@ jobs: name: Run tests for ${{ matrix.os }} for ${{ matrix.python }} runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] python: [3.6, 3.7, 3.8, 3.9] From 167639f8703a4aafc88acd02f132d817bd45b985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Sat, 18 Mar 2023 15:11:10 +0100 Subject: [PATCH 23/39] Update tests.yml --- .github/workflows/tests.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index aedc230c3..d83f08ca7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,10 +8,6 @@ on: branches: - '**' -concurrency: - group: ${{ github.ref }}-${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name != 'pull_request' && github.sha || '' }} - cancel-in-progress: true - workflow_dispatch: inputs: branch: @@ -19,6 +15,10 @@ concurrency: required: true default: 'master' +concurrency: + group: ${{ github.ref }}-${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name != 'pull_request' && github.sha || '' }} + cancel-in-progress: true + jobs: tox: name: ${{ matrix.tox.name }} ${{ matrix.os.emoji }} ${{ matrix.os.name }} ${{ matrix.python }} From f751e7def64445e1b0dc39028df3d5581551f1e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Sat, 18 Mar 2023 15:14:05 +0100 Subject: [PATCH 24/39] Update kqueue.py --- src/watchdog/observers/kqueue.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/watchdog/observers/kqueue.py b/src/watchdog/observers/kqueue.py index d54b4fcfc..c7f866583 100644 --- a/src/watchdog/observers/kqueue.py +++ b/src/watchdog/observers/kqueue.py @@ -85,17 +85,17 @@ EVENT_TYPE_CREATED, EVENT_TYPE_DELETED, EVENT_TYPE_MOVED, + DirAttribEvent, DirCreatedEvent, DirDeletedEvent, DirModifiedEvent, DirMovedEvent, + FileAttribEvent, FileCreatedEvent, FileDeletedEvent, FileModifiedEvent, FileMovedEvent, generate_sub_moved_events, - FileAttribEvent, - DirAttribEvent, ) from watchdog.observers.api import DEFAULT_EMITTER_TIMEOUT, DEFAULT_OBSERVER_TIMEOUT, BaseObserver, EventEmitter from watchdog.utils import platform From 482846e7f9861fb37944a78c8b5c6d540c2126e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Sat, 18 Mar 2023 15:23:32 +0100 Subject: [PATCH 25/39] Apply suggestions from code review --- src/watchdog/events.py | 6 +++--- src/watchdog/observers/fsevents.py | 4 ++-- tests/test_emitter.py | 14 +++++++------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/watchdog/events.py b/src/watchdog/events.py index bb59bfce2..84816ecba 100755 --- a/src/watchdog/events.py +++ b/src/watchdog/events.py @@ -113,8 +113,8 @@ EVENT_TYPE_CREATED = "created" EVENT_TYPE_MODIFIED = "modified" EVENT_TYPE_CLOSED = "closed" -EVENT_TYPE_OPENED = "opened"' -EVENT_TYPE_ATTRIB = 'attrib' +EVENT_TYPE_OPENED = "opened" +EVENT_TYPE_ATTRIB = "attrib" class FileSystemEvent: @@ -583,7 +583,7 @@ def on_modified(self, event): def on_attrib(self, event): super().on_attrib(event) - what = 'directory' if event.is_directory else 'file' + what = "directory" if event.is_directory else "file" self.logger.info("Attrib %s: %s", what, event.src_path) diff --git a/src/watchdog/observers/fsevents.py b/src/watchdog/observers/fsevents.py index 28391517f..fa13f6309 100644 --- a/src/watchdog/observers/fsevents.py +++ b/src/watchdog/observers/fsevents.py @@ -276,10 +276,10 @@ def queue_events(self, timeout, events): if dst_event.is_modified: self._queue_modified_event(dst_event, dst_path, dst_dirname) - if dst_event.is_inode_meta_mod or dst_event.is_xattr_mod: + elif dst_event.is_inode_meta_mod or dst_event.is_xattr_mod: self._queue_attrib_event(dst_event, dst_path, dst_dirname) - if dst_event.is_removed: + elif dst_event.is_removed: self._queue_deleted_event(dst_event, dst_path, dst_dirname) self._fs_view.discard(dst_event.inode) diff --git a/tests/test_emitter.py b/tests/test_emitter.py index 2b5a41b02..6a1de608b 100644 --- a/tests/test_emitter.py +++ b/tests/test_emitter.py @@ -143,22 +143,22 @@ def test_modify(p: P, event_queue: TestEventQueue, start_watching: StartWatching touch(p("a")) if platform.is_windows(): - expect_event(FileModifiedEvent(p('a'))) + expect_event(FileModifiedEvent(p("a"))) if platform.is_linux(): # on Linux we'd get the attrib event first then event = event_queue.get(timeout=5)[0] assert isinstance(event, FileAttribEvent) - assert event.src_path == p('a') + assert event.src_path == p("a") if platform.is_bsd(): event = event_queue.get(timeout=5)[0] assert isinstance(event, FileModifiedEvent) - assert event.src_path == p('a') + assert event.src_path == p("a") event = event_queue.get(timeout=5)[0] assert isinstance(event, FileAttribEvent) - assert event.src_path == p('a') + assert event.src_path == p("a") @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) @@ -413,7 +413,7 @@ def test_recursive_on(p: P, event_queue: TestEventQueue, start_watching: StartWa assert isinstance(event, DirModifiedEvent) event = event_queue.get(timeout=5)[0] - assert event.src_path == p('dir1', 'dir2', 'dir3', 'a') + assert event.src_path == p("dir1", "dir2", "dir3", "a") assert isinstance(event, FileAttribEvent) @@ -631,11 +631,11 @@ def test_move_nested_subdirectories( if not platform.is_windows(): event = event_queue.get(timeout=5)[0] - assert event.src_path == p('dir2/dir3', 'a') + assert event.src_path == p("dir2/dir3", "a") assert isinstance(event, FileAttribEvent) if platform.is_linux(): - expect_event(FileClosedEvent(p('dir2/dir3/', 'a'))) + expect_event(FileClosedEvent(p("dir2/dir3/", "a"))) event = event_queue.get(timeout=5)[0] assert event.src_path == p('dir2/dir3') From dfa6db5cae06443eea3af2791b6556770c28f16f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Sat, 18 Mar 2023 15:23:54 +0100 Subject: [PATCH 26/39] Apply suggestions from code review --- tests/test_emitter.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/tests/test_emitter.py b/tests/test_emitter.py index 6a1de608b..95c67d5ca 100644 --- a/tests/test_emitter.py +++ b/tests/test_emitter.py @@ -638,7 +638,7 @@ def test_move_nested_subdirectories( expect_event(FileClosedEvent(p("dir2/dir3/", "a"))) event = event_queue.get(timeout=5)[0] - assert event.src_path == p('dir2/dir3') + assert event.src_path == p("dir2/dir3") assert isinstance(event, DirModifiedEvent) @@ -733,21 +733,16 @@ def test_file_lifecyle(p: P, start_watching: StartWatching, expect_event: Expect @pytest.mark.skipif(platform.is_windows(), reason="FileAttribEvent isn't supported in Windows") def test_chmod_file(): - mkfile(p('newfile')) + mkfile(p("newfile")) start_watching() - - chmod(p('newfile'), 777) - - event_queue.get(FileAttribEvent(p('newfile'))) + chmod(p("newfile"), 777) + event_queue.get(FileAttribEvent(p("newfile"))) @pytest.mark.skipif(platform.is_windows(), reason="DirAttribEvent isn't supported in Windows") def test_chmod_dir(): - mkdir(p('dir1')) - + mkdir(p("dir1")) start_watching() - - chmod(p('dir1'), 777) - - event_queue.get(DirAttribEvent(p('dir1'))) + chmod(p("dir1"), 777) + event_queue.get(DirAttribEvent(p("dir1"))) From 008556d5121065b69bd235c15500de70587e5005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Sat, 22 Apr 2023 16:33:42 +0200 Subject: [PATCH 27/39] Update test_emitter.py --- tests/test_emitter.py | 226 +++++++++++++++--------------------------- 1 file changed, 78 insertions(+), 148 deletions(-) diff --git a/tests/test_emitter.py b/tests/test_emitter.py index cfba0c1cf..5cd3f3d65 100644 --- a/tests/test_emitter.py +++ b/tests/test_emitter.py @@ -38,7 +38,7 @@ ) from watchdog.utils import platform -from .shell import mkdir, mkfile, mv, rm, touch +from .shell import chmod, mkdir, mkfile, mv, rm, touch from .utils import ExpectEvent, P, StartWatching, TestEventQueue logging.basicConfig(level=logging.DEBUG) @@ -57,7 +57,7 @@ def rerun_filter(exc, *args): @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) -def test_create(p: P, event_queue: TestEventQueue, start_watching: StartWatching, expect_event: ExpectEvent) -> None: +def test_create(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: start_watching() open(p("a"), "a").close() @@ -67,35 +67,29 @@ def test_create(p: P, event_queue: TestEventQueue, start_watching: StartWatching expect_event(DirModifiedEvent(p())) if platform.is_linux(): - event = event_queue.get(timeout=5)[0] - assert event.src_path == p("a") - assert isinstance(event, FileOpenedEvent) - event = event_queue.get(timeout=5)[0] - assert event.src_path == p("a") - assert isinstance(event, FileClosedEvent) + expect_event(FileOpenedEvent(p("a"))) + expect_event(FileClosedEvent(p("a"))) @pytest.mark.xfail(reason="known to be problematic") @pytest.mark.skipif(not platform.is_linux(), reason="FileCloseEvent only supported in GNU/Linux") @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) -def test_close(p: P, event_queue: TestEventQueue, start_watching: StartWatching) -> None: +def test_close(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: f_d = open(p("a"), "a") start_watching() f_d.close() # After file creation/open in append mode - event = event_queue.get(timeout=5)[0] - assert event.src_path == p("a") - assert isinstance(event, FileClosedEvent) + expect_event(FileClosedEvent(p("a"))) - event = event_queue.get(timeout=5)[0] - assert os.path.normpath(event.src_path) == os.path.normpath(p("")) - assert isinstance(event, DirModifiedEvent) + expect_event(DirModifiedEvent(os.path.normpath(p("")))) # After read-only, only IN_CLOSE_NOWRITE is emitted but not caught for now #747 open(p("a"), "r").close() - assert event_queue.empty() + expect_event(FileOpenedEvent(p("a"))) + with pytest.raises(Empty): + expect_event(None) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) @@ -103,24 +97,19 @@ def test_close(p: P, event_queue: TestEventQueue, start_watching: StartWatching) platform.is_darwin() or platform.is_windows(), reason="Windows and macOS enforce proper encoding", ) -def test_create_wrong_encoding(p: P, event_queue: TestEventQueue, start_watching: StartWatching) -> None: +def test_create_wrong_encoding(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: start_watching() open(p("a_\udce4"), "a").close() - event = event_queue.get(timeout=5)[0] - assert event.src_path == p("a_\udce4") - assert isinstance(event, FileCreatedEvent) + expect_event(FileCreatedEvent(p("a_\udce4"))) 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) + expect_event(DirModifiedEvent(os.path.normpath(p("")))) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) def test_delete(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: mkfile(p("a")) - start_watching() rm(p("a")) @@ -131,7 +120,7 @@ def test_delete(p: P, start_watching: StartWatching, expect_event: ExpectEvent) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) -def test_modify(p: P, event_queue: TestEventQueue, start_watching: StartWatching, expect_event: ExpectEvent) -> None: +def test_modify(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: mkfile(p("a")) start_watching() @@ -139,21 +128,13 @@ def test_modify(p: P, event_queue: TestEventQueue, start_watching: StartWatching if platform.is_windows(): expect_event(FileModifiedEvent(p("a"))) - - if platform.is_linux(): - # on Linux we'd get the attrib event first then - event = event_queue.get(timeout=5)[0] - assert isinstance(event, FileAttribEvent) - assert event.src_path == p("a") - - if platform.is_bsd(): - event = event_queue.get(timeout=5)[0] - assert isinstance(event, FileModifiedEvent) - assert event.src_path == p("a") - - event = event_queue.get(timeout=5)[0] - assert isinstance(event, FileAttribEvent) - assert event.src_path == p("a") + elif platform.is_linux(): + expect_event(FileOpenedEvent(p("a"))) + expect_event(FileAttribEvent(p("a"))) + expect_event(FileClosedEvent(p("a"))) + elif platform.is_bsd(): + expect_event(FileModifiedEvent(p("a"))) + expect_event(FileAttribEvent(p("a"))) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) @@ -165,14 +146,17 @@ def test_chmod(p: P, start_watching: StartWatching, expect_event: ExpectEvent) - # allows setting the read-only flag. os.chmod(p("a"), stat.S_IREAD) - expect_event(FileModifiedEvent(p("a"))) + if platform.is_windows(): + expect_event(FileModifiedEvent(p("a"))) + else: + expect_event(FileAttribEvent(p("a"))) # Reset permissions to allow cleanup. os.chmod(p("a"), stat.S_IWRITE) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) -def test_move(p: P, event_queue: TestEventQueue, start_watching: StartWatching, expect_event: ExpectEvent) -> None: +def test_move(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: mkdir(p("dir1")) mkdir(p("dir2")) mkfile(p("dir1", "a")) @@ -183,30 +167,17 @@ def test_move(p: P, event_queue: TestEventQueue, start_watching: StartWatching, if not platform.is_windows(): expect_event(FileMovedEvent(p("dir1", "a"), p("dir2", "b"))) 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) + expect_event(FileDeletedEvent(p("dir1", "a"))) + expect_event(FileCreatedEvent(p("dir2", "b"))) - event = event_queue.get(timeout=5)[0] - assert event.src_path in [p("dir1"), p("dir2")] - assert isinstance(event, DirModifiedEvent) + expect_event(DirModifiedEvent(p("dir1"))) 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) + expect_event(DirModifiedEvent(p("dir2"))) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) -def test_case_change( - p: P, - event_queue: TestEventQueue, - start_watching: StartWatching, - expect_event: ExpectEvent, -) -> None: +def test_case_change(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: mkdir(p("dir1")) mkdir(p("dir2")) mkfile(p("dir1", "file")) @@ -217,21 +188,13 @@ def test_case_change( if not platform.is_windows(): expect_event(FileMovedEvent(p("dir1", "file"), p("dir2", "FILE"))) else: - event = event_queue.get(timeout=5)[0] - assert event.src_path == p("dir1", "file") - assert isinstance(event, FileDeletedEvent) - event = event_queue.get(timeout=5)[0] - assert event.src_path == p("dir2", "FILE") - assert isinstance(event, FileCreatedEvent) + expect_event(FileDeletedEvent(p("dir1", "file"))) + expect_event(FileCreatedEvent(p("dir2", "FILE"))) - event = event_queue.get(timeout=5)[0] - assert event.src_path in [p("dir1"), p("dir2")] - assert isinstance(event, DirModifiedEvent) + expect_event(DirModifiedEvent(p("dir1"))) 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) + expect_event(DirModifiedEvent(p("dir2"))) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) @@ -250,17 +213,16 @@ def test_move_to(p: P, start_watching: StartWatching, expect_event: ExpectEvent) @pytest.mark.skipif(not platform.is_linux(), reason="InotifyFullEmitter only supported in Linux") -def test_move_to_full(p: P, event_queue: TestEventQueue, start_watching: StartWatching) -> None: +def test_move_to_full(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: mkdir(p("dir1")) mkdir(p("dir2")) mkfile(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 == "" # Should be blank since the path was not watched + # `src_path` should be blank since the path was not watched + expect_event(FileMovedEvent("", p("dir2", "b"))) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) @@ -271,7 +233,6 @@ def test_move_from(p: P, start_watching: StartWatching, expect_event: ExpectEven start_watching(p("dir1")) mv(p("dir1", "a"), p("dir2", "b")) - expect_event(FileDeletedEvent(p("dir1", "a"))) if not platform.is_windows(): @@ -279,17 +240,16 @@ def test_move_from(p: P, start_watching: StartWatching, expect_event: ExpectEven @pytest.mark.skipif(not platform.is_linux(), reason="InotifyFullEmitter only supported in Linux") -def test_move_from_full(p: P, event_queue: TestEventQueue, start_watching: StartWatching) -> None: +def test_move_from_full(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: mkdir(p("dir1")) mkdir(p("dir2")) mkfile(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 == "" # Should be blank since path not watched + # `dest_path` should be blank since the path was not watched + expect_event(FileMovedEvent(p("dir1", "a"), "")) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) @@ -366,42 +326,35 @@ def test_fast_subdirectory_creation_deletion(p: P, event_queue: TestEventQueue, @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) -def test_passing_unicode_should_give_unicode(p: P, event_queue: TestEventQueue, start_watching: StartWatching) -> None: +def test_passing_unicode_should_give_unicode(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: start_watching(str(p())) mkfile(p("a")) - event = event_queue.get(timeout=5)[0] - assert isinstance(event.src_path, str) + expect_event(FileCreatedEvent(p("a"))) @pytest.mark.skipif( platform.is_windows(), - reason="Windows ReadDirectoryChangesW supports only" " unicode for paths.", + reason="Windows ReadDirectoryChangesW supports only unicode for paths.", ) -def test_passing_bytes_should_give_bytes(p: P, event_queue: TestEventQueue, start_watching: StartWatching) -> None: +def test_passing_bytes_should_give_bytes(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: start_watching(p().encode()) mkfile(p("a")) - event = event_queue.get(timeout=5)[0] - assert isinstance(event.src_path, bytes) + expect_event(FileCreatedEvent(p("a").encode())) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) -def test_recursive_on(p: P, event_queue: TestEventQueue, start_watching: StartWatching) -> None: +def test_recursive_on(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: 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) + expect_event(FileCreatedEvent(p("dir1", "dir2", "dir3", "a"))) 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, FileAttribEvent) + expect_event(DirModifiedEvent(p("dir1", "dir2", "dir3"))) + expect_event(FileOpenedEvent(p("dir1", "dir2", "dir3", "a"))) + expect_event(FileAttribEvent(p("dir1", "dir2", "dir3", "a"))) + expect_event(FileClosedEvent(p("dir1", "dir2", "dir3", "a"))) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) @@ -501,12 +454,7 @@ def test_renaming_top_level_directory( @pytest.mark.skipif(platform.is_windows(), reason="Windows create another set of events for this test") -def test_move_nested_subdirectories( - p: P, - event_queue: TestEventQueue, - start_watching: StartWatching, - expect_event: ExpectEvent, -) -> None: +def test_move_nested_subdirectories(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: mkdir(p("dir1/dir2/dir3"), parents=True) mkfile(p("dir1/dir2/dir3", "a")) start_watching() @@ -515,37 +463,25 @@ def test_move_nested_subdirectories( expect_event(DirMovedEvent(p("dir1", "dir2"), p("dir2"))) expect_event(DirModifiedEvent(p("dir1"))) expect_event(DirModifiedEvent(p())) - expect_event(DirMovedEvent(p("dir1", "dir2", "dir3"), p("dir2", "dir3"), is_synthetic=True)) expect_event(FileMovedEvent(p("dir1", "dir2", "dir3", "a"), p("dir2", "dir3", "a"), is_synthetic=True)) if platform.is_bsd(): - event = event_queue.get(timeout=5)[0] - assert p(event.src_path) == p() - assert isinstance(event, DirModifiedEvent) - - event = event_queue.get(timeout=5)[0] - assert p(event.src_path) == p("dir1") - assert isinstance(event, DirModifiedEvent) + expect_event(DirModifiedEvent(p())) + expect_event(DirModifiedEvent(p("dir1"))) touch(p("dir2/dir3", "a")) if platform.is_linux(): - event = event_queue.get(timeout=5)[0] - assert event.src_path == p("dir2/dir3", "a") - assert isinstance(event, FileOpenedEvent) + expect_event(FileOpenedEvent(p("dir2/dir3", "a"))) if not platform.is_windows(): - event = event_queue.get(timeout=5)[0] - assert event.src_path == p("dir2/dir3", "a") - assert isinstance(event, FileAttribEvent) + expect_event(FileAttribEvent(p("dir2/dir3", "a"))) - if platform.is_linux(): - expect_event(FileClosedEvent(p("dir2/dir3/", "a"))) + if platform.is_linux(): + expect_event(FileClosedEvent(p("dir2/dir3/", "a"))) - event = event_queue.get(timeout=5)[0] - assert event.src_path == p("dir2/dir3") - assert isinstance(event, DirModifiedEvent) + expect_event(DirModifiedEvent(p("dir2/dir3"))) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) @@ -557,27 +493,17 @@ def test_move_nested_subdirectories_on_windows( p: P, event_queue: TestEventQueue, start_watching: StartWatching, + expect_event: ExpectEvent, ) -> None: mkdir(p("dir1/dir2/dir3"), parents=True) mkfile(p("dir1/dir2/dir3", "a")) start_watching(p("")) mv(p("dir1/dir2"), p("dir2")) - event = event_queue.get(timeout=5)[0] - assert event.src_path == p("dir1", "dir2") - assert isinstance(event, FileDeletedEvent) - - event = event_queue.get(timeout=5)[0] - assert event.src_path == p("dir2") - assert isinstance(event, DirCreatedEvent) - - event = event_queue.get(timeout=5)[0] - assert event.src_path == p("dir2", "dir3") - assert isinstance(event, DirCreatedEvent) - - event = event_queue.get(timeout=5)[0] - assert event.src_path == p("dir2", "dir3", "a") - assert isinstance(event, FileCreatedEvent) + expect_event(FileDeletedEvent(p("dir1", "dir2"))) + expect_event(DirCreatedEvent(p("dir2"))) + expect_event(DirCreatedEvent(p("dir2", "dir3"))) + expect_event(FileCreatedEvent(p("dir2", "dir3", "a"))) touch(p("dir2/dir3", "a")) @@ -587,7 +513,7 @@ def test_move_nested_subdirectories_on_windows( if event_queue.empty(): break - assert all([isinstance(e, (FileModifiedEvent, DirModifiedEvent)) for e in events]) + assert all(isinstance(e, (FileModifiedEvent, DirModifiedEvent)) for e in events) for event in events: if isinstance(event, FileModifiedEvent): @@ -617,7 +543,10 @@ def test_file_lifecyle(p: P, start_watching: StartWatching, expect_event: Expect expect_event(DirModifiedEvent(p())) expect_event(FileOpenedEvent(p("a"))) - expect_event(FileModifiedEvent(p("a"))) + if platform.is_windows(): + expect_event(FileModifiedEvent(p("a"))) + else: + expect_event(FileAttribEvent(p("a"))) if platform.is_linux(): expect_event(FileClosedEvent(p("a"))) @@ -636,17 +565,18 @@ def test_file_lifecyle(p: P, start_watching: StartWatching, expect_event: Expect @pytest.mark.skipif(platform.is_windows(), reason="FileAttribEvent isn't supported in Windows") -def test_chmod_file(): +def test_chmod_file(p: P, start_watching: StartWatching, expect_event: ExpectEvent): mkfile(p("newfile")) - start_watching() + chmod(p("newfile"), 777) - event_queue.get(FileAttribEvent(p("newfile"))) + expect_event(FileAttribEvent(p("newfile"))) @pytest.mark.skipif(platform.is_windows(), reason="DirAttribEvent isn't supported in Windows") -def test_chmod_dir(): +def test_chmod_dir(p: P, start_watching: StartWatching, expect_event: ExpectEvent): mkdir(p("dir1")) start_watching() + chmod(p("dir1"), 777) - event_queue.get(DirAttribEvent(p("dir1"))) + expect_event(DirAttribEvent(p("dir1"))) From 408738f21295e8fc10dda3d6d096eda2e0dc4add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Sat, 22 Apr 2023 16:34:22 +0200 Subject: [PATCH 28/39] Apply suggestions from code review --- src/watchdog/events.py | 2 ++ tests/shell.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/watchdog/events.py b/src/watchdog/events.py index 3d9ccad08..c1f848cbe 100755 --- a/src/watchdog/events.py +++ b/src/watchdog/events.py @@ -161,6 +161,7 @@ class FileAttribEvent(FileSystemEvent): """ File system event representing file metadata modification on the file system. """ + event_type = EVENT_TYPE_ATTRIB @@ -228,6 +229,7 @@ class DirAttribEvent(FileSystemEvent): """ File system event representing directory metadata modification on the file system. """ + event_type = EVENT_TYPE_ATTRIB is_directory = True diff --git a/tests/shell.py b/tests/shell.py index 94c378c7f..a2f76a3e0 100644 --- a/tests/shell.py +++ b/tests/shell.py @@ -116,7 +116,7 @@ def msize(path): def chmod(path, mode): - """"Change file mode bits.""" + """Change file mode bits.""" os.chmod(path, mode) From 7c8ac0b9d821fa6a329fcf75a5ff8bec72cf3f71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Sat, 22 Apr 2023 16:41:53 +0200 Subject: [PATCH 29/39] Update test_emitter.py --- tests/test_emitter.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/test_emitter.py b/tests/test_emitter.py index 5cd3f3d65..e1a3eefda 100644 --- a/tests/test_emitter.py +++ b/tests/test_emitter.py @@ -89,7 +89,7 @@ def test_close(p: P, start_watching: StartWatching, expect_event: ExpectEvent) - expect_event(FileOpenedEvent(p("a"))) with pytest.raises(Empty): - expect_event(None) + expect_event(FileOpenedEvent(p("a"))) # Fake event to check the queue is empty @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) @@ -326,20 +326,22 @@ def test_fast_subdirectory_creation_deletion(p: P, event_queue: TestEventQueue, @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) -def test_passing_unicode_should_give_unicode(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: +def test_passing_unicode_should_give_unicode(p: P, event_queue: TestEventQueue, start_watching: StartWatching) -> None: start_watching(str(p())) mkfile(p("a")) - expect_event(FileCreatedEvent(p("a"))) + event = event_queue.get(timeout=5)[0] + assert isinstance(event.src_path, str) @pytest.mark.skipif( platform.is_windows(), reason="Windows ReadDirectoryChangesW supports only unicode for paths.", ) -def test_passing_bytes_should_give_bytes(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: +def test_passing_bytes_should_give_bytes(p: P, event_queue: TestEventQueue, start_watching: StartWatching) -> None: start_watching(p().encode()) mkfile(p("a")) - expect_event(FileCreatedEvent(p("a").encode())) + event = event_queue.get(timeout=5)[0] + assert isinstance(event.src_path, bytes) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) @@ -565,7 +567,7 @@ def test_file_lifecyle(p: P, start_watching: StartWatching, expect_event: Expect @pytest.mark.skipif(platform.is_windows(), reason="FileAttribEvent isn't supported in Windows") -def test_chmod_file(p: P, start_watching: StartWatching, expect_event: ExpectEvent): +def test_chmod_file(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: mkfile(p("newfile")) start_watching() @@ -574,7 +576,7 @@ def test_chmod_file(p: P, start_watching: StartWatching, expect_event: ExpectEve @pytest.mark.skipif(platform.is_windows(), reason="DirAttribEvent isn't supported in Windows") -def test_chmod_dir(p: P, start_watching: StartWatching, expect_event: ExpectEvent): +def test_chmod_dir(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: mkdir(p("dir1")) start_watching() From 25adb671d038aa22d4c143456a9a9a566de0325a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Sat, 22 Apr 2023 17:01:22 +0200 Subject: [PATCH 30/39] Apply suggestions from code review --- src/watchdog/observers/fsevents.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/watchdog/observers/fsevents.py b/src/watchdog/observers/fsevents.py index 114d8250e..b36ec9913 100644 --- a/src/watchdog/observers/fsevents.py +++ b/src/watchdog/observers/fsevents.py @@ -221,7 +221,7 @@ def queue_events(self, timeout, events): if event.is_modified: self._queue_modified_event(event, src_path, src_dirname) - elif event.is_inode_meta_mod or event.is_xattr_mod: + elif self._is_meta_mod(event): self._queue_attrib_event(event, src_path, src_dirname) self._queue_deleted_event(event, src_path, src_dirname) @@ -236,7 +236,7 @@ def queue_events(self, timeout, events): if event.is_modified: self._queue_modified_event(event, src_path, src_dirname) - elif event.is_inode_meta_mod or event.is_xattr_mod: + elif self._is_meta_mod(event): self._queue_attrib_event(event, src_path, src_dirname) elif event.is_renamed: @@ -266,7 +266,7 @@ def queue_events(self, timeout, events): if dst_event.is_modified: self._queue_modified_event(dst_event, dst_path, dst_dirname) - elif dst_event.is_inode_meta_mod or dst_event.is_xattr_mod: + elif self._is_meta_mod(dst_event): self._queue_attrib_event(dst_event, dst_path, dst_dirname) elif dst_event.is_removed: From 4622197cabee171291fe73429237615121aab110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Sat, 22 Apr 2023 17:02:11 +0200 Subject: [PATCH 31/39] Update test_emitter.py --- tests/test_emitter.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/test_emitter.py b/tests/test_emitter.py index e1a3eefda..b6e52bcd6 100644 --- a/tests/test_emitter.py +++ b/tests/test_emitter.py @@ -354,9 +354,11 @@ def test_recursive_on(p: P, start_watching: StartWatching, expect_event: ExpectE if not platform.is_windows(): expect_event(DirModifiedEvent(p("dir1", "dir2", "dir3"))) - expect_event(FileOpenedEvent(p("dir1", "dir2", "dir3", "a"))) + if platform.is_linux(): + expect_event(FileOpenedEvent(p("dir1", "dir2", "dir3", "a"))) expect_event(FileAttribEvent(p("dir1", "dir2", "dir3", "a"))) - expect_event(FileClosedEvent(p("dir1", "dir2", "dir3", "a"))) + if platform.is_linux(): + expect_event(FileClosedEvent(p("dir1", "dir2", "dir3", "a"))) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) @@ -378,9 +380,9 @@ def test_recursive_off( if not platform.is_windows(): expect_event(DirModifiedEvent(p())) - if platform.is_linux(): - expect_event(FileOpenedEvent(p("b"))) - expect_event(FileClosedEvent(p("b"))) + if platform.is_linux(): + expect_event(FileOpenedEvent(p("b"))) + expect_event(FileClosedEvent(p("b"))) # currently limiting these additional events to macOS only, see https://github.com/gorakhargosh/watchdog/pull/779 if platform.is_darwin(): From af9436edf601f140dd693f73e78efde9294482e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Sat, 22 Apr 2023 17:13:01 +0200 Subject: [PATCH 32/39] Update test_fsevents.py --- tests/test_fsevents.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_fsevents.py b/tests/test_fsevents.py index 01e9fee21..20f1bb914 100644 --- a/tests/test_fsevents.py +++ b/tests/test_fsevents.py @@ -262,6 +262,7 @@ def __init__(self, *args, **kwargs): self.observed_events = set() def on_any_event(self, event): + print(event) self.expected_events.remove(event) self.observed_events.add(event) From 8069705f57f6f8da23e2d8ff7562647777e23daf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Mon, 24 Apr 2023 09:25:38 +0200 Subject: [PATCH 33/39] Update test_emitter.py --- tests/test_emitter.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/test_emitter.py b/tests/test_emitter.py index b6e52bcd6..27f21097f 100644 --- a/tests/test_emitter.py +++ b/tests/test_emitter.py @@ -39,7 +39,7 @@ from watchdog.utils import platform from .shell import chmod, mkdir, mkfile, mv, rm, touch -from .utils import ExpectEvent, P, StartWatching, TestEventQueue +from .utils import ExpectAnyEvent, ExpectEvent, P, StartWatching, TestEventQueue logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) @@ -156,7 +156,7 @@ def test_chmod(p: P, start_watching: StartWatching, expect_event: ExpectEvent) - @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) -def test_move(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: +def test_move(p: P, start_watching: StartWatching, expect_any_event: ExpectAnyEvent, expect_event: ExpectEvent) -> None: mkdir(p("dir1")) mkdir(p("dir2")) mkfile(p("dir1", "a")) @@ -164,20 +164,20 @@ def test_move(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> mv(p("dir1", "a"), p("dir2", "b")) - if not platform.is_windows(): - expect_event(FileMovedEvent(p("dir1", "a"), p("dir2", "b"))) - else: + if platform.is_windows(): expect_event(FileDeletedEvent(p("dir1", "a"))) expect_event(FileCreatedEvent(p("dir2", "b"))) + else: + expect_event(FileMovedEvent(p("dir1", "a"), p("dir2", "b"))) - expect_event(DirModifiedEvent(p("dir1"))) + expect_any_event(DirModifiedEvent(p("dir1")), DirModifiedEvent(p("dir2"))) if not platform.is_windows(): - expect_event(DirModifiedEvent(p("dir2"))) + expect_any_event(DirModifiedEvent(p("dir1")), DirModifiedEvent(p("dir2"))) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) -def test_case_change(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: +def test_case_change(p: P, start_watching: StartWatching, expect_any_event: ExpectAnyEvent, expect_event: ExpectEvent) -> None: mkdir(p("dir1")) mkdir(p("dir2")) mkfile(p("dir1", "file")) @@ -185,16 +185,16 @@ def test_case_change(p: P, start_watching: StartWatching, expect_event: ExpectEv mv(p("dir1", "file"), p("dir2", "FILE")) - if not platform.is_windows(): - expect_event(FileMovedEvent(p("dir1", "file"), p("dir2", "FILE"))) - else: + if platform.is_windows(): expect_event(FileDeletedEvent(p("dir1", "file"))) expect_event(FileCreatedEvent(p("dir2", "FILE"))) + else: + expect_event(FileMovedEvent(p("dir1", "file"), p("dir2", "FILE"))) - expect_event(DirModifiedEvent(p("dir1"))) + expect_any_event(DirModifiedEvent(p("dir1")), DirModifiedEvent(p("dir2"))) if not platform.is_windows(): - expect_event(DirModifiedEvent(p("dir2"))) + e(DirModifiedEvent(p("dir1")), DirModifiedEvent(p("dir2"))) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) @@ -506,8 +506,8 @@ def test_move_nested_subdirectories_on_windows( expect_event(FileDeletedEvent(p("dir1", "dir2"))) expect_event(DirCreatedEvent(p("dir2"))) - expect_event(DirCreatedEvent(p("dir2", "dir3"))) - expect_event(FileCreatedEvent(p("dir2", "dir3", "a"))) + expect_event(DirCreatedEvent(p("dir2", "dir3"), is_synthetic=True)) + expect_event(FileCreatedEvent(p("dir2", "dir3", "a"), is_synthetic=True)) touch(p("dir2/dir3", "a")) From 6d031569242adb0c291d882c241895cc7f05e9e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Mon, 24 Apr 2023 09:26:52 +0200 Subject: [PATCH 34/39] Update conftest.py --- tests/conftest.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index b79f7ebc7..714b7ecd1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,7 +8,7 @@ import pytest -from .utils import ExpectEvent, Helper, P, StartWatching, TestEventQueue +from .utils import ExpectAnyEvent, ExpectEvent, Helper, P, StartWatching, TestEventQueue @pytest.fixture() @@ -78,3 +78,8 @@ def start_watching_fixture(helper: Helper) -> StartWatching: @pytest.fixture(name="expect_event") def expect_event_fixture(helper: Helper) -> ExpectEvent: return helper.expect_event + + +@pytest.fixture(name="expect_any_event") +def expect_any_event_fixture(helper: Helper) -> ExpectAnyEvent: + return helper.expect_any_event From 79bec884199ee404244d5109d6a40729a4374086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Mon, 24 Apr 2023 09:27:51 +0200 Subject: [PATCH 35/39] Update utils.py --- tests/utils.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/utils.py b/tests/utils.py index c70ad91b5..d47e9f6e8 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -42,6 +42,10 @@ class ExpectEvent(Protocol): def __call__(self, expected_event: FileSystemEvent, timeout: float = ...) -> None: ... +class ExpectAnyEvent(Protocol): + def __call__(self, *expected_events: FileSystemEvent, timeout: float = ...) -> None: + ... + TestEventQueue = Union["Queue[Tuple[FileSystemEvent, ObservedWatch]]"] @@ -84,6 +88,17 @@ def start_watching( return emitter + def expect_any_event(self, *expected_events: FileSystemEvent, timeout: float = 2) -> None: + """Utility function to wait up to `timeout` seconds for any `expected_event`s for `path` to show up in the queue. + + Provides some robustness for the otherwise flaky nature of asynchronous notifications. + """ + try: + event = self.event_queue.get(timeout=timeout)[0] + assert event in expected_events + except Empty: + raise + def expect_event(self, expected_event: FileSystemEvent, timeout: float = 2) -> None: """Utility function to wait up to `timeout` seconds for an `event_type` for `path` to show up in the queue. From f1593f4334472937437758a049eefce8ae0011a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Mon, 24 Apr 2023 09:28:20 +0200 Subject: [PATCH 36/39] Update tests/test_fsevents.py --- tests/test_fsevents.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_fsevents.py b/tests/test_fsevents.py index 20f1bb914..01e9fee21 100644 --- a/tests/test_fsevents.py +++ b/tests/test_fsevents.py @@ -262,7 +262,6 @@ def __init__(self, *args, **kwargs): self.observed_events = set() def on_any_event(self, event): - print(event) self.expected_events.remove(event) self.observed_events.add(event) From d2b3b247f486030567d8e474dc912f0597f0417e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Mon, 24 Apr 2023 09:33:37 +0200 Subject: [PATCH 37/39] Update tests/test_emitter.py --- tests/test_emitter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_emitter.py b/tests/test_emitter.py index 27f21097f..d529d74cf 100644 --- a/tests/test_emitter.py +++ b/tests/test_emitter.py @@ -194,7 +194,7 @@ def test_case_change(p: P, start_watching: StartWatching, expect_any_event: Expe expect_any_event(DirModifiedEvent(p("dir1")), DirModifiedEvent(p("dir2"))) if not platform.is_windows(): - e(DirModifiedEvent(p("dir1")), DirModifiedEvent(p("dir2"))) + expect_any_event(DirModifiedEvent(p("dir1")), DirModifiedEvent(p("dir2"))) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) From 814d68e6bd31d56d8aedbb066679e2501bc44415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Mon, 24 Apr 2023 09:48:39 +0200 Subject: [PATCH 38/39] Apply suggestions from code review --- tests/test_emitter.py | 7 ++++++- tests/utils.py | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/test_emitter.py b/tests/test_emitter.py index d529d74cf..777ed2cf7 100644 --- a/tests/test_emitter.py +++ b/tests/test_emitter.py @@ -177,7 +177,12 @@ def test_move(p: P, start_watching: StartWatching, expect_any_event: ExpectAnyEv @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter) -def test_case_change(p: P, start_watching: StartWatching, expect_any_event: ExpectAnyEvent, expect_event: ExpectEvent) -> None: +def test_case_change( + p: P, + start_watching: StartWatching, + expect_any_event: ExpectAnyEvent, + expect_event: ExpectEvent, +) -> None: mkdir(p("dir1")) mkdir(p("dir2")) mkfile(p("dir1", "file")) diff --git a/tests/utils.py b/tests/utils.py index d47e9f6e8..22dc72e2f 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -42,6 +42,7 @@ class ExpectEvent(Protocol): def __call__(self, expected_event: FileSystemEvent, timeout: float = ...) -> None: ... + class ExpectAnyEvent(Protocol): def __call__(self, *expected_events: FileSystemEvent, timeout: float = ...) -> None: ... @@ -89,7 +90,8 @@ def start_watching( return emitter def expect_any_event(self, *expected_events: FileSystemEvent, timeout: float = 2) -> None: - """Utility function to wait up to `timeout` seconds for any `expected_event`s for `path` to show up in the queue. + """Utility function to wait up to `timeout` seconds for any `expected_event` + for `path` to show up in the queue. Provides some robustness for the otherwise flaky nature of asynchronous notifications. """ From 550ec1dfa236547d0504e5e94974daada25b4a2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Sun, 28 Jul 2024 15:32:14 +0200 Subject: [PATCH 39/39] Apply suggestions from code review --- tests/test_emitter.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_emitter.py b/tests/test_emitter.py index 777ed2cf7..ddb94c03e 100644 --- a/tests/test_emitter.py +++ b/tests/test_emitter.py @@ -482,15 +482,15 @@ def test_move_nested_subdirectories(p: P, start_watching: StartWatching, expect_ touch(p("dir2/dir3", "a")) if platform.is_linux(): - expect_event(FileOpenedEvent(p("dir2/dir3", "a"))) + expect_event(FileOpenedEvent(p("dir2", "dir3", "a"))) if not platform.is_windows(): - expect_event(FileAttribEvent(p("dir2/dir3", "a"))) + expect_event(FileAttribEvent(p("dir2", "dir3", "a"))) if platform.is_linux(): - expect_event(FileClosedEvent(p("dir2/dir3/", "a"))) + expect_event(FileClosedEvent(p("dir2", "dir3", "a"))) - expect_event(DirModifiedEvent(p("dir2/dir3"))) + expect_event(DirModifiedEvent(p("dir2", "dir3"))) @pytest.mark.flaky(max_runs=5, min_passes=1, rerun_filter=rerun_filter)