From b3e43926bd26b6a20e69c9aa0673d077cda0e8bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Beamonte?= Date: Sun, 29 Jul 2018 01:22:10 -0400 Subject: [PATCH 01/10] Propagate superficial parameter to remove_watch_with_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The remove_watch method takes a superficial parameter but never used it, which made the remove_watch_with_id function set it as False by default. This fixes that behavior by propagating the parameter. Signed-off-by: Raphaël Beamonte --- inotify/adapters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inotify/adapters.py b/inotify/adapters.py index 8a67347..7f3f708 100644 --- a/inotify/adapters.py +++ b/inotify/adapters.py @@ -115,7 +115,7 @@ def remove_watch(self, path, superficial=False): del self.__watches[path] - self.remove_watch_with_id(wd) + self.remove_watch_with_id(wd, superficial) def remove_watch_with_id(self, wd, superficial=False): del self.__watches_r[wd] From 97c07852006a5fcd4c1c32f2d02f95d5f7c11d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Beamonte?= Date: Sun, 29 Jul 2018 01:24:52 -0400 Subject: [PATCH 02/10] Fix constant in adapter to match with DELETE events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous behavior was duplicating a block of code and making one of the two useless. This fixes that by replacing the constant to IN_DELETE instead of IN_MOVED_FROM in one of the two blocks. Signed-off-by: Raphaël Beamonte --- inotify/adapters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inotify/adapters.py b/inotify/adapters.py index 8a67347..3316e7a 100644 --- a/inotify/adapters.py +++ b/inotify/adapters.py @@ -299,7 +299,7 @@ def event_gen(self, ignore_missing_new_folders=False, **kwargs): self._i.add_watch(full_path, self._mask) - if header.mask & inotify.constants.IN_MOVED_FROM: + if header.mask & inotify.constants.IN_DELETE: _LOGGER.debug("A directory has been removed. We're " "being recursive, but it would have " "automatically been deregistered: [%s]", From fb4feb34f0139f94312f692e298205d5543f607a Mon Sep 17 00:00:00 2001 From: Elias Ohm Date: Wed, 19 Sep 2018 21:37:49 +0200 Subject: [PATCH 03/10] fix _BaseTree.event_gen function, fix+enhance Inotify.remove_watch[_with_fd] functions _BaseTree.event_gen(): remove duplicate IN_MOVED_TO, change superficial to False for IN_MOVED_TO Inotify.remove_watch_with_fd(): as this is a public function it should be possible to call this function without causing inconsitencies which are recoverable only with some dirty try/catch construct... there is also never a need to only remove the wfd from the adaptors list while keeping th path name in list. .. now functions remove_watch and remove_watch_with_fd are the same, only difference is parameter eighter pathname or watch-fd addtionally bot functions now accept a thirst state (None) for superficial - used to skip the "superficial" actions and just remove the wd from inotify (there are use-cases for this, for example with threading to be able to disable wathing from one thread while watching thread get the notification about watch end from inotify an clean up the inotify "superficial"). --- inotify/adapters.py | 53 +++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/inotify/adapters.py b/inotify/adapters.py index e8301da..36330a4 100644 --- a/inotify/adapters.py +++ b/inotify/adapters.py @@ -100,30 +100,42 @@ def add_watch(self, path_unicode, mask=inotify.constants.IN_ALL_EVENTS): return wd + def _remove_watch(self, wd, path, superficial=False): + _LOGGER.debug("Removing watch for watch-handle (%d): [%s]", + wd, path) + + if superficial is not None: + del self.__watches[path] + del self.__watches_r[wd] + inotify.adapters._LOGGER.debug(".. removed from adaptor") + if superficial is not False: + return + inotify.calls.inotify_rm_watch(self.__inotify_fd, wd) + _LOGGER.debug(".. removed from inotify") + + def remove_watch(self, path, superficial=False): """Remove our tracking information and call inotify to stop watching the given path. When a directory is removed, we'll just have to remove our tracking since inotify already cleans-up the watch. + With superficial set to None it is also possible to remove only inotify + watch to be able to wait for the final IN_IGNORED event received for + the wd (useful for example in threaded applications). """ wd = self.__watches.get(path) if wd is None: + _LOGGER.warning("Path not in watch list: [%s]", path_unicode) return - - _LOGGER.debug("Removing watch for watch-handle (%d): [%s]", - wd, path) - - del self.__watches[path] - - self.remove_watch_with_id(wd, superficial) + self._remove_watch(wd, path, superficial) def remove_watch_with_id(self, wd, superficial=False): - del self.__watches_r[wd] - - if superficial is False: - _LOGGER.debug("Removing watch for watch-handle (%d).", wd) - - inotify.calls.inotify_rm_watch(self.__inotify_fd, wd) + """Same as remove_watch but does the same by id""" + path = self.__watches_r.get(wd) + if path is None: + _LOGGER.warning("Watchdescriptor not in watch list: [%d]", wd) + return + self._remove_watch(wd, path, superficial) def _get_event_names(self, event_type): names = [] @@ -309,17 +321,12 @@ def event_gen(self, ignore_missing_new_folders=False, **kwargs): self._i.remove_watch(full_path, superficial=True) elif header.mask & inotify.constants.IN_MOVED_FROM: _LOGGER.debug("A directory has been renamed. We're " - "being recursive, but it would have " - "automatically been deregistered: [%s]", - full_path) + "being recursive, we will remove watch " + "from it and re-add with IN_MOVED_TO " + "if target parent dir is within " + "our tree: [%s]", full_path) - self._i.remove_watch(full_path, superficial=True) - elif header.mask & inotify.constants.IN_MOVED_TO: - _LOGGER.debug("A directory has been renamed. We're " - "adding a watch on it (because we're " - "being recursive): [%s]", full_path) - - self._i.add_watch(full_path, self._mask) + self._i.remove_watch(full_path, superficial=False) yield event From a4f2910be3f8852ce3816bf2c680e9ae15db4211 Mon Sep 17 00:00:00 2001 From: Elias Ohm Date: Wed, 19 Sep 2018 21:56:20 +0200 Subject: [PATCH 04/10] further safety fix for IN_MOVED_FROM handling (catch race condition doing non-superficial remove) --- .coverage | 2 +- inotify/adapters.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.coverage b/.coverage index ac029bf..88226e1 100644 --- a/.coverage +++ b/.coverage @@ -1 +1 @@ -!coverage.py: This is a private format, don't read it directly!{"lines":{"/home/dustin/development/python/pyinotify/inotify/test_support.py":[1,2,3,4,5,7,9,11,13,14,16,17,19,21,22],"/home/dustin/development/python/pyinotify/inotify/adapters.py":[1,2,3,4,5,6,8,10,11,15,16,20,22,23,25,26,27,28,31,32,35,36,39,40,41,42,43,44,46,47,49,51,53,56,59,60,61,63,65,66,67,69,70,72,73,75,76,78,84,85,88,89,91,92,93,95,97,98,99,100,101,102,104,105,107,110,112,115,117,118,121,123,124,126,132,134,135,136,138,139,141,142,145,148,150,152,153,154,161,162,163,165,170,171,172,176,177,191,194,195,196,197,199,200,201,202,204,208,209,210,216,217,219,221,230,231,232,234,235,238,239,242,245,247,250,252,253,256,259,261,264,265,267,268,269,271,273,275,276,278,280,281,282,283,285,287,288,289,292,294,295,298,299,301,302,303,305,307,308,310,312,313,314,315,317,319,327,328],"/home/dustin/development/python/pyinotify/inotify/library.py":[8,1,2,4,5],"/home/dustin/development/python/pyinotify/inotify/calls.py":[1,2,4,6,8,11,12,18,25,32,33,37,39,40,41,43,45,46,47,49,51,52,53,55,56],"/home/dustin/development/python/pyinotify/inotify/__init__.py":[1],"/home/dustin/development/python/pyinotify/inotify/constants.py":[3,4,8,9,10,11,12,13,14,15,16,17,18,19,23,24,30,34,35,36,40,41,42,43,44,46,47,48,52,53,54,55,56,57,58,59,60,61,62,63,67,68,69,73,74,75,76,77]}} \ No newline at end of file +!coverage.py: This is a private format, don't read it directly!{"lines":{"/home/dustin/development/python/pyinotify/inotify/__init__.py":[1],"/home/eohm/github/Elias481/PyInotify/inotify/constants.py":[3,4,8,9,10,11,12,13,14,15,16,17,18,19,23,24,30,34,35,36,40,41,42,43,44,46,47,48,52,53,54,55,56,57,58,59,60,61,62,63,67,68,69,73,74,75,76,77],"/home/eohm/github/Elias481/PyInotify/inotify/adapters.py":[1,2,3,4,5,6,8,10,11,15,16,20,25,27,28,30,31,32,33,36,37,40,41,44,45,50,51,52,53,54,55,57,58,60,61,63,65,68,71,72,73,75,77,78,79,81,82,89,90,91,93,95,96,98,99,101,103,104,105,107,108,109,110,111,112,113,114,116,117,118,120,121,123,124,126,127,128,129,130,131,132,133,135,136,138,140,141,142,143,144,145,146,147,148,150,152,153,155,158,159,161,162,163,164,165,167,168,169,171,172,173,175,176,177,178,179,180,181,182,183,184,185,187,188,189,190,192,193,194,195,196,197,199,200,201,203,204,205,209,210,213,215,216,217,221,222,224,227,228,230,231,232,234,235,236,239,240,242,243,244,245,246,247,248,250,252,253,255,257,258,259,260,262,265,266,267,269,270,271,272,278,279,280,281,282,283,284,285,288,289,292,293,294,295,296,297,300,301,302,303,304,306,307,309,310,311,312,314,315,316,317,318,320,321,322,323,324,326,327,329,331,332,333,334,335,336,338,339,340,341,342,343,345,347,348,349,350,352,354,355,356,357,359,361,362,363,365,366,368,369,370,372,373,374,375,376,377,379,380,381,382,384,386,387,388,389,391,393,394,395,401,402],"/home/eohm/github/Elias481/PyInotify/inotify/library.py":[1,2,4,5,8],"/home/eohm/github/Elias481/PyInotify/inotify/test_support.py":[1,2,3,4,5,7,9,11,13,14,16,17,19,21,22],"/home/eohm/github/Elias481/PyInotify/inotify/calls.py":[1,2,4,6,8,11,12,18,25,32,33,37,39,40,41,43,45,46,47,49,51,52,53,55,56],"/home/dustin/development/python/pyinotify/inotify/library.py":[8,1,2,4,5],"/home/dustin/development/python/pyinotify/inotify/adapters.py":[1,2,3,4,5,6,8,10,11,15,16,20,22,23,25,26,27,28,31,32,35,36,39,40,41,42,43,44,46,47,49,51,53,56,59,60,61,63,65,66,67,69,70,72,73,75,76,78,84,85,88,89,91,92,93,95,97,98,99,100,101,102,104,105,107,110,112,115,117,118,121,123,124,126,132,134,135,136,138,139,141,142,145,148,150,152,153,154,161,162,163,165,170,171,172,176,177,191,194,195,196,197,199,200,201,202,204,208,209,210,216,217,219,221,230,231,232,234,235,238,239,242,245,247,250,252,253,256,259,261,264,265,267,268,269,271,273,275,276,278,280,281,282,283,285,287,288,289,292,294,295,298,299,301,302,303,305,307,308,310,312,313,314,315,317,319,327,328],"/home/eohm/github/Elias481/PyInotify/inotify/__init__.py":[1],"/home/dustin/development/python/pyinotify/inotify/test_support.py":[1,2,3,4,5,7,9,11,13,14,16,17,19,21,22],"/home/dustin/development/python/pyinotify/inotify/calls.py":[1,2,4,6,8,11,12,18,25,32,33,37,39,40,41,43,45,46,47,49,51,52,53,55,56],"/home/dustin/development/python/pyinotify/inotify/constants.py":[3,4,8,9,10,11,12,13,14,15,16,17,18,19,23,24,30,34,35,36,40,41,42,43,44,46,47,48,52,53,54,55,56,57,58,59,60,61,62,63,67,68,69,73,74,75,76,77]}} \ No newline at end of file diff --git a/inotify/adapters.py b/inotify/adapters.py index 36330a4..2b677e3 100644 --- a/inotify/adapters.py +++ b/inotify/adapters.py @@ -326,7 +326,13 @@ def event_gen(self, ignore_missing_new_folders=False, **kwargs): "if target parent dir is within " "our tree: [%s]", full_path) - self._i.remove_watch(full_path, superficial=False) + try: + self._i.remove_watch(full_path, superficial=False) + except inotify.calls.InotifyError as ex: + # for the unlikely case the moved diretory is deleted + # and automatically unregistered before we try to + # unregister.... + pass yield event From d8767587fa77d59f03a7a094c73990b166f87758 Mon Sep 17 00:00:00 2001 From: Elias Ohm Date: Sat, 22 Sep 2018 18:24:37 +0200 Subject: [PATCH 05/10] testcase for https://github.com/dsoprea/PyInotify/issues/46 --- .coverage | 2 +- tests/test_inotify.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/.coverage b/.coverage index ac029bf..3efa2dd 100644 --- a/.coverage +++ b/.coverage @@ -1 +1 @@ -!coverage.py: This is a private format, don't read it directly!{"lines":{"/home/dustin/development/python/pyinotify/inotify/test_support.py":[1,2,3,4,5,7,9,11,13,14,16,17,19,21,22],"/home/dustin/development/python/pyinotify/inotify/adapters.py":[1,2,3,4,5,6,8,10,11,15,16,20,22,23,25,26,27,28,31,32,35,36,39,40,41,42,43,44,46,47,49,51,53,56,59,60,61,63,65,66,67,69,70,72,73,75,76,78,84,85,88,89,91,92,93,95,97,98,99,100,101,102,104,105,107,110,112,115,117,118,121,123,124,126,132,134,135,136,138,139,141,142,145,148,150,152,153,154,161,162,163,165,170,171,172,176,177,191,194,195,196,197,199,200,201,202,204,208,209,210,216,217,219,221,230,231,232,234,235,238,239,242,245,247,250,252,253,256,259,261,264,265,267,268,269,271,273,275,276,278,280,281,282,283,285,287,288,289,292,294,295,298,299,301,302,303,305,307,308,310,312,313,314,315,317,319,327,328],"/home/dustin/development/python/pyinotify/inotify/library.py":[8,1,2,4,5],"/home/dustin/development/python/pyinotify/inotify/calls.py":[1,2,4,6,8,11,12,18,25,32,33,37,39,40,41,43,45,46,47,49,51,52,53,55,56],"/home/dustin/development/python/pyinotify/inotify/__init__.py":[1],"/home/dustin/development/python/pyinotify/inotify/constants.py":[3,4,8,9,10,11,12,13,14,15,16,17,18,19,23,24,30,34,35,36,40,41,42,43,44,46,47,48,52,53,54,55,56,57,58,59,60,61,62,63,67,68,69,73,74,75,76,77]}} \ No newline at end of file +!coverage.py: This is a private format, don't read it directly!{"lines":{"/home/dustin/development/python/pyinotify/inotify/__init__.py":[1],"/home/eohm/github/Elias481/PyInotify/inotify/calls.py":[1,2,4,6,8,11,12,13,14,16,18,25,32,33,34,35,37,39,40,41,43,45,46,47,49,51,52,53,55,56],"/home/eohm/github/Elias481/PyInotify/inotify/constants.py":[3,4,8,9,10,11,12,13,14,15,16,17,18,19,23,24,30,34,35,36,40,41,42,43,44,46,47,48,52,53,54,55,56,57,58,59,60,61,62,63,67,68,69,73,74,75,76,77],"/home/dustin/development/python/pyinotify/inotify/calls.py":[1,2,4,6,8,11,12,18,25,32,33,37,39,40,41,43,45,46,47,49,51,52,53,55,56],"/home/dustin/development/python/pyinotify/inotify/test_support.py":[1,2,3,4,5,7,9,11,13,14,16,17,19,21,22],"/home/eohm/github/Elias481/PyInotify/inotify/test_support.py":[1,2,3,4,5,7,9,11,13,14,16,17,19,21,22],"/home/eohm/github/Elias481/PyInotify/inotify/__init__.py":[1],"/home/dustin/development/python/pyinotify/inotify/constants.py":[3,4,8,9,10,11,12,13,14,15,16,17,18,19,23,24,30,34,35,36,40,41,42,43,44,46,47,48,52,53,54,55,56,57,58,59,60,61,62,63,67,68,69,73,74,75,76,77],"/home/dustin/development/python/pyinotify/inotify/adapters.py":[1,2,3,4,5,6,8,10,11,15,16,20,22,23,25,26,27,28,31,32,35,36,39,40,41,42,43,44,46,47,49,51,53,56,59,60,61,63,65,66,67,69,70,72,73,75,76,78,84,85,88,89,91,92,93,95,97,98,99,100,101,102,104,105,107,110,112,115,117,118,121,123,124,126,132,134,135,136,138,139,141,142,145,148,150,152,153,154,161,162,163,165,170,171,172,176,177,191,194,195,196,197,199,200,201,202,204,208,209,210,216,217,219,221,230,231,232,234,235,238,239,242,245,247,250,252,253,256,259,261,264,265,267,268,269,271,273,275,276,278,280,281,282,283,285,287,288,289,292,294,295,298,299,301,302,303,305,307,308,310,312,313,314,315,317,319,327,328],"/home/eohm/github/Elias481/PyInotify/inotify/adapters.py":[1,2,3,4,5,6,8,10,11,15,16,20,25,27,28,30,31,32,33,36,37,40,41,44,45,50,51,52,53,54,55,57,58,60,61,63,65,68,71,72,73,75,77,78,79,81,82,89,90,91,93,95,96,98,99,101,103,109,110,113,114,116,118,120,121,123,124,126,128,129,130,131,132,133,135,136,138,141,143,146,147,150,152,153,155,161,163,164,165,167,168,169,171,172,175,178,180,182,183,184,185,187,188,189,192,193,201,203,204,205,209,210,224,227,228,230,231,232,234,235,236,240,243,245,246,247,248,250,253,258,259,260,266,267,269,271,280,281,282,284,285,288,289,292,293,295,297,300,302,303,306,309,310,317,318,320,322,324,326,331,332,334,335,336,338,340,342,343,345,347,348,349,350,352,354,355,356,359,361,362,365,366,368,369,370,372,374,375,377,379,380,381,382,384,386,394,395],"/home/eohm/github/Elias481/PyInotify/inotify/library.py":[1,2,4,5,8],"/home/dustin/development/python/pyinotify/inotify/library.py":[8,1,2,4,5]}} \ No newline at end of file diff --git a/tests/test_inotify.py b/tests/test_inotify.py index 0f87524..a2445d1 100644 --- a/tests/test_inotify.py +++ b/tests/test_inotify.py @@ -349,6 +349,37 @@ def test__automatic_new_watches_on_existing_paths(self): self.assertEquals(events, expected) + def test__moving_readded_folder(self): + #test for https://github.com/dsoprea/PyInotify/issues/46 + #doing no checks of genereated events as current master does + #not generate events that should really be expected in this case + #avoid having to adjust this - also not implement chcking for expected + #wd assignment now.. + #just check for no exception and expected watches in the end + #emulate slow mkdir/rmdir/rename... (because of another unfixed bug and + #because this is needed to reproduces issue) + with inotify.test_support.temp_path() as path: + path1 = os.path.join(path, 'org_folder') + path2 = os.path.join(path, 'ren_folder') + + i = inotify.adapters.InotifyTree(path) + os.mkdir(path1) + events = self.__read_all_events(i) + os.rmdir(path1) + events = self.__read_all_events(i) + os.mkdir(path1) + os.rename(path1, path2) + events = self.__read_all_events(i) + + watches = i._i._Inotify__watches + watches_reverse = i._i._Inotify__watches_r + + watches_expect = sorted((path,path2)) + watches_reg_names = sorted(sorted(watches.keys())) + watches_reg_check = dict((value, key) for key, value in watches.items()) + + self.assertEquals(watches_expect, watches_reg_names) + self.assertEquals(watches_reg_check, watches_reverse) class TestInotifyTrees(unittest.TestCase): def __init__(self, *args, **kwargs): From 83307350eb25e35a45cf480156f0f778950ec868 Mon Sep 17 00:00:00 2001 From: Elias Ohm Date: Sat, 22 Sep 2018 18:44:58 +0200 Subject: [PATCH 06/10] fixed bug in test found at merging time (it did not do all steps slow by slow) luckily it pointed me to another bug --- tests/test_inotify.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_inotify.py b/tests/test_inotify.py index a2445d1..1834075 100644 --- a/tests/test_inotify.py +++ b/tests/test_inotify.py @@ -368,6 +368,7 @@ def test__moving_readded_folder(self): os.rmdir(path1) events = self.__read_all_events(i) os.mkdir(path1) + events = self.__read_all_events(i) os.rename(path1, path2) events = self.__read_all_events(i) From 2e4c0d4ca92175d907af61b197ef4e3a1cc091d4 Mon Sep 17 00:00:00 2001 From: Elias Ohm Date: Sat, 22 Sep 2018 18:48:39 +0200 Subject: [PATCH 07/10] fixed small (nut important) bug in debug-log [caused by copy&pasting the block from my script that currently need to subclass INotify for different reasons...) --- inotify/adapters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inotify/adapters.py b/inotify/adapters.py index 2b677e3..af277f9 100644 --- a/inotify/adapters.py +++ b/inotify/adapters.py @@ -125,7 +125,7 @@ def remove_watch(self, path, superficial=False): wd = self.__watches.get(path) if wd is None: - _LOGGER.warning("Path not in watch list: [%s]", path_unicode) + _LOGGER.warning("Path not in watch list: [%s]", path) return self._remove_watch(wd, path, superficial) From 625b10737e8aedcc8997c130ea066563eeffaefc Mon Sep 17 00:00:00 2001 From: Elias Ohm Date: Sat, 22 Sep 2018 19:28:33 +0200 Subject: [PATCH 08/10] adjusted needed event list (has not been done by implementor of MOVED_FROM/-TO handling, as this branch shall fix all the bugs introduced by that change..) --- inotify/adapters.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/inotify/adapters.py b/inotify/adapters.py index af277f9..895b34e 100644 --- a/inotify/adapters.py +++ b/inotify/adapters.py @@ -273,10 +273,15 @@ def __init__(self, mask=inotify.constants.IN_ALL_EVENTS, # No matter what we actually received as the mask, make sure we have # the minimum that we require to curate our list of watches. + # + # todo: we really should have two masks... the combined one (requested|needed) + # and the user specified mask for the events he wants to receive from tree... self._mask = mask | \ inotify.constants.IN_ISDIR | \ inotify.constants.IN_CREATE | \ - inotify.constants.IN_DELETE + inotify.constants.IN_MOVED_TO | \ + inotify.constants.IN_DELETE | \ + inotify.constants.IN_MOVED_FROM self._i = Inotify(block_duration_s=block_duration_s) @@ -302,6 +307,13 @@ def event_gen(self, ignore_missing_new_folders=False, **kwargs): ) and \ ( os.path.exists(full_path) is True or + # todo: as long as the "Path already being watche/not in watch list" warnings + # instead of exceptions are in place, it should really be default to also log + # only a warning if target folder does not exists in tree autodiscover mode. + # - but probably better to implement that with try/catch around add_watch + # when errno fix is merged and also this should normally not be an argument + # to event_gen but to InotifyTree(s) constructor (at least set default there) + # to not steal someones use case to specify this differently for each event_call?? ignore_missing_new_folders is False ): _LOGGER.debug("A directory has been created. We're " From c8e65af7d9f50ed6955d63b95fa06b7197278b34 Mon Sep 17 00:00:00 2001 From: Elias Ohm Date: Sat, 22 Sep 2018 21:15:56 +0200 Subject: [PATCH 09/10] testcase for https://github.com/dsoprea/PyInotify/issues/51 --- .coverage | 2 +- tests/test_inotify.py | 74 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/.coverage b/.coverage index ac029bf..5338883 100644 --- a/.coverage +++ b/.coverage @@ -1 +1 @@ -!coverage.py: This is a private format, don't read it directly!{"lines":{"/home/dustin/development/python/pyinotify/inotify/test_support.py":[1,2,3,4,5,7,9,11,13,14,16,17,19,21,22],"/home/dustin/development/python/pyinotify/inotify/adapters.py":[1,2,3,4,5,6,8,10,11,15,16,20,22,23,25,26,27,28,31,32,35,36,39,40,41,42,43,44,46,47,49,51,53,56,59,60,61,63,65,66,67,69,70,72,73,75,76,78,84,85,88,89,91,92,93,95,97,98,99,100,101,102,104,105,107,110,112,115,117,118,121,123,124,126,132,134,135,136,138,139,141,142,145,148,150,152,153,154,161,162,163,165,170,171,172,176,177,191,194,195,196,197,199,200,201,202,204,208,209,210,216,217,219,221,230,231,232,234,235,238,239,242,245,247,250,252,253,256,259,261,264,265,267,268,269,271,273,275,276,278,280,281,282,283,285,287,288,289,292,294,295,298,299,301,302,303,305,307,308,310,312,313,314,315,317,319,327,328],"/home/dustin/development/python/pyinotify/inotify/library.py":[8,1,2,4,5],"/home/dustin/development/python/pyinotify/inotify/calls.py":[1,2,4,6,8,11,12,18,25,32,33,37,39,40,41,43,45,46,47,49,51,52,53,55,56],"/home/dustin/development/python/pyinotify/inotify/__init__.py":[1],"/home/dustin/development/python/pyinotify/inotify/constants.py":[3,4,8,9,10,11,12,13,14,15,16,17,18,19,23,24,30,34,35,36,40,41,42,43,44,46,47,48,52,53,54,55,56,57,58,59,60,61,62,63,67,68,69,73,74,75,76,77]}} \ No newline at end of file +!coverage.py: This is a private format, don't read it directly!{"lines":{"/home/dustin/development/python/pyinotify/inotify/library.py":[8,1,2,4,5],"/home/eohm/github/Elias481/PyInotify/inotify/test_support.py":[1,2,3,4,5,7,9,11,13,14,16,17,19,21,22],"/home/eohm/github/Elias481/PyInotify/inotify/calls.py":[1,2,4,6,8,11,12,13,14,16,18,25,32,33,34,35,37,39,40,41,43,45,46,47,49,51,52,53,55,56],"/home/dustin/development/python/pyinotify/inotify/constants.py":[3,4,8,9,10,11,12,13,14,15,16,17,18,19,23,24,30,34,35,36,40,41,42,43,44,46,47,48,52,53,54,55,56,57,58,59,60,61,62,63,67,68,69,73,74,75,76,77],"/home/dustin/development/python/pyinotify/inotify/__init__.py":[1],"/home/eohm/github/Elias481/PyInotify/inotify/adapters.py":[1,2,3,4,5,6,8,10,11,15,16,20,25,27,28,30,31,32,33,36,37,40,41,44,45,50,51,52,53,54,55,57,58,60,61,63,65,68,71,72,73,75,77,78,79,81,82,89,90,91,93,95,96,98,99,101,103,109,110,113,114,116,118,120,121,123,124,126,128,129,130,131,132,133,135,136,138,141,143,146,147,150,152,153,155,161,163,164,165,167,168,169,171,172,175,178,180,182,183,184,185,187,188,189,192,193,201,203,204,205,209,210,224,227,228,230,231,232,234,235,236,240,243,245,246,247,248,250,253,258,259,260,266,267,269,271,280,281,282,284,285,288,289,292,293,295,297,300,302,303,306,309,310,317,318,320,322,324,326,331,332,334,335,336,338,340,342,343,345,347,348,349,350,352,354,355,356,359,361,362,365,366,368,369,370,372,374,375,377,379,380,381,382,384,386,394,395],"/home/eohm/github/Elias481/PyInotify/inotify/constants.py":[3,4,8,9,10,11,12,13,14,15,16,17,18,19,23,24,30,34,35,36,40,41,42,43,44,46,47,48,52,53,54,55,56,57,58,59,60,61,62,63,67,68,69,73,74,75,76,77],"/home/dustin/development/python/pyinotify/inotify/adapters.py":[1,2,3,4,5,6,8,10,11,15,16,20,22,23,25,26,27,28,31,32,35,36,39,40,41,42,43,44,46,47,49,51,53,56,59,60,61,63,65,66,67,69,70,72,73,75,76,78,84,85,88,89,91,92,93,95,97,98,99,100,101,102,104,105,107,110,112,115,117,118,121,123,124,126,132,134,135,136,138,139,141,142,145,148,150,152,153,154,161,162,163,165,170,171,172,176,177,191,194,195,196,197,199,200,201,202,204,208,209,210,216,217,219,221,230,231,232,234,235,238,239,242,245,247,250,252,253,256,259,261,264,265,267,268,269,271,273,275,276,278,280,281,282,283,285,287,288,289,292,294,295,298,299,301,302,303,305,307,308,310,312,313,314,315,317,319,327,328],"/home/dustin/development/python/pyinotify/inotify/calls.py":[1,2,4,6,8,11,12,18,25,32,33,37,39,40,41,43,45,46,47,49,51,52,53,55,56],"/home/eohm/github/Elias481/PyInotify/inotify/library.py":[1,2,4,5,8],"/home/eohm/github/Elias481/PyInotify/inotify/__init__.py":[1],"/home/dustin/development/python/pyinotify/inotify/test_support.py":[1,2,3,4,5,7,9,11,13,14,16,17,19,21,22]}} \ No newline at end of file diff --git a/tests/test_inotify.py b/tests/test_inotify.py index 0f87524..e7bd0fe 100644 --- a/tests/test_inotify.py +++ b/tests/test_inotify.py @@ -2,6 +2,7 @@ import os import unittest +import shutil import inotify.constants import inotify.adapters @@ -349,6 +350,79 @@ def test__automatic_new_watches_on_existing_paths(self): self.assertEquals(events, expected) + def test__readd_deleted_folder(self): + #test for https://github.com/dsoprea/PyInotify/issues/51 + #doing no checks the directory-discovery events as current master does + #not generate events that should really be expected in this case + #avoid having to adjust this - also not implement chcking for expected + #wd assignment now.. + #just check for no exception, file creation events and expected watches + #at the end. emulate slow succession of filesystem actions... (because + #of another unfixed bug and because this is needed to reproduces issue) + with inotify.test_support.temp_path() as path: + path1 = os.path.join(path, 'folder') + file1 = os.path.join(path1, 'file1') + file2 = os.path.join(path1, 'file2') + + i = inotify.adapters.InotifyTree(path) + os.mkdir(path1) + events = self.__read_all_events(i) + with open(file1, 'w'): + pass + with open(file2, 'w'): + pass + events = self.__read_all_events(i) + + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=256, cookie=0, len=16), ['IN_CREATE'], path1, 'file1'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=32, cookie=0, len=16), ['IN_OPEN'], path1, 'file1'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], path1, 'file1'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=256, cookie=0, len=16), ['IN_CREATE'], path1, 'file2'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=32, cookie=0, len=16), ['IN_OPEN'], path1, 'file2'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], path1, 'file2'), + ] + self.assertEquals(events, expected) + + shutil.rmtree(path1) + events = self.__read_all_events(i) + + #could do the following asserts here to prove the the assumption of amigian74 in + #his 5th point in issue 51 ("everything until now works fine") false, but that is + #not target of this test, also it is not his reposibility to verify this... + #so to get same issue he describes it's just a comment... + #self.assertEquals(len(i._i._Inotify__watches), 1) + #self.assertEquals(len(i._i._Inotify__watches_r), 1) + #self.assertNotIn(path1, i._i._Inotify__watches) + + os.mkdir(path1) + events = self.__read_all_events(i) + with open(file1, 'w'): + pass + with open(file2, 'w'): + pass + events = self.__read_all_events(i) + + watches = i._i._Inotify__watches + watches_reverse = i._i._Inotify__watches_r + + watches_expect = sorted((path,path1)) + watches_reg_names = sorted(watches.keys()) + watches_reg_check = dict((value, key) for key, value in watches.items()) + + self.assertEquals(watches_expect, watches_reg_names) + self.assertEquals(watches_reg_check, watches_reverse) + + wd = watches[path1] + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=wd, mask=256, cookie=0, len=16), ['IN_CREATE'], path1, 'file1'), + (inotify.adapters._INOTIFY_EVENT(wd=wd, mask=32, cookie=0, len=16), ['IN_OPEN'], path1, 'file1'), + (inotify.adapters._INOTIFY_EVENT(wd=wd, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], path1, 'file1'), + (inotify.adapters._INOTIFY_EVENT(wd=wd, mask=256, cookie=0, len=16), ['IN_CREATE'], path1, 'file2'), + (inotify.adapters._INOTIFY_EVENT(wd=wd, mask=32, cookie=0, len=16), ['IN_OPEN'], path1, 'file2'), + (inotify.adapters._INOTIFY_EVENT(wd=wd, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], path1, 'file2'), + ] + self.assertEquals(events, expected) + class TestInotifyTrees(unittest.TestCase): def __init__(self, *args, **kwargs): From da8fadeb58cfc7e8626df50a0dd627392fbf121b Mon Sep 17 00:00:00 2001 From: Elias Ohm Date: Sat, 22 Sep 2018 21:21:44 +0200 Subject: [PATCH 10/10] fix minor typo... --- tests/test_inotify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_inotify.py b/tests/test_inotify.py index 1834075..4ec1b46 100644 --- a/tests/test_inotify.py +++ b/tests/test_inotify.py @@ -376,7 +376,7 @@ def test__moving_readded_folder(self): watches_reverse = i._i._Inotify__watches_r watches_expect = sorted((path,path2)) - watches_reg_names = sorted(sorted(watches.keys())) + watches_reg_names = sorted(watches.keys()) watches_reg_check = dict((value, key) for key, value in watches.items()) self.assertEquals(watches_expect, watches_reg_names)