From a2112700379f23a2c3a9e376fc5005d117bbaeb2 Mon Sep 17 00:00:00 2001 From: Daniel Franganillo Date: Wed, 26 Jun 2013 13:25:01 +0200 Subject: [PATCH 01/11] Properly manage directory name change --- inotify/watcher.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/inotify/watcher.py b/inotify/watcher.py index 429d167..512d12e 100644 --- a/inotify/watcher.py +++ b/inotify/watcher.py @@ -69,6 +69,9 @@ def __init__(self, raw, path): self.mask = raw.mask self.cookie = raw.cookie self.name = raw.name + + def __getstate__(self): + return self.raw def __repr__(self): r = repr(self.raw) @@ -167,7 +170,7 @@ def wd(self, wd): this watcher, return None.''' return self._wds.get(wd) - + def read(self, bufsize=None): '''Read a list of queued inotify events. @@ -270,6 +273,7 @@ class AutoWatcher(Watcher): __slots__ = ( 'addfilter', + 'moved_from', ) def __init__(self, addfilter=None): @@ -286,8 +290,11 @@ def __init__(self, addfilter=None): super(AutoWatcher, self).__init__() self.addfilter = addfilter + self.moved_from = None _dir_create_mask = inotify.IN_ISDIR | inotify.IN_CREATE + _dir_moved_from = inotify.IN_ISDIR | inotify.IN_MOVED_FROM + _dir_moved_to = inotify.IN_ISDIR | inotify.IN_MOVED_TO def read(self, bufsize=None): events = super(AutoWatcher, self).read(bufsize) @@ -302,8 +309,27 @@ def read(self, bufsize=None): except OSError, err: if err.errno not in self.ignored_errors: raise + else: + if evt.mask & self._dir_moved_from == self._dir_moved_from: + # a directory is changing its name + self.moved_from = (evt.fullpath, evt.cookie) + else: + if (self.moved_from) and evt.mask & self._dir_moved_to == self._dir_moved_to: + # a directory changed already its name + if (self.moved_from[1] == evt.cookie): + # gotcha + self.replace_name(self.moved_from[0], evt.fullpath) + self.moved_from = None return events + def replace_name (self, oldpath, fullpath): + wd = self._paths[oldpath][0] + origp, mask = self._wds[wd] + self._wds[wd] = fullpath, mask + self._paths[fullpath] = wd, mask + del(self._paths[origp]) + + class Threshold(object): '''Class that indicates whether a file descriptor has reached a From 094560eba468450beb91bb2a8023215823e940a8 Mon Sep 17 00:00:00 2001 From: Daniel Franganillo Date: Mon, 1 Jul 2013 14:46:43 +0200 Subject: [PATCH 02/11] Correctly change pathnames of subdirectories under a watch --- inotify/watcher.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/inotify/watcher.py b/inotify/watcher.py index 512d12e..e1ebe61 100644 --- a/inotify/watcher.py +++ b/inotify/watcher.py @@ -323,12 +323,25 @@ def read(self, bufsize=None): return events def replace_name (self, oldpath, fullpath): - wd = self._paths[oldpath][0] - origp, mask = self._wds[wd] - self._wds[wd] = fullpath, mask - self._paths[fullpath] = wd, mask - del(self._paths[origp]) + for path in self._paths.keys(): + # Search for directories below or in same path + if (path.startswith(oldpath)): + + # Build new pathame + newpath = fullpath + path[len(oldpath):] + + # Get original watch descriptor + wd, mask = self._paths[path] + + # Save new watch&mask for path + self._wds[wd] = newpath, mask + + # Save wd&mask for the new pathname + self._paths[newpath] = wd, mask + + # Delete old key,value + del(self._paths[path]) class Threshold(object): From a731d5419650c2ff8b4dff93fb51c316e17d7974 Mon Sep 17 00:00:00 2001 From: Daniel Franganillo Date: Tue, 12 Nov 2013 12:54:46 +0100 Subject: [PATCH 03/11] Use os.path for mental sanity and avoid '/' duplication if add_watch fails wd==-1, test it --- inotify/watcher.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/inotify/watcher.py b/inotify/watcher.py index e1ebe61..983995b 100644 --- a/inotify/watcher.py +++ b/inotify/watcher.py @@ -61,7 +61,7 @@ def __init__(self, raw, path): self.path = path self.raw = raw if raw.name: - self.fullpath = path + '/' + raw.name + self.fullpath = os.path.join(path, raw.name) else: self.fullpath = path @@ -141,9 +141,12 @@ def add(self, path, mask): path = os.path.normpath(path) wd = inotify.add_watch(self.fd, path, mask) - self._paths[path] = wd, mask - self._wds[wd] = path, mask - return wd + if (wd <=0): + self._paths[path] = wd, mask + self._wds[wd] = path, mask + return wd + else: + return -1 def remove(self, wd): '''Remove the given watch.''' @@ -180,11 +183,14 @@ def read(self, bufsize=None): events = [] for evt in inotify.read(self.fd, bufsize): - events.append(Event(evt, self._wds[evt.wd][0])) - if evt.mask & inotify.IN_IGNORED: - self._remove(evt.wd) - elif evt.mask & inotify.IN_UNMOUNT: - self.close() + try: + events.append(Event(evt, self._wds[evt.wd][0])) + if evt.mask & inotify.IN_IGNORED: + self._remove(evt.wd) + elif evt.mask & inotify.IN_UNMOUNT: + self.close() + except: + continue return events def close(self): From eb100c675e8da6b05583588c34634578592e664c Mon Sep 17 00:00:00 2001 From: Daniel Franganillo Date: Mon, 24 Mar 2014 15:49:13 +0100 Subject: [PATCH 04/11] Added me as a collaborator. Added version number. Return a value instead of a function whenever we read the procfs values of inotify --- inotify/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/inotify/__init__.py b/inotify/__init__.py index 4a9b753..01ef3f4 100644 --- a/inotify/__init__.py +++ b/inotify/__init__.py @@ -17,7 +17,8 @@ For a higher-level interface that remains highly efficient, use the inotify.watcher package.''' -__author__ = "Bryan O'Sullivan " +__author__ = "Bryan O'Sullivan , Daniel Franganillo " +__version__ = "1.2.2" from _inotify import * @@ -27,14 +28,14 @@ def _read_procfs_value(name): def read_value(): try: return int(open(procfs_path + '/' + name).read()) - except OSError, err: + except OSError: return None read_value.__doc__ = '''Return the value of the %s setting from /proc. If inotify is not enabled on this system, return None.''' % name - return read_value + return read_value() max_queued_events = _read_procfs_value('max_queued_events') max_user_instances = _read_procfs_value('max_user_instances') From dd865caf8d44320f74b7cf4374e471d9228192a4 Mon Sep 17 00:00:00 2001 From: Daniel Franganillo Date: Mon, 24 Mar 2014 15:50:17 +0100 Subject: [PATCH 05/11] Fix leak. --- inotify/_inotify.c | 1 + 1 file changed, 1 insertion(+) diff --git a/inotify/_inotify.c b/inotify/_inotify.c index ccb0371..75f70f6 100644 --- a/inotify/_inotify.c +++ b/inotify/_inotify.c @@ -543,6 +543,7 @@ PyObject *read_events(PyObject *self, PyObject *args) if (PyList_Append(ret, obj) == -1) goto mybail; + Py_DECREF(obj); pos += sizeof(struct inotify_event) + in->len; continue; From cb676d3f3cd7a37183b6a93ebb7a9fc1cb081f9d Mon Sep 17 00:00:00 2001 From: Daniel Franganillo Date: Mon, 24 Mar 2014 15:50:39 +0100 Subject: [PATCH 06/11] Better management of _wds and _path dictionaries for better memory use --- inotify/watcher.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/inotify/watcher.py b/inotify/watcher.py index 983995b..177d547 100644 --- a/inotify/watcher.py +++ b/inotify/watcher.py @@ -58,17 +58,22 @@ class Event(object): ) def __init__(self, raw, path): + + if (isinstance(path, str)): + path = path.decode('utf-8') + self.path = path self.raw = raw + if raw.name: - self.fullpath = os.path.join(path, raw.name) + self.fullpath = os.path.join(path, raw.name.decode('utf-8')) else: self.fullpath = path self.wd = raw.wd self.mask = raw.mask self.cookie = raw.cookie - self.name = raw.name + self.name = raw.name.decode('utf-8') def __getstate__(self): return self.raw @@ -140,24 +145,29 @@ def add(self, path, mask): Return the watch descriptor added or modified.''' path = os.path.normpath(path) - wd = inotify.add_watch(self.fd, path, mask) - if (wd <=0): + wd = inotify.add_watch(self.fd, path.encode('utf-8'), mask) + + if (wd >= 0): self._paths[path] = wd, mask self._wds[wd] = path, mask return wd else: return -1 + def remove(self, wd): '''Remove the given watch.''' - inotify.remove_watch(self.fd, wd) self._remove(wd) + inotify.remove_watch(self.fd, wd) def _remove(self, wd): - path_mask = self._wds.pop(wd, None) - if path_mask is not None: - self._paths.pop(path_mask[0]) + try: + path_mask = self._wds[wd] + del(self._wds[wd]) + del(self._paths[path_mask[0]]) + except: + pass def path(self, path): '''Return a (watch descriptor, event mask) pair for the given path. @@ -182,13 +192,14 @@ def read(self, bufsize=None): available.''' events = [] - for evt in inotify.read(self.fd, bufsize): + for evt in inotify.read(self.fd, bufsize): try: - events.append(Event(evt, self._wds[evt.wd][0])) if evt.mask & inotify.IN_IGNORED: self._remove(evt.wd) elif evt.mask & inotify.IN_UNMOUNT: self.close() + else: + events.append(Event(evt, self._wds[evt.wd][0])) except: continue return events From f9561091f8be7fc07806f9d2261f88421e14cb67 Mon Sep 17 00:00:00 2001 From: Daniel Franganillo Date: Mon, 24 Mar 2014 15:56:43 +0100 Subject: [PATCH 07/11] Added me as a collaborator. EOL and spaces havok --- inotify/watcher.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/inotify/watcher.py b/inotify/watcher.py index 177d547..b437abf 100644 --- a/inotify/watcher.py +++ b/inotify/watcher.py @@ -19,7 +19,7 @@ The AutoWatcher class is more useful, as it automatically watches newly-created directories on your behalf.''' -__author__ = "Bryan O'Sullivan " +__author__ = "Bryan O'Sullivan , Daniel Franganillo " import _inotify as inotify import array @@ -61,7 +61,7 @@ def __init__(self, raw, path): if (isinstance(path, str)): path = path.decode('utf-8') - + self.path = path self.raw = raw @@ -154,7 +154,6 @@ def add(self, path, mask): else: return -1 - def remove(self, wd): '''Remove the given watch.''' @@ -171,7 +170,7 @@ def _remove(self, wd): def path(self, path): '''Return a (watch descriptor, event mask) pair for the given path. - + If the path is not being watched, return None.''' return self._paths.get(path) @@ -192,7 +191,7 @@ def read(self, bufsize=None): available.''' events = [] - for evt in inotify.read(self.fd, bufsize): + for evt in inotify.read(self.fd, bufsize): try: if evt.mask & inotify.IN_IGNORED: self._remove(evt.wd) @@ -348,7 +347,7 @@ def replace_name (self, oldpath, fullpath): # Build new pathame newpath = fullpath + path[len(oldpath):] - # Get original watch descriptor + # Get original watch descriptor wd, mask = self._paths[path] # Save new watch&mask for path From 030f8c6436df507276babd712f8a488dcb33d485 Mon Sep 17 00:00:00 2001 From: Daniel Franganillo Date: Fri, 28 Mar 2014 16:43:48 +0100 Subject: [PATCH 08/11] Avoid empty exception. --- inotify/watcher.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/inotify/watcher.py b/inotify/watcher.py index b437abf..b3b0c6a 100644 --- a/inotify/watcher.py +++ b/inotify/watcher.py @@ -160,13 +160,13 @@ def remove(self, wd): self._remove(wd) inotify.remove_watch(self.fd, wd) - def _remove(self, wd): - try: + def _remove(self, wd): + if wd in self._wds: path_mask = self._wds[wd] del(self._wds[wd]) - del(self._paths[path_mask[0]]) - except: - pass + if path_mask[0] in self._paths: + del(self._paths[path_mask[0]]) + def path(self, path): '''Return a (watch descriptor, event mask) pair for the given path. From 3afad826ea0e3a6efae67e415ab7c9b139bea701 Mon Sep 17 00:00:00 2001 From: Daniel Franganillo Date: Fri, 28 Mar 2014 16:50:20 +0100 Subject: [PATCH 09/11] Removed old tinfoil exception --- inotify/watcher.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/inotify/watcher.py b/inotify/watcher.py index b3b0c6a..537c440 100644 --- a/inotify/watcher.py +++ b/inotify/watcher.py @@ -192,15 +192,12 @@ def read(self, bufsize=None): events = [] for evt in inotify.read(self.fd, bufsize): - try: - if evt.mask & inotify.IN_IGNORED: - self._remove(evt.wd) - elif evt.mask & inotify.IN_UNMOUNT: - self.close() - else: - events.append(Event(evt, self._wds[evt.wd][0])) - except: - continue + if evt.mask & inotify.IN_IGNORED: + self._remove(evt.wd) + elif evt.mask & inotify.IN_UNMOUNT: + self.close() + else: + events.append(Event(evt, self._wds[evt.wd][0])) return events def close(self): From 0b4009a1a975142e48c0faa4735890f37fb125cc Mon Sep 17 00:00:00 2001 From: Daniel Franganillo Date: Thu, 25 Feb 2016 16:47:01 +0100 Subject: [PATCH 10/11] Avoid exception on directories. --- inotify/watcher.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/inotify/watcher.py b/inotify/watcher.py index 537c440..beec321 100644 --- a/inotify/watcher.py +++ b/inotify/watcher.py @@ -67,17 +67,18 @@ def __init__(self, raw, path): if raw.name: self.fullpath = os.path.join(path, raw.name.decode('utf-8')) + self.name = raw.name.decode('utf-8') else: self.fullpath = path + self.name = None self.wd = raw.wd self.mask = raw.mask self.cookie = raw.cookie - self.name = raw.name.decode('utf-8') def __getstate__(self): return self.raw - + def __repr__(self): r = repr(self.raw) return 'Event(path=' + repr(self.path) + ', ' + r[r.find('(')+1:] @@ -160,7 +161,7 @@ def remove(self, wd): self._remove(wd) inotify.remove_watch(self.fd, wd) - def _remove(self, wd): + def _remove(self, wd): if wd in self._wds: path_mask = self._wds[wd] del(self._wds[wd]) From e546a170c2b42357a843a1113dc8614d853d9c69 Mon Sep 17 00:00:00 2001 From: Daniel Franganillo Date: Mon, 11 Apr 2016 12:00:11 +0200 Subject: [PATCH 11/11] Better than None, an empty name --- inotify/watcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inotify/watcher.py b/inotify/watcher.py index beec321..e61c941 100644 --- a/inotify/watcher.py +++ b/inotify/watcher.py @@ -70,7 +70,7 @@ def __init__(self, raw, path): self.name = raw.name.decode('utf-8') else: self.fullpath = path - self.name = None + self.name = "" self.wd = raw.wd self.mask = raw.mask