inotify functionality is available from the Linux kernel and allows you to register one or more directories for watching, and to simply block and wait for notification events.
[mythcat@desk ~]$ pip3 install inotify --user
...
Successfully installed inotify-0.2.10 nose-1.3.7
Let's test it and see how this can be load it:[mythcat@desk ~]$ python3
Python 3.7.4 (default, Jul 9 2019, 16:32:37)
[GCC 9.1.1 20190503 (Red Hat 9.1.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import inotify
>>> from inotify import *
>>> print(dir(inotify))
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__',
'__path__', '__spec__', '__version__']
>>> import inotify.adapters
>>> print(dir(inotify))
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__',
'__path__', '__spec__', '__version__', 'adapters', 'calls', 'constants', 'library']
>>> print(dir(inotify.adapters))
['EINTR', 'EventTimeoutException', 'Inotify', 'InotifyTree', 'InotifyTrees', 'TerminalEventException',
'_BaseTree', '_DEFAULT_EPOLL_BLOCK_DURATION_S', '_DEFAULT_TERMINAL_EVENTS', '_HEADER_STRUCT_FORMAT',
'_INOTIFY_EVENT', '_IS_DEBUG', '_LOGGER', '_STRUCT_HEADER_LENGTH', '__builtins__', '__cached__', '__doc__',
'__file__', '__loader__', '__name__', '__package__', '__spec__', 'collections', 'inotify', 'logging', 'os',
'select', 'struct', 'time']
>>> import inotify.calls
>>> print(dir(inotify.calls))
['InotifyError', '_LIB', '_LOGGER', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__',
'__name__', '__package__', '__spec__', '_check_nonnegative', '_check_nonzero', '_check_zero', 'ctypes',
'errno', 'inotify', 'inotify_add_watch', 'inotify_init', 'inotify_rm_watch', 'logging']
>>> import inotify.constants
>>> print(dir(inotify.constants))
['IN_ACCESS', 'IN_ALL_EVENTS', 'IN_ATTRIB', 'IN_CLOEXEC', 'IN_CLOSE', 'IN_CLOSE_NOWRITE', 'IN_CLOSE_WRITE',
'IN_CREATE', 'IN_DELETE', 'IN_DELETE_SELF', 'IN_DONT_FOLLOW', 'IN_IGNORED', 'IN_ISDIR', 'IN_MASK_ADD',
'IN_MODIFY', 'IN_MOVE', 'IN_MOVED_FROM', 'IN_MOVED_TO', 'IN_MOVE_SELF', 'IN_NONBLOCK', 'IN_ONESHOT',
'IN_ONLYDIR', 'IN_OPEN', 'IN_Q_OVERFLOW', 'IN_UNMOUNT', 'MASK_LOOKUP', '__builtins__', '__cached__',
'__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
>>> import inotify.library
>>> print(dir(inotify.library))
['_FILEPATH', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__',
'__spec__',
'ctypes', 'instance']
Let's test it with a simple watch issue for a file named test_file.First, let create this file:
[mythcat@desk ~]$ touch /tmp/test_file
[mythcat@desk ~]$ ll /tmp/test_file
-rw-rw-r--. 1 mythcat mythcat 0 Aug 22 13:54 /tmp/test_file
Now, I can use the next script to watch on it:import inotify.adapters
def notif_tmp():
adapter_tmp = inotify.adapters.Inotify()
adapter_tmp.add_watch('/tmp')
with open('/tmp/test_file', 'w'):
pass
events = adapter_tmp.event_gen(yield_nones=False, timeout_s=1)
events = list(events)
print(events)
if __name__ == '__main__':
notif_tmp()
The result is this:$ python3 notif.py
[(_INOTIFY_EVENT(wd=1, mask=2, cookie=0, len=16), ['IN_MODIFY'], '/tmp', 'test_file'),
(_INOTIFY_EVENT(wd=1, mask=32, cookie=0, len=16), ['IN_OPEN'], '/tmp', 'test_file'),
(_INOTIFY_EVENT(wd=1, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], '/tmp', 'test_file')]
If remove this file the result will be this:$ python3 notif.py
[(_INOTIFY_EVENT(wd=1, mask=256, cookie=0, len=16), ['IN_CREATE'], '/tmp', 'test_file'),
(_INOTIFY_EVENT(wd=1, mask=32, cookie=0, len=16), ['IN_OPEN'], '/tmp', 'test_file'),
(_INOTIFY_EVENT(wd=1, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], '/tmp', 'test_file')]
Let's test another example:import inotify.adapters
import logging
_LOGGER = logging.getLogger(__name__)
def test():
i = inotify.adapters.Inotify()
i.add_watch('/tmp')
try:
for event in i.event_gen():
if event is not None:
(header, type_names, path, filename) = event
_LOGGER.info("WD=(%d) MASK=(%d) COOKIE=(%d) LEN=(%d) MASK->NAMES=%s "
"FILENAME=[%s]",
header.wd, header.mask, header.cookie, header.len, type_names,
filename)
print(header, type_names, path, filename)
finally:
i.remove_watch('/tmp')
if __name__ == '__main__':
test()
If you run this and will try to edit with vim editor the /tmp/text.txt file the output will be this:$ python3 notif_002.py
_INOTIFY_EVENT(wd=1, mask=32, cookie=0, len=16) ['IN_OPEN'] /tmp text.txt
_INOTIFY_EVENT(wd=1, mask=256, cookie=0, len=16) ['IN_CREATE'] /tmp .text.txt.swp
_INOTIFY_EVENT(wd=1, mask=32, cookie=0, len=16) ['IN_OPEN'] /tmp .text.txt.swp
...
If you want to use many paths then use this: paths = [
'/tmp',
]
i = Inotify(paths=paths)
This module is not well documented but can be successfully used for certain tasks.