Hello community, here is the log from the commit of package python3-pyinotify for openSUSE:Factory checked in at 2015-01-12 09:49:50 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python3-pyinotify (Old) and /work/SRC/openSUSE:Factory/.python3-pyinotify.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python3-pyinotify" Changes: -------- --- /work/SRC/openSUSE:Factory/python3-pyinotify/python3-pyinotify.changes 2013-06-21 19:01:55.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.python3-pyinotify.new/python3-pyinotify.changes 2015-01-12 09:49:51.000000000 +0100 @@ -1,0 +2,29 @@ +Sat Jan 10 18:00:31 UTC 2015 - [email protected] + +- specfile: + * update copyright year + * update source url to pypi + +- update to version 0.9.5 (taken from git log): + * Implement stop method in AsyncioNotifier + * Remove extra lines, fix indents + * Update pyinotify.py + * Allow TornadoAsyncNotifier.stop() to clean up all hanging refs. + * doc update + * Add asyncio example + * Add asyncio Notifier adapter + * Fix ExcludeFilter docstring examples. + * Add new classifiers. + * Typo. + * Improve example daemon.py. + * Stub support for Pyinotify on FreeBSD. + * Add WatchManager attribute to ignore events. + * Fix /proc inotify interfaces. + * Update Travis conf. + * Handle OSError exception in process_IN_CREATE. + * Add python-3.3 to travis + * py-2.5 removed as per http://about.travis-ci.org/blog/2013-11-18-upcoming-build-environment-updates/ + * add glob option to command line + * Fall back to glob.glob() on Python 2.4 + +------------------------------------------------------------------- Old: ---- pyinotify-0.9.4.tar.gz New: ---- pyinotify-0.9.5.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python3-pyinotify.spec ++++++ --- /var/tmp/diff_new_pack.zumeZo/_old 2015-01-12 09:49:52.000000000 +0100 +++ /var/tmp/diff_new_pack.zumeZo/_new 2015-01-12 09:49:52.000000000 +0100 @@ -1,7 +1,7 @@ # # spec file for package python3-pyinotify # -# Copyright (c) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,14 +17,13 @@ Name: python3-pyinotify -Version: 0.9.4 +Version: 0.9.5 Release: 0 Summary: Python module for watching filesystems changes License: MIT Group: Development/Libraries/Python Url: http://github.com/seb-m/pyinotify -# downloaded from https://github.com/seb-m/pyinotify/tags -Source: pyinotify-%{version}.tar.gz +Source: https://pypi.python.org/packages/source/p/pyinotify/pyinotify-%{version}.tar.gz Source1: pyinotify BuildRequires: python3 BuildRequires: python3-devel ++++++ pyinotify-0.9.4.tar.gz -> pyinotify-0.9.5.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyinotify-0.9.4/.gitignore new/pyinotify-0.9.5/.gitignore --- old/pyinotify-0.9.4/.gitignore 2012-11-15 19:39:22.000000000 +0100 +++ new/pyinotify-0.9.5/.gitignore 1970-01-01 01:00:00.000000000 +0100 @@ -1,15 +0,0 @@ -# General ignore rules - -*.kpf -*.o -*.pyc -*.a -*~ -\#* -*.log -*.tmp -*.bak -a.out -*.out -docstrings/ -build/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyinotify-0.9.4/.travis.yml new/pyinotify-0.9.5/.travis.yml --- old/pyinotify-0.9.4/.travis.yml 2012-11-15 19:39:22.000000000 +0100 +++ new/pyinotify-0.9.5/.travis.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,9 +0,0 @@ -language: python -python: - - "2.4" - - "2.5" - - "2.6" - - "2.7" - - "3.2" - - "pypy" -script: /bin/true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyinotify-0.9.4/PKG-INFO new/pyinotify-0.9.5/PKG-INFO --- old/pyinotify-0.9.4/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ new/pyinotify-0.9.5/PKG-INFO 2015-01-09 22:49:28.000000000 +0100 @@ -0,0 +1,33 @@ +Metadata-Version: 1.1 +Name: pyinotify +Version: 0.9.5 +Summary: Linux filesystem events monitoring +Home-page: http://github.com/seb-m/pyinotify +Author: Sebastien Martini +Author-email: [email protected] +License: MIT License +Download-URL: http://pypi.python.org/pypi/pyinotify +Description: UNKNOWN +Platform: Linux +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Natural Language :: English +Classifier: Operating System :: POSIX :: Linux +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2.4 +Classifier: Programming Language :: Python :: 2.5 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.0 +Classifier: Programming Language :: Python :: 3.1 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: System :: Filesystems +Classifier: Topic :: System :: Monitoring diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyinotify-0.9.4/python2/examples/daemon.py new/pyinotify-0.9.5/python2/examples/daemon.py --- old/pyinotify-0.9.4/python2/examples/daemon.py 2012-11-15 19:39:22.000000000 +0100 +++ new/pyinotify-0.9.5/python2/examples/daemon.py 2014-07-10 20:51:42.000000000 +0200 @@ -35,14 +35,18 @@ # Notifier instance spawns a new process when daemonize is set to True. This # child process' PID is written to /tmp/pyinotify.pid (it also automatically -# deletes it when it exits normally). If no custom pid_file is provided it -# would write it more traditionally under /var/run/. Note that in both cases -# the caller must ensure the pid file doesn't exist when this method is called -# othewise it will raise an exception. /tmp/stdout.txt is used as stdout -# stream thus traces of events will be written in it. callback is the above -# function and will be called after each event loop. +# deletes it when it exits normally). Note that this tmp location is just for +# the sake of the example to avoid requiring administrative rights in order +# to run this example. But by default if no explicit pid_file parameter is +# provided it will default to its more traditional location under /var/run/. +# Note that in both cases the caller must ensure the pid file doesn't exist +# before this method is called otherwise it will raise an exception. +# /tmp/pyinotify.log is used as log file to dump received events. Likewise +# in your real code choose a more appropriate location for instance under +# /var/log (this file may contain sensitive data). Finally, callback is the +# above function and will be called after each event loop. try: notifier.loop(daemonize=True, callback=on_loop_func, - pid_file='/tmp/pyinotify.pid', stdout='/tmp/stdout.txt') + pid_file='/tmp/pyinotify.pid', stdout='/tmp/pyinotify.log') except pyinotify.NotifierError, err: print >> sys.stderr, err diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyinotify-0.9.4/python2/pyinotify.egg-info/PKG-INFO new/pyinotify-0.9.5/python2/pyinotify.egg-info/PKG-INFO --- old/pyinotify-0.9.4/python2/pyinotify.egg-info/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ new/pyinotify-0.9.5/python2/pyinotify.egg-info/PKG-INFO 2015-01-09 22:49:27.000000000 +0100 @@ -0,0 +1,33 @@ +Metadata-Version: 1.1 +Name: pyinotify +Version: 0.9.5 +Summary: Linux filesystem events monitoring +Home-page: http://github.com/seb-m/pyinotify +Author: Sebastien Martini +Author-email: [email protected] +License: MIT License +Download-URL: http://pypi.python.org/pypi/pyinotify +Description: UNKNOWN +Platform: Linux +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Natural Language :: English +Classifier: Operating System :: POSIX :: Linux +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2.4 +Classifier: Programming Language :: Python :: 2.5 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.0 +Classifier: Programming Language :: Python :: 3.1 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: System :: Filesystems +Classifier: Topic :: System :: Monitoring diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyinotify-0.9.4/python2/pyinotify.egg-info/SOURCES.txt new/pyinotify-0.9.5/python2/pyinotify.egg-info/SOURCES.txt --- old/pyinotify-0.9.4/python2/pyinotify.egg-info/SOURCES.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/pyinotify-0.9.5/python2/pyinotify.egg-info/SOURCES.txt 2015-01-09 22:49:28.000000000 +0100 @@ -0,0 +1,34 @@ +ACKS +COPYING +MANIFEST.in +README.md +setup.py +common/inotify_syscalls.c +old/ChangeLog +old/NEWS +python2/Makefile +python2/pyinotify.py +python2/examples/autocompile.py +python2/examples/chain.py +python2/examples/coalesce.py +python2/examples/daemon.py +python2/examples/exclude.lst +python2/examples/exclude.py +python2/examples/loop.py +python2/examples/not_quiet.py +python2/examples/stats.py +python2/examples/stats_threaded.py +python2/examples/tornado_notifier.py +python2/examples/transient_file.py +python2/examples/transient_file.sh +python2/examples/tutorial_asyncnotifier.py +python2/examples/tutorial_notifier.py +python2/examples/tutorial_threadednotifier.py +python2/examples/unicode.py +python2/pyinotify.egg-info/PKG-INFO +python2/pyinotify.egg-info/SOURCES.txt +python2/pyinotify.egg-info/dependency_links.txt +python2/pyinotify.egg-info/top_level.txt +python3/Makefile +python3/pyinotify.py +python3/examples/asyncio_notifier.py \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyinotify-0.9.4/python2/pyinotify.egg-info/dependency_links.txt new/pyinotify-0.9.5/python2/pyinotify.egg-info/dependency_links.txt --- old/pyinotify-0.9.4/python2/pyinotify.egg-info/dependency_links.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/pyinotify-0.9.5/python2/pyinotify.egg-info/dependency_links.txt 2015-01-09 22:49:27.000000000 +0100 @@ -0,0 +1 @@ + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyinotify-0.9.4/python2/pyinotify.egg-info/top_level.txt new/pyinotify-0.9.5/python2/pyinotify.egg-info/top_level.txt --- old/pyinotify-0.9.4/python2/pyinotify.egg-info/top_level.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/pyinotify-0.9.5/python2/pyinotify.egg-info/top_level.txt 2015-01-09 22:49:27.000000000 +0100 @@ -0,0 +1 @@ +pyinotify diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyinotify-0.9.4/python2/pyinotify.py new/pyinotify-0.9.5/python2/pyinotify.py --- old/pyinotify-0.9.4/python2/pyinotify.py 2012-11-15 19:39:22.000000000 +0100 +++ new/pyinotify-0.9.5/python2/pyinotify.py 2015-01-09 22:13:04.000000000 +0100 @@ -1,7 +1,7 @@ #!/usr/bin/env python # pyinotify.py - python interface to inotify -# Copyright (c) 2005-2011 Sebastien Martini <[email protected]> +# Copyright (c) 2005-2015 Sebastien Martini <[email protected]> # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -68,7 +68,6 @@ import time import re import asyncore -import glob import subprocess try: @@ -77,6 +76,12 @@ pass # Will fail on Python 2.4 which has reduce() builtin anyway. try: + from glob import iglob as glob +except ImportError: + # Python 2.4 does not have glob.iglob(). + from glob import glob as glob + +try: import ctypes import ctypes.util except ImportError: @@ -90,7 +95,7 @@ __author__ = "[email protected] (Sebastien Martini)" -__version__ = "0.9.4" +__version__ = "0.9.5" __metaclass__ = type # Use new-style classes by default @@ -197,9 +202,14 @@ def init(self): assert ctypes + + try_libc_name = 'c' + if sys.platform.startswith('freebsd'): + try_libc_name = 'inotify' + libc_name = None try: - libc_name = ctypes.util.find_library('c') + libc_name = ctypes.util.find_library(try_libc_name) except (OSError, IOError): pass # Will attemp to load it with None anyway. @@ -223,7 +233,7 @@ self._libc.inotify_init.argtypes = [] self._libc.inotify_init.restype = ctypes.c_int - self._libc.inotify_add_watch.argtypes = [ctypes.c_int, ctypes.c_char_p, + self._libc.inotify_add_watch.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_uint32] self._libc.inotify_add_watch.restype = ctypes.c_int self._libc.inotify_rm_watch.argtypes = [ctypes.c_int, ctypes.c_int] @@ -305,22 +315,26 @@ def get_val(self): """ - Gets attribute's value. + Gets attribute's value. Raises OSError if the operation failed. @return: stored value. @rtype: int """ oldv = ctypes.c_int(0) size = ctypes.c_int(ctypes.sizeof(oldv)) - self._inotify_wrapper._sysctl(self._attr, 3, - ctypes.c_voidp(ctypes.addressof(oldv)), - ctypes.addressof(size), - None, 0) + sysctl = self._inotify_wrapper._sysctl + res = sysctl(self._attr, 3, + ctypes.c_voidp(ctypes.addressof(oldv)), + ctypes.addressof(size), + None, 0) + if res == -1: + raise OSError(self._inotify_wrapper.get_errno(), + self._inotify_wrapper.str_errno()) return oldv.value def set_val(self, nval): """ - Sets new attribute's value. + Sets new attribute's value. Raises OSError if the operation failed. @param nval: replaces current value by nval. @type nval: int @@ -329,11 +343,15 @@ sizeo = ctypes.c_int(ctypes.sizeof(oldv)) newv = ctypes.c_int(nval) sizen = ctypes.c_int(ctypes.sizeof(newv)) - self._inotify_wrapper._sysctl(self._attr, 3, - ctypes.c_voidp(ctypes.addressof(oldv)), - ctypes.addressof(sizeo), - ctypes.c_voidp(ctypes.addressof(newv)), - ctypes.addressof(sizen)) + sysctl = self._inotify_wrapper._sysctl + res = sysctl(self._attr, 3, + ctypes.c_voidp(ctypes.addressof(oldv)), + ctypes.addressof(sizeo), + ctypes.c_voidp(ctypes.addressof(newv)), + sizen) + if res == -1: + raise OSError(self._inotify_wrapper.get_errno(), + self._inotify_wrapper.str_errno()) value = property(get_val, set_val) @@ -730,23 +748,28 @@ # Since the directory d2 is new, then everything inside it must # also be new. created_dir_wd = addw_ret.get(created_dir) - if (created_dir_wd is not None) and (created_dir_wd > 0): - for name in os.listdir(created_dir): - inner = os.path.join(created_dir, name) - if self._watch_manager.get_wd(inner) is not None: - continue - # Generate (simulate) creation events for sub- - # directories and files. - if os.path.isfile(inner): - # symlinks are handled as files. - flags = IN_CREATE - elif os.path.isdir(inner): - flags = IN_CREATE | IN_ISDIR - else: - # This path should not be taken. - continue - rawevent = _RawEvent(created_dir_wd, flags, 0, name) - self._notifier.append_event(rawevent) + if ((created_dir_wd is not None) and (created_dir_wd > 0) and + os.path.isdir(created_dir)): + try: + for name in os.listdir(created_dir): + inner = os.path.join(created_dir, name) + if self._watch_manager.get_wd(inner) is not None: + continue + # Generate (simulate) creation events for sub- + # directories and files. + if os.path.isfile(inner): + # symlinks are handled as files. + flags = IN_CREATE + elif os.path.isdir(inner): + flags = IN_CREATE | IN_ISDIR + else: + # This path should not be taken. + continue + rawevent = _RawEvent(created_dir_wd, flags, 0, name) + self._notifier.append_event(rawevent) + except OSError, err: + msg = "process_IN_CREATE, invalid directory %s: %s" + log.debug(msg % (created_dir, str(err))) return self.process_default(raw_event) def process_IN_MOVED_FROM(self, raw_event): @@ -1139,7 +1162,7 @@ At least with read_freq set you might sleep. @type threshold: int @param timeout: - http://docs.python.org/lib/poll-objects.html#poll-objects + https://docs.python.org/3/library/select.html#polling-objects @type timeout: int """ # Watch Manager instance @@ -1279,6 +1302,9 @@ """ while self._eventq: raw_event = self._eventq.popleft() # pop next event + if self._watch_manager.ignore_events: + log.debug("Event ignored: %s" % repr(raw_event)) + continue watch_ = self._watch_manager.get_watch(raw_event.wd) if (watch_ is None) and not (raw_event.mask & IN_Q_OVERFLOW): if not (raw_event.mask & IN_IGNORED): @@ -1355,7 +1381,6 @@ # Register unlink function atexit.register(lambda : os.unlink(pid_file)) - def _sleep(self, ref_time): # Only consider sleeping if read_freq is > 0 if self._read_freq > 0: @@ -1365,7 +1390,6 @@ log.debug('Now sleeping %d seconds', sleep_amount) time.sleep(sleep_amount) - def loop(self, callback=None, daemonize=False, **args): """ Events are read only one time every min(read_freq, timeout) @@ -1412,7 +1436,6 @@ # Close internals self.stop() - def stop(self): """ Close inotify's instance (close its file descriptor). @@ -1421,6 +1444,7 @@ """ self._pollobj.unregister(self._fd) os.close(self._fd) + self._sys_proc_fun = None class ThreadedNotifier(threading.Thread, Notifier): @@ -1455,7 +1479,7 @@ least with read_freq you might sleep. @type threshold: int @param timeout: - see http://docs.python.org/lib/poll-objects.html#poll-objects + https://docs.python.org/3/library/select.html#polling-objects @type timeout: int """ # Init threading base class @@ -1545,28 +1569,32 @@ Tornado ioloop adapter. """ - def __init__(self, watch_manager, ioloop, callback=None, - default_proc_fun=None, read_freq=0, threshold=0, timeout=None, + def __init__(self, watch_manager, ioloop, callback=None, + default_proc_fun=None, read_freq=0, threshold=0, timeout=None, channel_map=None): """ - Note that if later you must call ioloop.close() be sure to let the + Note that if later you must call ioloop.close() be sure to let the default parameter to all_fds=False. See example tornado_notifier.py for an example using this notifier. @param ioloop: Tornado's IO loop. @type ioloop: tornado.ioloop.IOLoop instance. - @param callback: Functor called at the end of each call to handle_read - (IOLoop's read handler). Expects to receive the + @param callback: Functor called at the end of each call to handle_read + (IOLoop's read handler). Expects to receive the notifier object (self) as single parameter. @type callback: callable object or function """ - self.io_loop = ioloop + self.io_loop = ioloop self.handle_read_callback = callback Notifier.__init__(self, watch_manager, default_proc_fun, read_freq, threshold, timeout) ioloop.add_handler(self._fd, self.handle_read, ioloop.READ) + def stop(self): + self.io_loop.remove_handler(self._fd) + Notifier.stop(self) + def handle_read(self, *args, **kwargs): """ See comment in AsyncNotifier. @@ -1578,6 +1606,43 @@ self.handle_read_callback(self) +class AsyncioNotifier(Notifier): + """ + + asyncio/trollius event loop adapter. + + """ + def __init__(self, watch_manager, loop, callback=None, + default_proc_fun=None, read_freq=0, threshold=0, timeout=None): + """ + + See examples/asyncio_notifier.py for an example usage. + + @param loop: asyncio or trollius event loop instance. + @type loop: asyncio.BaseEventLoop or trollius.BaseEventLoop instance. + @param callback: Functor called at the end of each call to handle_read. + Expects to receive the notifier object (self) as + single parameter. + @type callback: callable object or function + + """ + self.loop = loop + self.handle_read_callback = callback + Notifier.__init__(self, watch_manager, default_proc_fun, read_freq, + threshold, timeout) + loop.add_reader(self._fd, self.handle_read) + + def stop(self): + self.loop.remove_reader(self._fd) + Notifier.stop(self) + + def handle_read(self, *args, **kwargs): + self.read_events() + self.process_events() + if self.handle_read_callback is not None: + self.handle_read_callback(self) + + class Watch: """ Represent a watch, i.e. a file or directory being watched. @@ -1639,11 +1704,11 @@ def __init__(self, arg_lst): """ Examples: - ef1 = ExcludeFilter(["^/etc/rc.*", "^/etc/hostname"]) + ef1 = ExcludeFilter(["/etc/rc.*", "/etc/hostname"]) ef2 = ExcludeFilter("/my/path/exclude.lst") Where exclude.lst contains: - ^/etc/rc.* - ^/etc/hostname + /etc/rc.* + /etc/hostname Note: it is not possible to exclude a file if its encapsulating directory is itself watched. See this issue for more details @@ -1734,6 +1799,7 @@ filter for every call to add_watch. @type exclude_filter: callable object """ + self._ignore_events = False self._exclude_filter = exclude_filter self._wmd = {} # watch dict key: watch descriptor, value: watch @@ -1830,7 +1896,7 @@ def __glob(self, path, do_glob): if do_glob: - return glob.iglob(path) + return glob(path) else: return [path] @@ -2166,6 +2232,16 @@ auto_add=False, do_glob=False, exclude_filter=lambda path: False) + def get_ignore_events(self): + return self._ignore_events + + def set_ignore_events(self, nval): + self._ignore_events = nval + + ignore_events = property(get_ignore_events, set_ignore_events, + "Make watch manager ignoring new events.") + + class RawOutputFormat: """ @@ -2253,6 +2329,9 @@ parser.add_option("-a", "--auto_add", action="store_true", dest="auto_add", help="Automatically add watches on new directories") + parser.add_option("-g", "--glob", action="store_true", + dest="glob", + help="Treat paths as globs") parser.add_option("-e", "--events-list", metavar="EVENT[,...]", dest="events_list", help=("A comma-separated list of events to watch for - " @@ -2328,7 +2407,7 @@ log.debug('Start monitoring %s, (press c^c to halt pyinotify)' % path) - wm.add_watch(path, mask, rec=options.recursive, auto_add=options.auto_add) + wm.add_watch(path, mask, rec=options.recursive, auto_add=options.auto_add, do_glob=options.glob) # Loop forever (until sigint signal get caught) notifier.loop(callback=cb_fun) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyinotify-0.9.4/python3/examples/asyncio_notifier.py new/pyinotify-0.9.5/python3/examples/asyncio_notifier.py --- old/pyinotify-0.9.4/python3/examples/asyncio_notifier.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pyinotify-0.9.5/python3/examples/asyncio_notifier.py 2014-12-22 16:53:32.000000000 +0100 @@ -0,0 +1,20 @@ +import pyinotify +import asyncio + + +def handle_read_callback(notifier): + """ + Just stop receiving IO read events after the first + iteration (unrealistic example). + """ + print('handle_read callback') + notifier.loop.stop() + + +wm = pyinotify.WatchManager() +loop = asyncio.get_event_loop() +notifier = pyinotify.AsyncioNotifier(wm, loop, + callback=handle_read_callback) +wm.add_watch('/tmp', pyinotify.ALL_EVENTS) +loop.run_forever() +notifier.stop() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyinotify-0.9.4/python3/pyinotify.py new/pyinotify-0.9.5/python3/pyinotify.py --- old/pyinotify-0.9.4/python3/pyinotify.py 2012-11-15 19:39:22.000000000 +0100 +++ new/pyinotify-0.9.5/python3/pyinotify.py 2015-01-09 22:13:32.000000000 +0100 @@ -1,7 +1,7 @@ #!/usr/bin/env python # pyinotify.py - python interface to inotify -# Copyright (c) 2005-2011 Sebastien Martini <[email protected]> +# Copyright (c) 2005-2015 Sebastien Martini <[email protected]> # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -92,7 +92,7 @@ __author__ = "[email protected] (Sebastien Martini)" -__version__ = "0.9.4" +__version__ = "0.9.5" # Compatibity mode: set to True to improve compatibility with @@ -200,9 +200,14 @@ def init(self): assert ctypes + + try_libc_name = 'c' + if sys.platform.startswith('freebsd'): + try_libc_name = 'inotify' + libc_name = None try: - libc_name = ctypes.util.find_library('c') + libc_name = ctypes.util.find_library(try_libc_name) except (OSError, IOError): pass # Will attemp to load it with None anyway. @@ -300,22 +305,26 @@ def get_val(self): """ - Gets attribute's value. + Gets attribute's value. Raises OSError if the operation failed. @return: stored value. @rtype: int """ oldv = ctypes.c_int(0) size = ctypes.c_int(ctypes.sizeof(oldv)) - self._inotify_wrapper._sysctl(self._attr, 3, - ctypes.c_voidp(ctypes.addressof(oldv)), - ctypes.addressof(size), - None, 0) + sysctl = self._inotify_wrapper._sysctl + res = sysctl(self._attr, 3, + ctypes.c_voidp(ctypes.addressof(oldv)), + ctypes.addressof(size), + None, 0) + if res == -1: + raise OSError(self._inotify_wrapper.get_errno(), + self._inotify_wrapper.str_errno()) return oldv.value def set_val(self, nval): """ - Sets new attribute's value. + Sets new attribute's value. Raises OSError if the operation failed. @param nval: replaces current value by nval. @type nval: int @@ -324,11 +333,15 @@ sizeo = ctypes.c_int(ctypes.sizeof(oldv)) newv = ctypes.c_int(nval) sizen = ctypes.c_int(ctypes.sizeof(newv)) - self._inotify_wrapper._sysctl(self._attr, 3, - ctypes.c_voidp(ctypes.addressof(oldv)), - ctypes.addressof(sizeo), - ctypes.c_voidp(ctypes.addressof(newv)), - ctypes.addressof(sizen)) + sysctl = self._inotify_wrapper._sysctl + res = sysctl(self._attr, 3, + ctypes.c_voidp(ctypes.addressof(oldv)), + ctypes.addressof(sizeo), + ctypes.c_voidp(ctypes.addressof(newv)), + sizen) + if res == -1: + raise OSError(self._inotify_wrapper.get_errno(), + self._inotify_wrapper.str_errno()) value = property(get_val, set_val) @@ -725,23 +738,28 @@ # Since the directory d2 is new, then everything inside it must # also be new. created_dir_wd = addw_ret.get(created_dir) - if (created_dir_wd is not None) and (created_dir_wd > 0): - for name in os.listdir(created_dir): - inner = os.path.join(created_dir, name) - if self._watch_manager.get_wd(inner) is not None: - continue - # Generate (simulate) creation events for sub- - # directories and files. - if os.path.isfile(inner): - # symlinks are handled as files. - flags = IN_CREATE - elif os.path.isdir(inner): - flags = IN_CREATE | IN_ISDIR - else: - # This path should not be taken. - continue - rawevent = _RawEvent(created_dir_wd, flags, 0, name) - self._notifier.append_event(rawevent) + if ((created_dir_wd is not None) and (created_dir_wd > 0) and + os.path.isdir(created_dir)): + try: + for name in os.listdir(created_dir): + inner = os.path.join(created_dir, name) + if self._watch_manager.get_wd(inner) is not None: + continue + # Generate (simulate) creation events for sub- + # directories and files. + if os.path.isfile(inner): + # symlinks are handled as files. + flags = IN_CREATE + elif os.path.isdir(inner): + flags = IN_CREATE | IN_ISDIR + else: + # This path should not be taken. + continue + rawevent = _RawEvent(created_dir_wd, flags, 0, name) + self._notifier.append_event(rawevent) + except OSError as err: + msg = "process_IN_CREATE, invalid directory: %s" + log.debug(msg % str(err)) return self.process_default(raw_event) def process_IN_MOVED_FROM(self, raw_event): @@ -1276,6 +1294,9 @@ """ while self._eventq: raw_event = self._eventq.popleft() # pop next event + if self._watch_manager.ignore_events: + log.debug("Event ignored: %s" % repr(raw_event)) + continue watch_ = self._watch_manager.get_watch(raw_event.wd) if (watch_ is None) and not (raw_event.mask & IN_Q_OVERFLOW): if not (raw_event.mask & IN_IGNORED): @@ -1350,7 +1371,6 @@ # Register unlink function atexit.register(lambda : os.unlink(pid_file)) - def _sleep(self, ref_time): # Only consider sleeping if read_freq is > 0 if self._read_freq > 0: @@ -1360,7 +1380,6 @@ log.debug('Now sleeping %d seconds', sleep_amount) time.sleep(sleep_amount) - def loop(self, callback=None, daemonize=False, **args): """ Events are read only one time every min(read_freq, timeout) @@ -1407,7 +1426,6 @@ # Close internals self.stop() - def stop(self): """ Close inotify's instance (close its file descriptor). @@ -1416,6 +1434,7 @@ """ self._pollobj.unregister(self._fd) os.close(self._fd) + self._sys_proc_fun = None class ThreadedNotifier(threading.Thread, Notifier): @@ -1562,6 +1581,10 @@ threshold, timeout) ioloop.add_handler(self._fd, self.handle_read, ioloop.READ) + def stop(self): + self.io_loop.remove_handler(self._fd) + Notifier.stop(self) + def handle_read(self, *args, **kwargs): """ See comment in AsyncNotifier. @@ -1573,6 +1596,43 @@ self.handle_read_callback(self) +class AsyncioNotifier(Notifier): + """ + + asyncio/trollius event loop adapter. + + """ + def __init__(self, watch_manager, loop, callback=None, + default_proc_fun=None, read_freq=0, threshold=0, timeout=None): + """ + + See examples/asyncio_notifier.py for an example usage. + + @param loop: asyncio or trollius event loop instance. + @type loop: asyncio.BaseEventLoop or trollius.BaseEventLoop instance. + @param callback: Functor called at the end of each call to handle_read. + Expects to receive the notifier object (self) as + single parameter. + @type callback: callable object or function + + """ + self.loop = loop + self.handle_read_callback = callback + Notifier.__init__(self, watch_manager, default_proc_fun, read_freq, + threshold, timeout) + loop.add_reader(self._fd, self.handle_read) + + def stop(self): + self.loop.remove_reader(self._fd) + Notifier.stop(self) + + def handle_read(self, *args, **kwargs): + self.read_events() + self.process_events() + if self.handle_read_callback is not None: + self.handle_read_callback(self) + + class Watch: """ Represent a watch, i.e. a file or directory being watched. @@ -1633,11 +1693,11 @@ def __init__(self, arg_lst): """ Examples: - ef1 = ExcludeFilter(["^/etc/rc.*", "^/etc/hostname"]) + ef1 = ExcludeFilter(["/etc/rc.*", "/etc/hostname"]) ef2 = ExcludeFilter("/my/path/exclude.lst") Where exclude.lst contains: - ^/etc/rc.* - ^/etc/hostname + /etc/rc.* + /etc/hostname Note: it is not possible to exclude a file if its encapsulating directory is itself watched. See this issue for more details @@ -1724,6 +1784,7 @@ filter for every call to add_watch. @type exclude_filter: callable object """ + self._ignore_events = False self._exclude_filter = exclude_filter self._wmd = {} # watch dict key: watch descriptor, value: watch @@ -2152,6 +2213,15 @@ auto_add=False, do_glob=False, exclude_filter=lambda path: False) + def get_ignore_events(self): + return self._ignore_events + + def set_ignore_events(self, nval): + self._ignore_events = nval + + ignore_events = property(get_ignore_events, set_ignore_events, + "Make watch manager ignoring new events.") + class RawOutputFormat: """ @@ -2239,6 +2309,9 @@ parser.add_option("-a", "--auto_add", action="store_true", dest="auto_add", help="Automatically add watches on new directories") + parser.add_option("-g", "--glob", action="store_true", + dest="glob", + help="Treat paths as globs") parser.add_option("-e", "--events-list", metavar="EVENT[,...]", dest="events_list", help=("A comma-separated list of events to watch for - " @@ -2314,7 +2387,7 @@ log.debug('Start monitoring %s, (press c^c to halt pyinotify)' % path) - wm.add_watch(path, mask, rec=options.recursive, auto_add=options.auto_add) + wm.add_watch(path, mask, rec=options.recursive, auto_add=options.auto_add, do_glob=options.glob) # Loop forever (until sigint signal get caught) notifier.loop(callback=cb_fun) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyinotify-0.9.4/setup.cfg new/pyinotify-0.9.5/setup.cfg --- old/pyinotify-0.9.4/setup.cfg 1970-01-01 01:00:00.000000000 +0100 +++ new/pyinotify-0.9.5/setup.cfg 2015-01-09 22:49:28.000000000 +0100 @@ -0,0 +1,5 @@ +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyinotify-0.9.4/setup.py new/pyinotify-0.9.5/setup.py --- old/pyinotify-0.9.4/setup.py 2012-11-15 19:39:22.000000000 +0100 +++ new/pyinotify-0.9.5/setup.py 2015-01-09 22:11:57.000000000 +0100 @@ -25,7 +25,7 @@ sys.exit(1) # check linux platform -if not platform.startswith('linux'): +if not platform.startswith('linux') and not platform.startswith('freebsd'): sys.stderr.write("inotify is not available on %s\n" % platform) sys.exit(1) @@ -46,6 +46,10 @@ 'Programming Language :: Python :: 3.0', 'Programming Language :: Python :: 3.1', 'Programming Language :: Python :: 3.2', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: Implementation :: CPython', + 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: System :: Filesystems', 'Topic :: System :: Monitoring', @@ -66,9 +70,13 @@ except: return True + try_libc_name = 'c' + if platform.startswith('freebsd'): + try_libc_name = 'inotify' + libc_name = None try: - libc_name = ctypes.util.find_library('c') + libc_name = ctypes.util.find_library(try_libc_name) except: pass # Will attemp to load it with None anyway. @@ -89,13 +97,13 @@ # sources for ext module ext_mod_src = ['common/inotify_syscalls.c'] # dst for ext module - ext_mod.append(distutils.extension.Extension('inotify_syscalls', + ext_mod.append(distutils.extension.Extension('inotify_syscalls', ext_mod_src)) setup( name='pyinotify', - version='0.9.4', + version='0.9.5', description='Linux filesystem events monitoring', author='Sebastien Martini', author_email='[email protected]', -- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
