Skip to content

Recent Developments

seb-m edited this page Dec 20, 2011 · 26 revisions

Changes introduced with Pyinotify 0.9.3

Changes introduced with Pyinotify 0.9.2

  • Added support for systems without ctypes / libc / libc+inotiy. This works by compiling a C file accessing inotify's syscalls (like in pyinotify 0.7.1). However it should be noted, this file is not compiled unless necessary, setup.py try to detect if it needs it or not. As before, in the most common case where ctypes are used to access inotify, pyinotify.py still can be used as a standalone file. For more details see this commit. Notice, currently inotify's system wide variables max_user_instances , max_user_watches, max_queued_events can only be accessed when ctypes are used otherwise there are set to None.
  • Replaced exception UnsupportedLibcVersionError by InotifyBindingNotFoundError. WatchManager.__init__() may raise this exception on error.
  • Improved the mitigation of this issue.

Changes introduced with Pyinotify 0.9.1

  • Added new command line options --version, --raw-format (to disable colorized output).
  • Added new method WatchManager.close() to explicitly close inotify's file descriptor (and delete all watches), see commit.
  • Removed optional argument force_kill for notifier's daemonization, it could be abused, also support new argument pid_file=False to not write the pid into a file, see commit1 and commit2.

Changes introduced with Pyinotify 0.9.0

  • It is possible to stop Notifier instance from its associated event loop's callback function, see this commit.
  • It is safe now to call add_watch() multiple times on the same path without any risk of modifying existing watches, see this commit.
  • Similar events can be coalesced together, see this commit.

Changes introduced with Pyinotify 0.8.9

  • The syntax of exclusion filter files has changed see the related commit and the new syntax exclude.lst. This new way of parsing external patterns is safer.
  • Pyinotify is not licensed anymore under GPL2+, its new license is now the MIT License.
  • This is the first release to support Python 3.

Changes introduced with Pyinotify 0.8.7

A new function was added in order to provide a minimal compatibility mode with Pyinotify 0.7.1. Call this function from your code only if it was developed for the old versions of Pyinotify (<= 0.7.1) (although you should strongly consider upgrading your code, read this link for more details on what has changed). The compatibility mode is set to False by default therefore you have to make an explicit call to activate it (note that once this mode is activated at runtime, it is not possible to disable it).

import pyinotify
#  [... old code ...]
if __name__ == '__main__':
    pyinotify.compatibily_mode()  # Turns on compatibility mode
    #  [... old code ...]

Changes introduced with Pyinotify 0.8.0

Structural modifications

  • pyinotify.py is now a standalone file.
  • The namespace is reduced to pyinotify. Thus, you access everything from pyinotify, For instance accessing max_user_instances attribute :
>>> import pyinotify
>>> pyinotify.max_user_watches
<max_user_watches=524288>
>>> pyinotify.max_user_watches.value
524288
>>> pyinotify.max_user_watches.value = 42
>>> pyinotify.max_user_watches.value
42
  • Likewise, events names are directly accessible at pyinotify's scope eg: pyinotify.IN_MODIFY.
  • Event instances provide a new attribute: an_event.pathname. Example: <Event dir=False mask=0x100 maskname=IN_CREATE name=foo path=/tmp pathname=/tmp/foo wd=1>

Chaining several processing functions together

  • ProcessEvent instances can be chained together, see chain.py.

Debugging and statistics

  • The logging module is used for debugging and logging errors, it can be accessed through pyinotify.log and its level set with log.setLevel(level) (by default the level is set to 20 when pyinotify is imported and is set to 10 when it is executed from command line
import pyinotify
# [...]
# Modify logging level
pyinotify.log.setLevel(10)
# output debug message
pyinotify.log.debug('debug msg2')
  • There is a tiny functionality available to display statistics see new option -s (from command line) and this example stats.py, which has given this output

New method Notifier.loop()

  • Notifier now provides a loop() method (the former pattern while True: ... is now useless), see loop.py.

Policy for reading events

  • select.poll timeout's value is set to None when called from Notifier and ThreadedNotifier.
  • Notifier's constructor accepts read_freq, threshold and timeout as parameters, for specifying at which frequency and when to read new events. read_freq sets the minimal period of time between two read of events (0s by default), sleeping during the rest of time. threshold is the minimal size (0 byte by default) of accumulated events needed before processing the queue (must be used in combination with read_freq, read the docstrings for more details). timeout is the parameter given to select.poll.
# examples:
n = Notifier(wm, read_freq=0, threshold=0, timeout=None)  # values by default read events asap
n = Notifier(wm, read_freq=5)  # read events every 5s
n = Notifier(wm, read_freq=5, threshold=512)  # read events every 5s if size to read is >= 512 bytes

Functionality for helping watch transient files

Daemonize pyinotify

  • Ability to detach and daemonize Notifier instances, see daemon.py
notifier.loop(daemonize=True)  # By default writes its pid to /var/run/<scriptname>.pid

Exclude files from being watched

  • WatchManager and .add_watch support an exclusion path filter: a boolean function can optionally be called each time a new path is about to be watched, if this predicate returns True this path is excluded (i.e. not watched).
  • See how it works through this example exclude.lst and exclude.py.

Various improvements

  • There is a way to delegate initialization to my_init(*kargs) when ProcessEvent's subclass is instantiated. Override this method in your subclass for achieving custom initialization. Moreover, you must instantiate your class with keyworded arguments, see the following example chain.py.
class Log(ProcessEvent):
    def my_init(self, pathname):
        self._fileobj = file(pathname, 'w')
# [...]
notifier = Notifier(wm, Empty(TrackMofications(Log(pathname=f)), msg='outer'))
  • Accepts a New option -e list,of,events,separated,with,commas. It works like this python pyinotify.py -e IN_CREATE,IN_DELETE