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') 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; diff --git a/inotify/watcher.py b/inotify/watcher.py index 429d167..e61c941 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 @@ -58,18 +58,27 @@ 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 = path + '/' + 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 = "" self.wd = raw.wd 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) return 'Event(path=' + repr(self.path) + ', ' + r[r.find('(')+1:] @@ -137,25 +146,32 @@ 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) - self._paths[path] = wd, mask - self._wds[wd] = path, mask - return wd + 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]) + if wd in self._wds: + path_mask = self._wds[wd] + del(self._wds[wd]) + 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. - + If the path is not being watched, return None.''' return self._paths.get(path) @@ -167,7 +183,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. @@ -177,11 +193,12 @@ 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() + else: + events.append(Event(evt, self._wds[evt.wd][0])) return events def close(self): @@ -270,6 +287,7 @@ class AutoWatcher(Watcher): __slots__ = ( 'addfilter', + 'moved_from', ) def __init__(self, addfilter=None): @@ -286,8 +304,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 +323,40 @@ 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): + + 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): '''Class that indicates whether a file descriptor has reached a