diff --git a/.coverage b/.coverage index ac029bf..ee77b7c 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/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/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/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/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/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/__init__.py":[1],"/home/eohm/github/Elias481/PyInotify/inotify/library.py":[1,2,4,5,8],"/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/library.py":[8,1,2,4,5],"/home/eohm/github/Elias481/PyInotify/inotify/adapters.py":[1,2,3,4,5,6,8,10,11,12,13,14,15,16,18,19,20,23,24,25,27,28,30,31,32,33,35,36,37,38,39,40,41,44,45,48,49,50,51,52,53,54,55,57,58,59,60,61,62,63,65,66,68,69,71,72,73,75,76,77,78,79,80,81,82,83,85,86,87,89,90,91,93,95,96,97,98,99,101,103,104,106,107,109,110,111,113,114,116,117,118,120,121,122,123,124,126,128,129,130,131,132,133,134,135,136,137,138,139,140,141,143,144,146,147,149,150,151,152,153,154,155,158,160,161,163,164,165,167,168,169,171,172,173,175,176,177,178,179,180,182,183,184,185,186,187,188,189,190,191,192,193,195,196,197,200,201,203,204,205,209,210,211,212,213,217,218,223,224,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,396,397,398,399]}} \ No newline at end of file diff --git a/inotify/adapters.py b/inotify/adapters.py index 8a67347..222c7c4 100644 --- a/inotify/adapters.py +++ b/inotify/adapters.py @@ -5,6 +5,14 @@ import collections import time +if hasattr(os, 'scandir'): + from os import walk +else: + try: + from scandir import walk + except ImportError: + from os import walk + from errno import EINTR import inotify.constants @@ -223,9 +231,13 @@ def event_gen( for fd, event_type in events: # (fd) looks to always match the inotify FD. - - names = self._get_event_names(event_type) - _LOGGER.debug("Events received from epoll: {}".format(names)) + + #names = self._get_event_names(event_type) + #_LOGGER.debug("Events received from epoll: {}".format(names)) + #remove confusing event name... if resolved it should resolve to + #proper EPOLL* name (EPOLLIN/1 should be common case) + #but implement this just for this single debug line? + _LOGGER.debug("Events received from epoll (mask o%o)", event_type) for (header, type_names, path, filename) \ in self._handle_inotify_event(fd): @@ -257,7 +269,7 @@ def last_success_return(self): class _BaseTree(object): def __init__(self, mask=inotify.constants.IN_ALL_EVENTS, - block_duration_s=_DEFAULT_EPOLL_BLOCK_DURATION_S): + block_duration_s=_DEFAULT_EPOLL_BLOCK_DURATION_S, ignored_dirs=[]): # No matter what we actually received as the mask, make sure we have # the minimum that we require to curate our list of watches. @@ -266,6 +278,16 @@ def __init__(self, mask=inotify.constants.IN_ALL_EVENTS, inotify.constants.IN_CREATE | \ inotify.constants.IN_DELETE + ignored_dirs_lookup = {} + for parent, child in (os.path.split(ignored.rstrip('/')) for ignored in ignored_dirs): + if not parent: + parent = '.' + if parent in ignored_dirs_lookup: + ignored_dirs_lookup[parent].add(child) + else: + ignored_dirs_lookup[parent] = set((child,)) + self._ignored_dirs = ignored_dirs_lookup + self._i = Inotify(block_duration_s=block_duration_s) def event_gen(self, ignore_missing_new_folders=False, **kwargs): @@ -291,13 +313,17 @@ def event_gen(self, ignore_missing_new_folders=False, **kwargs): ( os.path.exists(full_path) is True or ignore_missing_new_folders is False + ) and \ + ( + path not in self._ignored_dirs or + filename not in self._ignored_dirs[path] ): _LOGGER.debug("A directory has been created. We're " "adding a watch on it (because we're " "being recursive): [%s]", full_path) - self._i.add_watch(full_path, self._mask) + self._load_tree(full_path) if header.mask & inotify.constants.IN_MOVED_FROM: _LOGGER.debug("A directory has been removed. We're " @@ -314,12 +340,6 @@ def event_gen(self, ignore_missing_new_folders=False, **kwargs): 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) yield event @@ -327,69 +347,53 @@ def event_gen(self, ignore_missing_new_folders=False, **kwargs): def inotify(self): return self._i + def _load_tree(self, path): + def filter_dirs_add_watches_gen(inotify, mask, dirpath, subdirs, ignored_subdirs): + for subdir in subdirs: + if subdir in ignored_subdirs: + continue + inotify.add_watch(os.path.join(dirpath, subdir), mask) + yield subdir + + inotify = self._i + mask = self._mask + inotify.add_watch(path, mask) + ignored_dirs = self._ignored_dirs + + for dirpath, subdirs, _f in walk(path): + ignored_subdirs = ignored_dirs.get(dirpath) + if ignored_subdirs: + subdirs[:] = filter_dirs_add_watches_gen(inotify, mask, dirpath, subdirs, ignored_subdirs) + continue + for subdir in subdirs: + inotify.add_watch(os.path.join(dirpath, subdir), mask) class InotifyTree(_BaseTree): """Recursively watch a path.""" def __init__(self, path, mask=inotify.constants.IN_ALL_EVENTS, - block_duration_s=_DEFAULT_EPOLL_BLOCK_DURATION_S): - super(InotifyTree, self).__init__(mask=mask, block_duration_s=block_duration_s) - - self.__root_path = path + block_duration_s=_DEFAULT_EPOLL_BLOCK_DURATION_S, ignored_dirs=[]): + super(InotifyTree, self).__init__(mask=mask, block_duration_s=block_duration_s, + ignored_dirs=ignored_dirs) self.__load_tree(path) def __load_tree(self, path): _LOGGER.debug("Adding initial watches on tree: [%s]", path) - - paths = [] - - q = [path] - while q: - current_path = q[0] - del q[0] - - paths.append(current_path) - - for filename in os.listdir(current_path): - entry_filepath = os.path.join(current_path, filename) - if os.path.isdir(entry_filepath) is False: - continue - - q.append(entry_filepath) - - for path in paths: - self._i.add_watch(path, self._mask) + self._load_tree(path) class InotifyTrees(_BaseTree): """Recursively watch over a list of trees.""" def __init__(self, paths, mask=inotify.constants.IN_ALL_EVENTS, - block_duration_s=_DEFAULT_EPOLL_BLOCK_DURATION_S): - super(InotifyTrees, self).__init__(mask=mask, block_duration_s=block_duration_s) + block_duration_s=_DEFAULT_EPOLL_BLOCK_DURATION_S, ignored_dirs=[]): + super(InotifyTrees, self).__init__(mask=mask, block_duration_s=block_duration_s, + ignored_dirs=ignored_dirs) self.__load_trees(paths) def __load_trees(self, paths): _LOGGER.debug("Adding initial watches on trees: [%s]", ",".join(map(str, paths))) - - found = [] - - q = paths - while q: - current_path = q[0] - del q[0] - - found.append(current_path) - - for filename in os.listdir(current_path): - entry_filepath = os.path.join(current_path, filename) - if os.path.isdir(entry_filepath) is False: - continue - - q.append(entry_filepath) - - - for path in found: - self._i.add_watch(path, self._mask) + for path in paths: + self._load_tree(path) diff --git a/inotify/resources/README.rst b/inotify/resources/README.rst index 3003a8f..d636662 100644 --- a/inotify/resources/README.rst +++ b/inotify/resources/README.rst @@ -110,6 +110,7 @@ This will immediately recurse through the directory tree and add watches on all The other differences from the standard functionality: +- You can ignore specific directories with the *ignored_dirs* parameter. - You can't remove a watch since watches are automatically managed. - Even if you provide a very restrictive mask that doesn't allow for directory create/delete events, the *IN_ISDIR*, *IN_CREATE*, and *IN_DELETE* flags will still be seen. @@ -118,7 +119,9 @@ The other differences from the standard functionality: Notes ===== -- **IMPORTANT:** Recursively monitoring paths is **not** a functionality provided by the kernel. Rather, we artificially implement it. As directory-created events are received, we create watches for the child directories on-the-fly. This means that there is potential for a race condition: if a directory is created and a file or directory is created inside before you (using the `event_gen()` loop) have a chance to observe it, then you are going to have a problem: If it is a file, then you will miss the events related to its creation, but, if it is a directory, then not only will you miss those creation events but this library will also miss them and not be able to add a watch for them. If you are dealing with a **large number of hierarchical directory creations** and have the ability to be aware new directories via a secondary channel with some lead time before any files are populated *into* them, you can take advantage of this and call `add_watch()` manually. In this case there is limited value in using `InotifyTree()`/`InotifyTree()` instead of just `Inotify()` but this choice is left to you. +- **IMPORTANT:** Recursively monitoring paths is **not** a functionality provided by the kernel. Rather, we artificially implement it. As directory-created events are received, we create watches for the child directories on-the-fly. This means that there is potential for a race condition: if a directory is created and files or directorie are created inside before you (using the `event_gen()` loop) have a chance to observe it, then you are going to miss the events related to them up to the point in time the directory is registered by `event_gen()` loop. If you are dealing with a **large number of hierarchical directory creations** and have the ability to be aware new directories via a secondary channel with some lead time before any files are populated *into* them, you can take advantage of this and call `add_watch()` manually. In this case there would be limited value in using `InotifyTree()`/`InotifyTree()` instead of just `Inotify()` so `add_watch()` is not provided by `InotifyTree()`/`InotifyTree()`. + +- For best performance on recursive paths monitoring it is recommended to install *scandir* module if You are using a pre 3.5 Python. - *epoll* is used to audit for *inotify* kernel events. diff --git a/tests/test_inotify.py b/tests/test_inotify.py index 0f87524..028cb74 100644 --- a/tests/test_inotify.py +++ b/tests/test_inotify.py @@ -14,6 +14,39 @@ else: _HAS_PYTHON2_UNICODE_SUPPORT = True +_HAS_DIRECTORY_ACCESS_EVENTS = None + +def setUpModule(): + with inotify.test_support.temp_path() as path: + subdirname = 'dir_acc_evt_tst' + inner_path = os.path.join(path, subdirname) + os.mkdir(inner_path) + + i = inotify.adapters.Inotify() + i.add_watch(path) + + dircontent = os.listdir(inner_path) + + events = list(i.event_gen(timeout_s=1, yield_nones=False)) + + expected_na = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path, subdirname), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, subdirname), + ] + expected_wa = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path, subdirname), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], path, subdirname), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, subdirname), + ] + global _HAS_DIRECTORY_ACCESS_EVENTS + if events == expected_na: + _HAS_DIRECTORY_ACCESS_EVENTS = False + elif events == expected_wa: + _HAS_DIRECTORY_ACCESS_EVENTS = True + else: + print('Got unknown list directory pattern:\n%r' %(events,)) + raise AssertionError('Found neighter expected list-directory pattern') + class TestInotify(unittest.TestCase): def __init__(self, *args, **kwargs): @@ -21,6 +54,11 @@ def __init__(self, *args, **kwargs): super(TestInotify, self).__init__(*args, **kwargs) + @classmethod + def setUpClass(cls): + global _HAS_DIRECTORY_ACCESS_EVENTS + cls._HAS_DIRECTORY_ACCESS_EVENTS = _HAS_DIRECTORY_ACCESS_EVENTS + def __read_all_events(self, i): events = list(i.event_gen(timeout_s=1, yield_nones=False)) return events @@ -152,6 +190,11 @@ def __init__(self, *args, **kwargs): super(TestInotifyTree, self).__init__(*args, **kwargs) + @classmethod + def setUpClass(cls): + global _HAS_DIRECTORY_ACCESS_EVENTS + cls._HAS_DIRECTORY_ACCESS_EVENTS = _HAS_DIRECTORY_ACCESS_EVENTS + def __read_all_events(self, i): events = list(i.event_gen(timeout_s=1, yield_nones=False)) return events @@ -166,7 +209,10 @@ def test__cycle(self): i = inotify.adapters.InotifyTree(path) - with open('seen_new_file1', 'w'): + watches = i._i._Inotify__watches + w2, w3 = watches[path1], watches[path2] + + with open(os.path.join(path, 'seen_new_file1'), 'w'): pass with open(os.path.join(path1, 'seen_new_file2'), 'w'): @@ -184,29 +230,77 @@ def test__cycle(self): events = self.__read_all_events(i) - expected = [ + if self._HAS_DIRECTORY_ACCESS_EVENTS: + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, ''), + ] + _access_dir_a = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path, 'aa'), + (inotify.adapters._INOTIFY_EVENT(wd=w2, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], path, 'aa'), + (inotify.adapters._INOTIFY_EVENT(wd=w2, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], path, 'aa'), + (inotify.adapters._INOTIFY_EVENT(wd=w2, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, 'aa'), + (inotify.adapters._INOTIFY_EVENT(wd=w2, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path1, ''), + ] + _access_dir_b = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path, 'bb'), + (inotify.adapters._INOTIFY_EVENT(wd=w3, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], path, 'bb'), + (inotify.adapters._INOTIFY_EVENT(wd=w3, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], path, 'bb'), + (inotify.adapters._INOTIFY_EVENT(wd=w3, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, 'bb'), + (inotify.adapters._INOTIFY_EVENT(wd=w3, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path2, ''), + ] + else: + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, ''), + ] + _access_dir_a = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path, 'aa'), + (inotify.adapters._INOTIFY_EVENT(wd=w2, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, 'aa'), + (inotify.adapters._INOTIFY_EVENT(wd=w2, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path1, ''), + ] + _access_dir_b = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path, 'bb'), + (inotify.adapters._INOTIFY_EVENT(wd=w3, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, 'bb'), + (inotify.adapters._INOTIFY_EVENT(wd=w3, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path2, ''), + ] + + # we can't be sure about the order the watches were registered + expected += (_access_dir_a + _access_dir_b if w2 < w3 + else _access_dir_b + _access_dir_a) + + expected += [ (inotify.adapters._INOTIFY_EVENT(wd=1, mask=256, cookie=0, len=16), ['IN_CREATE'], path, 'seen_new_file1'), (inotify.adapters._INOTIFY_EVENT(wd=1, mask=32, cookie=0, len=16), ['IN_OPEN'], path, 'seen_new_file1'), (inotify.adapters._INOTIFY_EVENT(wd=1, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], path, 'seen_new_file1'), - (inotify.adapters._INOTIFY_EVENT(wd=2, mask=256, cookie=0, len=16), ['IN_CREATE'], path1, 'seen_new_file2'), - (inotify.adapters._INOTIFY_EVENT(wd=2, mask=32, cookie=0, len=16), ['IN_OPEN'], path1, 'seen_new_file2'), - (inotify.adapters._INOTIFY_EVENT(wd=2, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], path1, 'seen_new_file2'), + (inotify.adapters._INOTIFY_EVENT(wd=w2, mask=256, cookie=0, len=16), ['IN_CREATE'], path1, 'seen_new_file2'), + (inotify.adapters._INOTIFY_EVENT(wd=w2, mask=32, cookie=0, len=16), ['IN_OPEN'], path1, 'seen_new_file2'), + (inotify.adapters._INOTIFY_EVENT(wd=w2, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], path1, 'seen_new_file2'), - (inotify.adapters._INOTIFY_EVENT(wd=3, mask=256, cookie=0, len=16), ['IN_CREATE'], path2, 'seen_new_file3'), - (inotify.adapters._INOTIFY_EVENT(wd=3, mask=32, cookie=0, len=16), ['IN_OPEN'], path2, 'seen_new_file3'), - (inotify.adapters._INOTIFY_EVENT(wd=3, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], path2, 'seen_new_file3'), + (inotify.adapters._INOTIFY_EVENT(wd=w3, mask=256, cookie=0, len=16), ['IN_CREATE'], path2, 'seen_new_file3'), + (inotify.adapters._INOTIFY_EVENT(wd=w3, mask=32, cookie=0, len=16), ['IN_OPEN'], path2, 'seen_new_file3'), + (inotify.adapters._INOTIFY_EVENT(wd=w3, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], path2, 'seen_new_file3'), (inotify.adapters._INOTIFY_EVENT(wd=1, mask=512, cookie=0, len=16), ['IN_DELETE'], path, 'seen_new_file1'), - (inotify.adapters._INOTIFY_EVENT(wd=2, mask=512, cookie=0, len=16), ['IN_DELETE'], path1, 'seen_new_file2'), - (inotify.adapters._INOTIFY_EVENT(wd=3, mask=512, cookie=0, len=16), ['IN_DELETE'], path2, 'seen_new_file3'), + (inotify.adapters._INOTIFY_EVENT(wd=w2, mask=512, cookie=0, len=16), ['IN_DELETE'], path1, 'seen_new_file2'), + (inotify.adapters._INOTIFY_EVENT(wd=w3, mask=512, cookie=0, len=16), ['IN_DELETE'], path2, 'seen_new_file3'), - (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1024, cookie=0, len=0), ['IN_DELETE_SELF'], path1, ''), - (inotify.adapters._INOTIFY_EVENT(wd=2, mask=32768, cookie=0, len=0), ['IN_IGNORED'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=w2, mask=1024, cookie=0, len=0), ['IN_DELETE_SELF'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=w2, mask=32768, cookie=0, len=0), ['IN_IGNORED'], path1, ''), (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073742336, cookie=0, len=16), ['IN_ISDIR', 'IN_DELETE'], path, 'aa'), - (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1024, cookie=0, len=0), ['IN_DELETE_SELF'], path2, ''), - (inotify.adapters._INOTIFY_EVENT(wd=3, mask=32768, cookie=0, len=0), ['IN_IGNORED'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=w3, mask=1024, cookie=0, len=0), ['IN_DELETE_SELF'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=w3, mask=32768, cookie=0, len=0), ['IN_IGNORED'], path2, ''), (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073742336, cookie=0, len=16), ['IN_ISDIR', 'IN_DELETE'], path, 'bb'), ] @@ -228,9 +322,31 @@ def test__renames(self): events1 = self.__read_all_events(i) - expected = [ - (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073742080, cookie=events1[0][0].cookie, len=16), ['IN_ISDIR', 'IN_CREATE'], path, 'old_folder'), - ] + if self._HAS_DIRECTORY_ACCESS_EVENTS: + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073742080, cookie=0, len=16), ['IN_ISDIR', 'IN_CREATE'], path, 'old_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path, 'old_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], old_path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], path, 'old_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], old_path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], path, 'old_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], old_path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, 'old_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], old_path, ''), + ] + else: + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073742080, cookie=0, len=16), ['IN_ISDIR', 'IN_CREATE'], path, 'old_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path, 'old_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], old_path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, 'old_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], old_path, ''), + ] self.assertEquals(events1, expected) @@ -239,10 +355,28 @@ def test__renames(self): events2 = self.__read_all_events(i) - expected = [ - (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741888, cookie=events2[0][0].cookie, len=16), ['IN_MOVED_FROM', 'IN_ISDIR'], path, 'old_folder'), - (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741952, cookie=events2[1][0].cookie, len=16), ['IN_MOVED_TO', 'IN_ISDIR'], path, 'new_folder'), - ] + if self._HAS_DIRECTORY_ACCESS_EVENTS: + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741888, cookie=events2[1][0].cookie, len=16), ['IN_MOVED_FROM', 'IN_ISDIR'], path, 'old_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741952, cookie=events2[0][0].cookie, len=16), ['IN_MOVED_TO', 'IN_ISDIR'], path, 'new_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path, 'new_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], new_path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], path, 'new_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], new_path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], path, 'new_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], new_path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, 'new_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], new_path, ''), + ] + else: + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741888, cookie=events2[1][0].cookie, len=16), ['IN_MOVED_FROM', 'IN_ISDIR'], path, 'old_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741952, cookie=events2[0][0].cookie, len=16), ['IN_MOVED_TO', 'IN_ISDIR'], path, 'new_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path, 'new_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], new_path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, 'new_folder'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], new_path, ''), + ] self.assertEquals(events2, expected) @@ -254,8 +388,8 @@ def test__renames(self): os.path.join(new_path, 'old_filename'), os.path.join(new_path, 'new_filename')) - os.remove(os.path.join('new_folder', 'new_filename')) - os.rmdir('new_folder') + os.remove(os.path.join(path, 'new_folder', 'new_filename')) + os.rmdir(os.path.join(path, 'new_folder')) events3 = self.__read_all_events(i) @@ -291,9 +425,31 @@ def test__automatic_new_watches_on_new_paths(self): events = self.__read_all_events(i) - expected = [ - (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073742080, cookie=0, len=16), ['IN_ISDIR', 'IN_CREATE'], path, 'folder1'), - ] + if self._HAS_DIRECTORY_ACCESS_EVENTS: + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073742080, cookie=0, len=16), ['IN_ISDIR', 'IN_CREATE'], path, 'folder1'), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path, 'folder1'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], path, 'folder1'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], path, 'folder1'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, 'folder1'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path1, ''), + ] + else: + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073742080, cookie=0, len=16), ['IN_ISDIR', 'IN_CREATE'], path, 'folder1'), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path, 'folder1'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, 'folder1'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path1, ''), + ] self.assertEquals(events, expected) @@ -302,9 +458,26 @@ def test__automatic_new_watches_on_new_paths(self): events = self.__read_all_events(i) - expected = [ - (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073742080, cookie=0, len=16), ['IN_ISDIR', 'IN_CREATE'], path1, 'folder2'), - ] + if self._HAS_DIRECTORY_ACCESS_EVENTS: + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073742080, cookie=0, len=16), ['IN_ISDIR', 'IN_CREATE'], path1, 'folder2'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path1, 'folder2'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], path1, 'folder2'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], path1, 'folder2'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path1, 'folder2'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path2, ''), + ] + else: + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073742080, cookie=0, len=16), ['IN_ISDIR', 'IN_CREATE'], path1, 'folder2'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path1, 'folder2'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path1, 'folder2'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path2, ''), + ] self.assertEquals(events, expected) @@ -341,11 +514,133 @@ def test__automatic_new_watches_on_existing_paths(self): events = self.__read_all_events(i) - expected = [ - (inotify.adapters._INOTIFY_EVENT(wd=3, mask=256, cookie=0, len=16), ['IN_CREATE'], path2, 'filename'), - (inotify.adapters._INOTIFY_EVENT(wd=3, mask=32, cookie=0, len=16), ['IN_OPEN'], path2, 'filename'), - (inotify.adapters._INOTIFY_EVENT(wd=3, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], path2, 'filename'), - ] + if self._HAS_DIRECTORY_ACCESS_EVENTS: + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path, 'folder1'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], path, 'folder1'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], path, 'folder1'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, 'folder1'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path1, 'folder2'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], path1, 'folder2'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], path1, 'folder2'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path1, 'folder2'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=256, cookie=0, len=16), ['IN_CREATE'], path2, 'filename'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=32, cookie=0, len=16), ['IN_OPEN'], path2, 'filename'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], path2, 'filename'), + ] + else: + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path, 'folder1'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, 'folder1'), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], path1, 'folder2'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path1, 'folder2'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=256, cookie=0, len=16), ['IN_CREATE'], path2, 'filename'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=32, cookie=0, len=16), ['IN_OPEN'], path2, 'filename'), + (inotify.adapters._INOTIFY_EVENT(wd=3, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], path2, 'filename'), + ] + + self.assertEquals(events, expected) + + def test__exclude_subdirectories(self): + + # Tests whether the skip_dires parameter works as expected + + with inotify.test_support.temp_path() as path: + for foldernum1 in range(1,5): + path1 = os.path.join(path, 'folder%d' % foldernum1) + os.mkdir(path1) + for foldernum2 in range(1,5): + path2 = os.path.join(path1, 'subfolder%d' % foldernum2) + os.mkdir(path2) + for foldernum3 in range(1,3): + path3 = os.path.join(path2, 'subsubfolder%d' % foldernum3) + os.mkdir(path3) + + ignored_dirs = (os.path.join(path, 'folder1'), + os.path.join(path, 'folder2', 'subfolder2'), + os.path.join(path, 'folder2', 'subfolder3'), + os.path.join(path, 'folder2', 'subfolder4', 'subsubfolder2'), + os.path.join(path, 'folder3'), + os.path.join(path, 'folder4'), + ) + + expected_watches = (path, os.path.join(path, 'folder2'), + os.path.join(path, 'folder2', 'subfolder1'), + os.path.join(path, 'folder2', 'subfolder1', 'subsubfolder1'), + os.path.join(path, 'folder2', 'subfolder1', 'subsubfolder2'), + os.path.join(path, 'folder2', 'subfolder4'), + os.path.join(path, 'folder2', 'subfolder4', 'subsubfolder1'), + ) + + i = inotify.adapters.InotifyTree(path, ignored_dirs=ignored_dirs) + events = self.__read_all_events(i) + + watches = i._i._Inotify__watches + self.assertEquals(sorted(watches.keys()), sorted(expected_watches)) + + discovered_subdirs_expects = { path: [] } + + if self._HAS_DIRECTORY_ACCESS_EVENTS: + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, ''), + ] + for dirpath, dirwd in sorted(watches.items(), key=lambda tup: tup[1])[1:]: + parentpath, dirname = os.path.split(dirpath) + parentwd = watches[parentpath] + expects = [ + (inotify.adapters._INOTIFY_EVENT(wd=parentwd, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], parentpath, dirname), + (inotify.adapters._INOTIFY_EVENT(wd=dirwd, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], dirpath, ''), + (inotify.adapters._INOTIFY_EVENT(wd=parentwd, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], parentpath, dirname), + (inotify.adapters._INOTIFY_EVENT(wd=dirwd, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], dirpath, ''), + (inotify.adapters._INOTIFY_EVENT(wd=parentwd, mask=1073741825, cookie=0, len=16), ['IN_ACCESS', 'IN_ISDIR'], parentpath, dirname), + (inotify.adapters._INOTIFY_EVENT(wd=dirwd, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], dirpath, ''), + (inotify.adapters._INOTIFY_EVENT(wd=parentwd, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], parentpath, dirname), + (inotify.adapters._INOTIFY_EVENT(wd=dirwd, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], dirpath, ''), + ] + discovered_subdirs_expects[parentpath].append((dirpath, expects)) + discovered_subdirs_expects[dirpath] = [] + else: + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path, ''), + ] + for dirpath, dirwd in sorted(watches.items(), key=lambda tup: tup[1])[1:]: + parentpath, dirname = os.path.split(dirpath) + parentwd = watches[parentpath] + expects = [ + (inotify.adapters._INOTIFY_EVENT(wd=parentwd, mask=1073741856, cookie=0, len=16), ['IN_ISDIR', 'IN_OPEN'], parentpath, dirname), + (inotify.adapters._INOTIFY_EVENT(wd=dirwd, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], dirpath, ''), + (inotify.adapters._INOTIFY_EVENT(wd=parentwd, mask=1073741840, cookie=0, len=16), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], parentpath, dirname), + (inotify.adapters._INOTIFY_EVENT(wd=dirwd, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], dirpath, ''), + ] + discovered_subdirs_expects[parentpath].append((dirpath, expects)) + discovered_subdirs_expects[dirpath] = [] + + for dirpath, expects in discovered_subdirs_expects[path]: + expected += expects + for dirpath, expects in discovered_subdirs_expects[dirpath]: + expected += expects + for dirpath, expects in discovered_subdirs_expects[dirpath]: + expected += expects self.assertEquals(events, expected) @@ -356,6 +651,11 @@ def __init__(self, *args, **kwargs): super(TestInotifyTrees, self).__init__(*args, **kwargs) + @classmethod + def setUpClass(cls): + global _HAS_DIRECTORY_ACCESS_EVENTS + cls._HAS_DIRECTORY_ACCESS_EVENTS = _HAS_DIRECTORY_ACCESS_EVENTS + def __read_all_events(self, i): events = list(i.event_gen(timeout_s=1, yield_nones=False)) return events @@ -378,7 +678,24 @@ def test__cycle(self): events = self.__read_all_events(i) - expected = [ + if self._HAS_DIRECTORY_ACCESS_EVENTS: + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741825, cookie=0, len=0), ['IN_ACCESS', 'IN_ISDIR'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path2, ''), + ] + else: + expected = [ + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=1, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path1, ''), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741856, cookie=0, len=0), ['IN_ISDIR', 'IN_OPEN'], path2, ''), + (inotify.adapters._INOTIFY_EVENT(wd=2, mask=1073741840, cookie=0, len=0), ['IN_ISDIR', 'IN_CLOSE_NOWRITE'], path2, ''), + ] + + expected += [ (inotify.adapters._INOTIFY_EVENT(wd=1, mask=256, cookie=0, len=16), ['IN_CREATE'], path1, 'seen_new_file1'), (inotify.adapters._INOTIFY_EVENT(wd=1, mask=32, cookie=0, len=16), ['IN_OPEN'], path1, 'seen_new_file1'), (inotify.adapters._INOTIFY_EVENT(wd=1, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], path1, 'seen_new_file1'),