Skip to content

Commit

Permalink
Modified add_watch() to handle repeated calls on the same path. When
Browse files Browse the repository at this point in the history
this method is called with an already watched path, it is ignored.
When option rec=True is used, all the subtree is iterated and
watches are only added on paths not already watched. That way
existing watches are preserved while enabling new watches to be
placed on new subdirectories. To update a watch use update_watch()
instead. (reported by Matthew Webber [email protected]).
  • Loading branch information
seb-m committed May 14, 2010
1 parent 2f2dd6b commit f751065
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 28 deletions.
40 changes: 25 additions & 15 deletions python2/pyinotify.py
Original file line number Diff line number Diff line change
Expand Up @@ -1583,29 +1583,32 @@ def watches(self):
"""
return self._wmd

def __add_watch(self, path, mask, proc_fun, auto_add, exclude_filter):
def __format_path(self, path):
"""
Add a watch on path, build a Watch object and insert it in the
watch manager dictionary. Return the wd value.
Format path to its internal (stored in watch manager) representation.
"""
# Unicode strings are converted to byte strings, it seems to be
# required because LIBC.inotify_add_watch does not work well when
# it receives an ctypes.create_unicode_buffer instance as argument.
# Therefore even wd are indexed with bytes string and not with
# unicode paths.
if isinstance(path, unicode):
byte_path = path.encode(sys.getfilesystemencoding())
else:
byte_path = path
path = path.encode(sys.getfilesystemencoding())
return os.path.normpath(path)

def __add_watch(self, path, mask, proc_fun, auto_add, exclude_filter):
"""
Add a watch on path, build a Watch object and insert it in the
watch manager dictionary. Return the wd value.
"""
byte_path = self.__format_path(path)
wd_ = LIBC.inotify_add_watch(self._fd,
ctypes.create_string_buffer(byte_path),
mask)
if wd_ < 0:
return wd_
watch_ = Watch(wd=wd_, path=os.path.normpath(byte_path), mask=mask,
proc_fun=proc_fun, auto_add=auto_add,
exclude_filter=exclude_filter)
watch_ = Watch(wd=wd_, path=byte_path, mask=mask, proc_fun=proc_fun,
auto_add=auto_add, exclude_filter=exclude_filter)
self._wmd[wd_] = watch_
log.debug('New %s', watch_)
return wd_
Expand All @@ -1627,6 +1630,9 @@ def add_watch(self, path, mask, proc_fun=None, rec=False,
although unicode paths are accepted there are converted to byte
strings before a watch is put on that path. The encoding used for
converting the unicode object is given by sys.getfilesystemencoding().
If |path| si already watched it is ignored, but if it is called with
option rec=True a watch is put on each one of its not-watched
subdirectory.
@param path: Path to watch, the path can either be a file or a
directory. Also accepts a sequence (list) of paths.
Expand Down Expand Up @@ -1658,7 +1664,8 @@ def add_watch(self, path, mask, proc_fun=None, rec=False,
@return: dict of paths associated to watch descriptors. A wd value
is positive if the watch was added sucessfully,
otherwise the value is negative. If the path was invalid
it is not included into this returned dictionary.
or was already watched it is not included into this returned
dictionary.
@rtype: dict of {str: int}
"""
ret_ = {} # return {path: wd, ...}
Expand All @@ -1672,6 +1679,11 @@ def add_watch(self, path, mask, proc_fun=None, rec=False,
for apath in self.__glob(npath, do_glob):
# recursively list subdirs according to rec param
for rpath in self.__walk_rec(apath, rec):
if self.get_wd(rpath) is not None:
# We decide to ignore paths already inserted into
# the watch manager. Need to be removed with rm_watch()
# first. Or simply call update_watch() to update it.
continue
if not exclude_filter(rpath):
wd = ret_[rpath] = self.__add_watch(rpath, mask,
proc_fun,
Expand Down Expand Up @@ -1702,7 +1714,7 @@ def __get_sub_rec(self, lpath):
"""
for d in lpath:
root = self.get_path(d)
if root:
if root is not None:
# always keep root
yield d
else:
Expand Down Expand Up @@ -1820,11 +1832,10 @@ def get_wd(self, path):
@return: WD or None.
@rtype: int or None
"""
path = os.path.normpath(path)
path = self.__format_path(path)
for iwd in self._wmd.items():
if iwd[1].path == path:
return iwd[0]
log.debug('get_wd: unknown path %s', path)

def get_path(self, wd):
"""
Expand All @@ -1836,9 +1847,8 @@ def get_path(self, wd):
@rtype: string or None
"""
watch_ = self._wmd.get(wd)
if watch_:
if watch_ is not None:
return watch_.path
log.debug('get_path: unknown WD %d', wd)

def __walk_rec(self, top, rec):
"""
Expand Down
38 changes: 25 additions & 13 deletions python3/pyinotify.py
Original file line number Diff line number Diff line change
Expand Up @@ -1537,13 +1537,20 @@ def watches(self):
"""
return self._wmd

def __format_path(self, path):
"""
Format path to its internal (stored in watch manager) representation.
"""
# path must be a unicode string (str) and is just normalized.
return os.path.normpath(path)

def __add_watch(self, path, mask, proc_fun, auto_add, exclude_filter):
"""
Add a watch on path, build a Watch object and insert it in the
watch manager dictionary. Return the wd value.
"""
# path must be a unicode string (str) it is then converted to a
# bytes string. This conversion seems to be required because
path = self.__format_path(path)
# path to a bytes string. This conversion seems to be required because
# ctypes.create_string_buffer seems to manipulate bytes
# strings representations internally.
# Moreover it seems that LIBC.inotify_add_watch does not work very
Expand All @@ -1556,9 +1563,8 @@ def __add_watch(self, path, mask, proc_fun, auto_add, exclude_filter):
mask)
if wd_ < 0:
return wd_
watch_ = Watch(wd=wd_, path=os.path.normpath(path), mask=mask,
proc_fun=proc_fun, auto_add=auto_add,
exclude_filter=exclude_filter)
watch_ = Watch(wd=wd_, path=path, mask=mask, proc_fun=proc_fun,
auto_add=auto_add, exclude_filter=exclude_filter)
self._wmd[wd_] = watch_
log.debug('New %s', watch_)
return wd_
Expand All @@ -1577,6 +1583,9 @@ def add_watch(self, path, mask, proc_fun=None, rec=False,
value and optionally with a processing |proc_fun| function and
recursive flag |rec| set to True.
All |path| components _must_ be str (i.e. unicode) objects.
If |path| is already watched it is ignored, but if it is called with
option rec=True a watch is put on each one of its not-watched
subdirectory.
@param path: Path to watch, the path can either be a file or a
directory. Also accepts a sequence (list) of paths.
Expand Down Expand Up @@ -1606,9 +1615,9 @@ def add_watch(self, path, mask, proc_fun=None, rec=False,
the class' constructor.
@type exclude_filter: callable object
@return: dict of paths associated to watch descriptors. A wd value
is positive if the watch was added sucessfully,
otherwise the value is negative. If the path was invalid
it is not included into this returned dictionary.
is positive if the watch was added sucessfully, otherwise
the value is negative. If the path was invalid or was already
watched it is not included into this returned dictionary.
@rtype: dict of {str: int}
"""
ret_ = {} # return {path: wd, ...}
Expand All @@ -1627,6 +1636,11 @@ def add_watch(self, path, mask, proc_fun=None, rec=False,
for apath in self.__glob(npath, do_glob):
# recursively list subdirs according to rec param
for rpath in self.__walk_rec(apath, rec):
if self.get_wd(rpath) is not None:
# We decide to ignore paths already inserted into
# the watch manager. Need to be removed with rm_watch()
# first. Or simply call update_watch() to update it.
continue
if not exclude_filter(rpath):
wd = ret_[rpath] = self.__add_watch(rpath, mask,
proc_fun,
Expand Down Expand Up @@ -1657,7 +1671,7 @@ def __get_sub_rec(self, lpath):
"""
for d in lpath:
root = self.get_path(d)
if root:
if root is not None:
# always keep root
yield d
else:
Expand Down Expand Up @@ -1779,11 +1793,10 @@ def get_wd(self, path):
@return: WD or None.
@rtype: int or None
"""
path = os.path.normpath(path)
path = self.__format_path(path)
for iwd in self._wmd.items():
if iwd[1].path == path:
return iwd[0]
log.debug('get_wd: unknown path %s', path)

def get_path(self, wd):
"""
Expand All @@ -1795,9 +1808,8 @@ def get_path(self, wd):
@rtype: string or None
"""
watch_ = self._wmd.get(wd)
if watch_:
if watch_ is not None:
return watch_.path
log.debug('get_path: unknown WD %d', wd)

def __walk_rec(self, top, rec):
"""
Expand Down

0 comments on commit f751065

Please sign in to comment.