Module pyinotify
[hide private]
[frames] | no frames]

Source Code for Module pyinotify

   1  #!/usr/bin/env python 
   2   
   3  # pyinotify.py - python interface to inotify 
   4  # Copyright (c) 2005-2015 Sebastien Martini <seb@dbzteam.org> 
   5  # 
   6  # Permission is hereby granted, free of charge, to any person obtaining a copy 
   7  # of this software and associated documentation files (the "Software"), to deal 
   8  # in the Software without restriction, including without limitation the rights 
   9  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
  10  # copies of the Software, and to permit persons to whom the Software is 
  11  # furnished to do so, subject to the following conditions: 
  12  # 
  13  # The above copyright notice and this permission notice shall be included in 
  14  # all copies or substantial portions of the Software. 
  15  # 
  16  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
  17  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
  18  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
  19  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
  20  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
  21  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
  22  # THE SOFTWARE. 
  23  """ 
  24  pyinotify 
  25   
  26  @author: Sebastien Martini 
  27  @license: MIT License 
  28  @contact: seb@dbzteam.org 
  29  """ 
30 31 -class PyinotifyError(Exception):
32 """Indicates exceptions raised by a Pyinotify class.""" 33 pass
34
35 36 -class UnsupportedPythonVersionError(PyinotifyError):
37 """ 38 Raised on unsupported Python versions. 39 """
40 - def __init__(self, version):
41 """ 42 @param version: Current Python version 43 @type version: string 44 """ 45 err = 'Python %s is unsupported, requires at least Python 2.4' 46 PyinotifyError.__init__(self, err % version)
47 48 49 # Check Python version 50 import sys 51 if sys.version_info < (2, 4): 52 raise UnsupportedPythonVersionError(sys.version) 53 54 55 # Import directives 56 import threading 57 import os 58 import select 59 import struct 60 import fcntl 61 import errno 62 import termios 63 import array 64 import logging 65 import atexit 66 from collections import deque 67 from datetime import datetime, timedelta 68 import time 69 import re 70 import asyncore 71 import subprocess 72 73 try: 74 from functools import reduce 75 except ImportError: 76 pass # Will fail on Python 2.4 which has reduce() builtin anyway. 77 78 try: 79 from glob import iglob as glob 80 except ImportError: 81 # Python 2.4 does not have glob.iglob(). 82 from glob import glob as glob 83 84 try: 85 import ctypes 86 import ctypes.util 87 except ImportError: 88 ctypes = None 89 90 try: 91 import inotify_syscalls 92 except ImportError: 93 inotify_syscalls = None 94 95 96 __author__ = "seb@dbzteam.org (Sebastien Martini)" 97 98 __version__ = "0.9.6" 99 100 __metaclass__ = type # Use new-style classes by default 101 102 103 # Compatibity mode: set to True to improve compatibility with 104 # Pyinotify 0.7.1. Do not set this variable yourself, call the 105 # function compatibility_mode() instead. 106 COMPATIBILITY_MODE = False
107 108 109 -class InotifyBindingNotFoundError(PyinotifyError):
110 """ 111 Raised when no inotify support couldn't be found. 112 """
113 - def __init__(self):
114 err = "Couldn't find any inotify binding" 115 PyinotifyError.__init__(self, err)
116
117 118 -class INotifyWrapper:
119 """ 120 Abstract class wrapping access to inotify's functions. This is an 121 internal class. 122 """ 123 @staticmethod
124 - def create():
125 # First, try to use ctypes. 126 if ctypes: 127 inotify = _CtypesLibcINotifyWrapper() 128 if inotify.init(): 129 return inotify 130 # Second, see if C extension is compiled. 131 if inotify_syscalls: 132 inotify = _INotifySyscallsWrapper() 133 if inotify.init(): 134 return inotify
135
136 - def get_errno(self):
137 """ 138 Return None is no errno code is available. 139 """ 140 return self._get_errno()
141
142 - def str_errno(self):
143 code = self.get_errno() 144 if code is None: 145 return 'Errno: no errno support' 146 return 'Errno=%s (%s)' % (os.strerror(code), errno.errorcode[code])
147
148 - def inotify_init(self):
149 return self._inotify_init()
150
151 - def inotify_add_watch(self, fd, pathname, mask):
152 # Unicode strings must be encoded to string prior to calling this 153 # method. 154 assert isinstance(pathname, str) 155 return self._inotify_add_watch(fd, pathname, mask)
156
157 - def inotify_rm_watch(self, fd, wd):
158 return self._inotify_rm_watch(fd, wd)
159
160 161 -class _INotifySyscallsWrapper(INotifyWrapper):
162 - def __init__(self):
163 # Stores the last errno value. 164 self._last_errno = None
165
166 - def init(self):
167 assert inotify_syscalls 168 return True
169
170 - def _get_errno(self):
171 return self._last_errno
172
173 - def _inotify_init(self):
174 try: 175 fd = inotify_syscalls.inotify_init() 176 except IOError, err: 177 self._last_errno = err.errno 178 return -1 179 return fd
180
181 - def _inotify_add_watch(self, fd, pathname, mask):
182 try: 183 wd = inotify_syscalls.inotify_add_watch(fd, pathname, mask) 184 except IOError, err: 185 self._last_errno = err.errno 186 return -1 187 return wd
188
189 - def _inotify_rm_watch(self, fd, wd):
190 try: 191 ret = inotify_syscalls.inotify_rm_watch(fd, wd) 192 except IOError, err: 193 self._last_errno = err.errno 194 return -1 195 return ret
196
197 198 -class _CtypesLibcINotifyWrapper(INotifyWrapper):
199 - def __init__(self):
200 self._libc = None 201 self._get_errno_func = None
202
203 - def init(self):
204 assert ctypes 205 206 try_libc_name = 'c' 207 if sys.platform.startswith('freebsd'): 208 try_libc_name = 'inotify' 209 210 libc_name = None 211 try: 212 libc_name = ctypes.util.find_library(try_libc_name) 213 except (OSError, IOError): 214 pass # Will attemp to load it with None anyway. 215 216 if sys.version_info >= (2, 6): 217 self._libc = ctypes.CDLL(libc_name, use_errno=True) 218 self._get_errno_func = ctypes.get_errno 219 else: 220 self._libc = ctypes.CDLL(libc_name) 221 try: 222 location = self._libc.__errno_location 223 location.restype = ctypes.POINTER(ctypes.c_int) 224 self._get_errno_func = lambda: location().contents.value 225 except AttributeError: 226 pass 227 228 # Eventually check that libc has needed inotify bindings. 229 if (not hasattr(self._libc, 'inotify_init') or 230 not hasattr(self._libc, 'inotify_add_watch') or 231 not hasattr(self._libc, 'inotify_rm_watch')): 232 return False 233 234 self._libc.inotify_init.argtypes = [] 235 self._libc.inotify_init.restype = ctypes.c_int 236 self._libc.inotify_add_watch.argtypes = [ctypes.c_int, ctypes.c_char_p, 237 ctypes.c_uint32] 238 self._libc.inotify_add_watch.restype = ctypes.c_int 239 self._libc.inotify_rm_watch.argtypes = [ctypes.c_int, ctypes.c_int] 240 self._libc.inotify_rm_watch.restype = ctypes.c_int 241 return True
242
243 - def _get_errno(self):
244 if self._get_errno_func is not None: 245 return self._get_errno_func() 246 return None
247
248 - def _inotify_init(self):
249 assert self._libc is not None 250 return self._libc.inotify_init()
251
252 - def _inotify_add_watch(self, fd, pathname, mask):
253 assert self._libc is not None 254 pathname = ctypes.create_string_buffer(pathname) 255 return self._libc.inotify_add_watch(fd, pathname, mask)
256
257 - def _inotify_rm_watch(self, fd, wd):
258 assert self._libc is not None 259 return self._libc.inotify_rm_watch(fd, wd)
260
261 262 # Logging 263 -def logger_init():
264 """Initialize logger instance.""" 265 log = logging.getLogger("pyinotify") 266 console_handler = logging.StreamHandler() 267 console_handler.setFormatter( 268 logging.Formatter("[%(asctime)s %(name)s %(levelname)s] %(message)s")) 269 log.addHandler(console_handler) 270 log.setLevel(20) 271 return log
272 273 log = logger_init()
274 275 276 # inotify's variables 277 -class ProcINotify:
278 """ 279 Access (read, write) inotify's variables through /proc/sys/. Note that 280 usually it requires administrator rights to update them. 281 282 Examples: 283 - Read max_queued_events attribute: myvar = max_queued_events.value 284 - Update max_queued_events attribute: max_queued_events.value = 42 285 """
286 - def __init__(self, attr):
287 self._base = "/proc/sys/fs/inotify" 288 self._attr = attr
289
290 - def get_val(self):
291 """ 292 Gets attribute's value. 293 294 @return: stored value. 295 @rtype: int 296 @raise IOError: if corresponding file in /proc/sys cannot be read. 297 """ 298 file_obj = file(os.path.join(self._base, self._attr), 'r') 299 try: 300 val = int(file_obj.readline()) 301 finally: 302 file_obj.close() 303 return val
304
305 - def set_val(self, nval):
306 """ 307 Sets new attribute's value. 308 309 @param nval: replaces current value by nval. 310 @type nval: int 311 @raise IOError: if corresponding file in /proc/sys cannot be written. 312 """ 313 file_obj = file(os.path.join(self._base, self._attr), 'w') 314 try: 315 file_obj.write(str(nval) + '\n') 316 finally: 317 file_obj.close()
318 319 value = property(get_val, set_val) 320
321 - def __repr__(self):
322 return '<%s=%d>' % (self._attr, self.get_val())
323 324 325 # Inotify's variables 326 # 327 # Note: may raise IOError if the corresponding value in /proc/sys 328 # cannot be accessed. 329 # 330 # Examples: 331 # - read: myvar = max_queued_events.value 332 # - update: max_queued_events.value = 42 333 # 334 for attrname in ('max_queued_events', 'max_user_instances', 'max_user_watches'): 335 globals()[attrname] = ProcINotify(attrname)
336 337 338 -class EventsCodes:
339 """ 340 Set of codes corresponding to each kind of events. 341 Some of these flags are used to communicate with inotify, whereas 342 the others are sent to userspace by inotify notifying some events. 343 344 @cvar IN_ACCESS: File was accessed. 345 @type IN_ACCESS: int 346 @cvar IN_MODIFY: File was modified. 347 @type IN_MODIFY: int 348 @cvar IN_ATTRIB: Metadata changed. 349 @type IN_ATTRIB: int 350 @cvar IN_CLOSE_WRITE: Writtable file was closed. 351 @type IN_CLOSE_WRITE: int 352 @cvar IN_CLOSE_NOWRITE: Unwrittable file closed. 353 @type IN_CLOSE_NOWRITE: int 354 @cvar IN_OPEN: File was opened. 355 @type IN_OPEN: int 356 @cvar IN_MOVED_FROM: File was moved from X. 357 @type IN_MOVED_FROM: int 358 @cvar IN_MOVED_TO: File was moved to Y. 359 @type IN_MOVED_TO: int 360 @cvar IN_CREATE: Subfile was created. 361 @type IN_CREATE: int 362 @cvar IN_DELETE: Subfile was deleted. 363 @type IN_DELETE: int 364 @cvar IN_DELETE_SELF: Self (watched item itself) was deleted. 365 @type IN_DELETE_SELF: int 366 @cvar IN_MOVE_SELF: Self (watched item itself) was moved. 367 @type IN_MOVE_SELF: int 368 @cvar IN_UNMOUNT: Backing fs was unmounted. 369 @type IN_UNMOUNT: int 370 @cvar IN_Q_OVERFLOW: Event queued overflowed. 371 @type IN_Q_OVERFLOW: int 372 @cvar IN_IGNORED: File was ignored. 373 @type IN_IGNORED: int 374 @cvar IN_ONLYDIR: only watch the path if it is a directory (new 375 in kernel 2.6.15). 376 @type IN_ONLYDIR: int 377 @cvar IN_DONT_FOLLOW: don't follow a symlink (new in kernel 2.6.15). 378 IN_ONLYDIR we can make sure that we don't watch 379 the target of symlinks. 380 @type IN_DONT_FOLLOW: int 381 @cvar IN_EXCL_UNLINK: Events are not generated for children after they 382 have been unlinked from the watched directory. 383 (new in kernel 2.6.36). 384 @type IN_EXCL_UNLINK: int 385 @cvar IN_MASK_ADD: add to the mask of an already existing watch (new 386 in kernel 2.6.14). 387 @type IN_MASK_ADD: int 388 @cvar IN_ISDIR: Event occurred against dir. 389 @type IN_ISDIR: int 390 @cvar IN_ONESHOT: Only send event once. 391 @type IN_ONESHOT: int 392 @cvar ALL_EVENTS: Alias for considering all of the events. 393 @type ALL_EVENTS: int 394 """ 395 396 # The idea here is 'configuration-as-code' - this way, we get our nice class 397 # constants, but we also get nice human-friendly text mappings to do lookups 398 # against as well, for free: 399 FLAG_COLLECTIONS = {'OP_FLAGS': { 400 'IN_ACCESS' : 0x00000001, # File was accessed 401 'IN_MODIFY' : 0x00000002, # File was modified 402 'IN_ATTRIB' : 0x00000004, # Metadata changed 403 'IN_CLOSE_WRITE' : 0x00000008, # Writable file was closed 404 'IN_CLOSE_NOWRITE' : 0x00000010, # Unwritable file closed 405 'IN_OPEN' : 0x00000020, # File was opened 406 'IN_MOVED_FROM' : 0x00000040, # File was moved from X 407 'IN_MOVED_TO' : 0x00000080, # File was moved to Y 408 'IN_CREATE' : 0x00000100, # Subfile was created 409 'IN_DELETE' : 0x00000200, # Subfile was deleted 410 'IN_DELETE_SELF' : 0x00000400, # Self (watched item itself) 411 # was deleted 412 'IN_MOVE_SELF' : 0x00000800, # Self (watched item itself) was moved 413 }, 414 'EVENT_FLAGS': { 415 'IN_UNMOUNT' : 0x00002000, # Backing fs was unmounted 416 'IN_Q_OVERFLOW' : 0x00004000, # Event queued overflowed 417 'IN_IGNORED' : 0x00008000, # File was ignored 418 }, 419 'SPECIAL_FLAGS': { 420 'IN_ONLYDIR' : 0x01000000, # only watch the path if it is a 421 # directory 422 'IN_DONT_FOLLOW' : 0x02000000, # don't follow a symlink 423 'IN_EXCL_UNLINK' : 0x04000000, # exclude events on unlinked objects 424 'IN_MASK_ADD' : 0x20000000, # add to the mask of an already 425 # existing watch 426 'IN_ISDIR' : 0x40000000, # event occurred against dir 427 'IN_ONESHOT' : 0x80000000, # only send event once 428 }, 429 } 430
431 - def maskname(mask):
432 """ 433 Returns the event name associated to mask. IN_ISDIR is appended to 434 the result when appropriate. Note: only one event is returned, because 435 only one event can be raised at a given time. 436 437 @param mask: mask. 438 @type mask: int 439 @return: event name. 440 @rtype: str 441 """ 442 ms = mask 443 name = '%s' 444 if mask & IN_ISDIR: 445 ms = mask - IN_ISDIR 446 name = '%s|IN_ISDIR' 447 return name % EventsCodes.ALL_VALUES[ms]
448 449 maskname = staticmethod(maskname)
450 451 452 # So let's now turn the configuration into code 453 EventsCodes.ALL_FLAGS = {} 454 EventsCodes.ALL_VALUES = {} 455 for flagc, valc in EventsCodes.FLAG_COLLECTIONS.items(): 456 # Make the collections' members directly accessible through the 457 # class dictionary 458 setattr(EventsCodes, flagc, valc) 459 460 # Collect all the flags under a common umbrella 461 EventsCodes.ALL_FLAGS.update(valc) 462 463 # Make the individual masks accessible as 'constants' at globals() scope 464 # and masknames accessible by values. 465 for name, val in valc.items(): 466 globals()[name] = val 467 EventsCodes.ALL_VALUES[val] = name 468 469 470 # all 'normal' events 471 ALL_EVENTS = reduce(lambda x, y: x | y, EventsCodes.OP_FLAGS.values()) 472 EventsCodes.ALL_FLAGS['ALL_EVENTS'] = ALL_EVENTS 473 EventsCodes.ALL_VALUES[ALL_EVENTS] = 'ALL_EVENTS'
474 475 476 -class _Event:
477 """ 478 Event structure, represent events raised by the system. This 479 is the base class and should be subclassed. 480 481 """
482 - def __init__(self, dict_):
483 """ 484 Attach attributes (contained in dict_) to self. 485 486 @param dict_: Set of attributes. 487 @type dict_: dictionary 488 """ 489 for tpl in dict_.items(): 490 setattr(self, *tpl)
491
492 - def __repr__(self):
493 """ 494 @return: Generic event string representation. 495 @rtype: str 496 """ 497 s = '' 498 for attr, value in sorted(self.__dict__.items(), key=lambda x: x[0]): 499 if attr.startswith('_'): 500 continue 501 if attr == 'mask': 502 value = hex(getattr(self, attr)) 503 elif isinstance(value, basestring) and not value: 504 value = "''" 505 s += ' %s%s%s' % (output_format.field_name(attr), 506 output_format.punctuation('='), 507 output_format.field_value(value)) 508 509 s = '%s%s%s %s' % (output_format.punctuation('<'), 510 output_format.class_name(self.__class__.__name__), 511 s, 512 output_format.punctuation('>')) 513 return s
514
515 - def __str__(self):
516 return repr(self)
517
518 519 -class _RawEvent(_Event):
520 """ 521 Raw event, it contains only the informations provided by the system. 522 It doesn't infer anything. 523 """
524 - def __init__(self, wd, mask, cookie, name):
525 """ 526 @param wd: Watch Descriptor. 527 @type wd: int 528 @param mask: Bitmask of events. 529 @type mask: int 530 @param cookie: Cookie. 531 @type cookie: int 532 @param name: Basename of the file or directory against which the 533 event was raised in case where the watched directory 534 is the parent directory. None if the event was raised 535 on the watched item itself. 536 @type name: string or None 537 """ 538 # Use this variable to cache the result of str(self), this object 539 # is immutable. 540 self._str = None 541 # name: remove trailing '\0' 542 d = {'wd': wd, 543 'mask': mask, 544 'cookie': cookie, 545 'name': name.rstrip('\0')} 546 _Event.__init__(self, d) 547 log.debug(str(self))
548
549 - def __str__(self):
550 if self._str is None: 551 self._str = _Event.__str__(self) 552 return self._str
553
554 555 -class Event(_Event):
556 """ 557 This class contains all the useful informations about the observed 558 event. However, the presence of each field is not guaranteed and 559 depends on the type of event. In effect, some fields are irrelevant 560 for some kind of event (for example 'cookie' is meaningless for 561 IN_CREATE whereas it is mandatory for IN_MOVE_TO). 562 563 The possible fields are: 564 - wd (int): Watch Descriptor. 565 - mask (int): Mask. 566 - maskname (str): Readable event name. 567 - path (str): path of the file or directory being watched. 568 - name (str): Basename of the file or directory against which the 569 event was raised in case where the watched directory 570 is the parent directory. None if the event was raised 571 on the watched item itself. This field is always provided 572 even if the string is ''. 573 - pathname (str): Concatenation of 'path' and 'name'. 574 - src_pathname (str): Only present for IN_MOVED_TO events and only in 575 the case where IN_MOVED_FROM events are watched too. Holds the 576 source pathname from where pathname was moved from. 577 - cookie (int): Cookie. 578 - dir (bool): True if the event was raised against a directory. 579 580 """
581 - def __init__(self, raw):
582 """ 583 Concretely, this is the raw event plus inferred infos. 584 """ 585 _Event.__init__(self, raw) 586 self.maskname = EventsCodes.maskname(self.mask) 587 if COMPATIBILITY_MODE: 588 self.event_name = self.maskname 589 try: 590 if self.name: 591 self.pathname = os.path.abspath(os.path.join(self.path, 592 self.name)) 593 else: 594 self.pathname = os.path.abspath(self.path) 595 except AttributeError, err: 596 # Usually it is not an error some events are perfectly valids 597 # despite the lack of these attributes. 598 log.debug(err)
599
600 601 -class ProcessEventError(PyinotifyError):
602 """ 603 ProcessEventError Exception. Raised on ProcessEvent error. 604 """
605 - def __init__(self, err):
606 """ 607 @param err: Exception error description. 608 @type err: string 609 """ 610 PyinotifyError.__init__(self, err)
611
612 613 -class _ProcessEvent:
614 """ 615 Abstract processing event class. 616 """
617 - def __call__(self, event):
618 """ 619 To behave like a functor the object must be callable. 620 This method is a dispatch method. Its lookup order is: 621 1. process_MASKNAME method 622 2. process_FAMILY_NAME method 623 3. otherwise calls process_default 624 625 @param event: Event to be processed. 626 @type event: Event object 627 @return: By convention when used from the ProcessEvent class: 628 - Returning False or None (default value) means keep on 629 executing next chained functors (see chain.py example). 630 - Returning True instead means do not execute next 631 processing functions. 632 @rtype: bool 633 @raise ProcessEventError: Event object undispatchable, 634 unknown event. 635 """ 636 stripped_mask = event.mask - (event.mask & IN_ISDIR) 637 maskname = EventsCodes.ALL_VALUES.get(stripped_mask) 638 if maskname is None: 639 raise ProcessEventError("Unknown mask 0x%08x" % stripped_mask) 640 641 # 1- look for process_MASKNAME 642 meth = getattr(self, 'process_' + maskname, None) 643 if meth is not None: 644 return meth(event) 645 # 2- look for process_FAMILY_NAME 646 meth = getattr(self, 'process_IN_' + maskname.split('_')[1], None) 647 if meth is not None: 648 return meth(event) 649 # 3- default call method process_default 650 return self.process_default(event)
651
652 - def __repr__(self):
653 return '<%s>' % self.__class__.__name__
654
655 656 -class _SysProcessEvent(_ProcessEvent):
657 """ 658 There is three kind of processing according to each event: 659 660 1. special handling (deletion from internal container, bug, ...). 661 2. default treatment: which is applied to the majority of events. 662 3. IN_ISDIR is never sent alone, he is piggybacked with a standard 663 event, he is not processed as the others events, instead, its 664 value is captured and appropriately aggregated to dst event. 665 """
666 - def __init__(self, wm, notifier):
667 """ 668 669 @param wm: Watch Manager. 670 @type wm: WatchManager instance 671 @param notifier: Notifier. 672 @type notifier: Notifier instance 673 """ 674 self._watch_manager = wm # watch manager 675 self._notifier = notifier # notifier 676 self._mv_cookie = {} # {cookie(int): (src_path(str), date), ...} 677 self._mv = {} # {src_path(str): (dst_path(str), date), ...}
678
679 - def cleanup(self):
680 """ 681 Cleanup (delete) old (>1mn) records contained in self._mv_cookie 682 and self._mv. 683 """ 684 date_cur_ = datetime.now() 685 for seq in [self._mv_cookie, self._mv]: 686 for k in seq.keys(): 687 if (date_cur_ - seq[k][1]) > timedelta(minutes=1): 688 log.debug('Cleanup: deleting entry %s', seq[k][0]) 689 del seq[k]
690
691 - def process_IN_CREATE(self, raw_event):
692 """ 693 If the event affects a directory and the auto_add flag of the 694 targetted watch is set to True, a new watch is added on this 695 new directory, with the same attribute values than those of 696 this watch. 697 """ 698 if raw_event.mask & IN_ISDIR: 699 watch_ = self._watch_manager.get_watch(raw_event.wd) 700 created_dir = os.path.join(watch_.path, raw_event.name) 701 if watch_.auto_add and not watch_.exclude_filter(created_dir): 702 addw = self._watch_manager.add_watch 703 # The newly monitored directory inherits attributes from its 704 # parent directory. 705 addw_ret = addw(created_dir, watch_.mask, 706 proc_fun=watch_.proc_fun, 707 rec=False, auto_add=watch_.auto_add, 708 exclude_filter=watch_.exclude_filter) 709 710 # Trick to handle mkdir -p /d1/d2/t3 where d1 is watched and 711 # d2 and t3 (directory or file) are created. 712 # Since the directory d2 is new, then everything inside it must 713 # also be new. 714 created_dir_wd = addw_ret.get(created_dir) 715 if ((created_dir_wd is not None) and (created_dir_wd > 0) and 716 os.path.isdir(created_dir)): 717 try: 718 for name in os.listdir(created_dir): 719 inner = os.path.join(created_dir, name) 720 if self._watch_manager.get_wd(inner) is not None: 721 continue 722 # Generate (simulate) creation events for sub- 723 # directories and files. 724 if os.path.isfile(inner): 725 # symlinks are handled as files. 726 flags = IN_CREATE 727 elif os.path.isdir(inner): 728 flags = IN_CREATE | IN_ISDIR 729 else: 730 # This path should not be taken. 731 continue 732 rawevent = _RawEvent(created_dir_wd, flags, 0, name) 733 self._notifier.append_event(rawevent) 734 except OSError, err: 735 msg = "process_IN_CREATE, invalid directory %s: %s" 736 log.debug(msg % (created_dir, str(err))) 737 return self.process_default(raw_event)
738
739 - def process_IN_MOVED_FROM(self, raw_event):
740 """ 741 Map the cookie with the source path (+ date for cleaning). 742 """ 743 watch_ = self._watch_manager.get_watch(raw_event.wd) 744 path_ = watch_.path 745 src_path = os.path.normpath(os.path.join(path_, raw_event.name)) 746 self._mv_cookie[raw_event.cookie] = (src_path, datetime.now()) 747 return self.process_default(raw_event, {'cookie': raw_event.cookie})
748
749 - def process_IN_MOVED_TO(self, raw_event):
750 """ 751 Map the source path with the destination path (+ date for 752 cleaning). 753 """ 754 watch_ = self._watch_manager.get_watch(raw_event.wd) 755 path_ = watch_.path 756 dst_path = os.path.normpath(os.path.join(path_, raw_event.name)) 757 mv_ = self._mv_cookie.get(raw_event.cookie) 758 to_append = {'cookie': raw_event.cookie} 759 if mv_ is not None: 760 self._mv[mv_[0]] = (dst_path, datetime.now()) 761 # Let's assume that IN_MOVED_FROM event is always queued before 762 # that its associated (they share a common cookie) IN_MOVED_TO 763 # event is queued itself. It is then possible in that scenario 764 # to provide as additional information to the IN_MOVED_TO event 765 # the original pathname of the moved file/directory. 766 to_append['src_pathname'] = mv_[0] 767 elif (raw_event.mask & IN_ISDIR and watch_.auto_add and 768 not watch_.exclude_filter(dst_path)): 769 # We got a diretory that's "moved in" from an unknown source and 770 # auto_add is enabled. Manually add watches to the inner subtrees. 771 # The newly monitored directory inherits attributes from its 772 # parent directory. 773 self._watch_manager.add_watch(dst_path, watch_.mask, 774 proc_fun=watch_.proc_fun, 775 rec=True, auto_add=True, 776 exclude_filter=watch_.exclude_filter) 777 return self.process_default(raw_event, to_append)
778
779 - def process_IN_MOVE_SELF(self, raw_event):
780 """ 781 STATUS: the following bug has been fixed in recent kernels (FIXME: 782 which version ?). Now it raises IN_DELETE_SELF instead. 783 784 Old kernels were bugged, this event raised when the watched item 785 were moved, so we had to update its path, but under some circumstances 786 it was impossible: if its parent directory and its destination 787 directory wasn't watched. The kernel (see include/linux/fsnotify.h) 788 doesn't bring us enough informations like the destination path of 789 moved items. 790 """ 791 watch_ = self._watch_manager.get_watch(raw_event.wd) 792 src_path = watch_.path 793 mv_ = self._mv.get(src_path) 794 if mv_: 795 dest_path = mv_[0] 796 watch_.path = dest_path 797 # add the separator to the source path to avoid overlapping 798 # path issue when testing with startswith() 799 src_path += os.path.sep 800 src_path_len = len(src_path) 801 # The next loop renames all watches with src_path as base path. 802 # It seems that IN_MOVE_SELF does not provide IN_ISDIR information 803 # therefore the next loop is iterated even if raw_event is a file. 804 for w in self._watch_manager.watches.values(): 805 if w.path.startswith(src_path): 806 # Note that dest_path is a normalized path. 807 w.path = os.path.join(dest_path, w.path[src_path_len:]) 808 else: 809 log.error("The pathname '%s' of this watch %s has probably changed " 810 "and couldn't be updated, so it cannot be trusted " 811 "anymore. To fix this error move directories/files only " 812 "between watched parents directories, in this case e.g. " 813 "put a watch on '%s'.", 814 watch_.path, watch_, 815 os.path.normpath(os.path.join(watch_.path, 816 os.path.pardir))) 817 if not watch_.path.endswith('-unknown-path'): 818 watch_.path += '-unknown-path' 819 return self.process_default(raw_event)
820
821 - def process_IN_Q_OVERFLOW(self, raw_event):
822 """ 823 Only signal an overflow, most of the common flags are irrelevant 824 for this event (path, wd, name). 825 """ 826 return Event({'mask': raw_event.mask})
827
828 - def process_IN_IGNORED(self, raw_event):
829 """ 830 The watch descriptor raised by this event is now ignored (forever), 831 it can be safely deleted from the watch manager dictionary. 832 After this event we can be sure that neither the event queue nor 833 the system will raise an event associated to this wd again. 834 """ 835 event_ = self.process_default(raw_event) 836 self._watch_manager.del_watch(raw_event.wd) 837 return event_
838
839 - def process_default(self, raw_event, to_append=None):
840 """ 841 Commons handling for the followings events: 842 843 IN_ACCESS, IN_MODIFY, IN_ATTRIB, IN_CLOSE_WRITE, IN_CLOSE_NOWRITE, 844 IN_OPEN, IN_DELETE, IN_DELETE_SELF, IN_UNMOUNT. 845 """ 846 watch_ = self._watch_manager.get_watch(raw_event.wd) 847 if raw_event.mask & (IN_DELETE_SELF | IN_MOVE_SELF): 848 # Unfornulately this information is not provided by the kernel 849 dir_ = watch_.dir 850 else: 851 dir_ = bool(raw_event.mask & IN_ISDIR) 852 dict_ = {'wd': raw_event.wd, 853 'mask': raw_event.mask, 854 'path': watch_.path, 855 'name': raw_event.name, 856 'dir': dir_} 857 if COMPATIBILITY_MODE: 858 dict_['is_dir'] = dir_ 859 if to_append is not None: 860 dict_.update(to_append) 861 return Event(dict_)
862
863 864 -class ProcessEvent(_ProcessEvent):
865 """ 866 Process events objects, can be specialized via subclassing, thus its 867 behavior can be overriden: 868 869 Note: you should not override __init__ in your subclass instead define 870 a my_init() method, this method will be called automatically from the 871 constructor of this class with its optionals parameters. 872 873 1. Provide specialized individual methods, e.g. process_IN_DELETE for 874 processing a precise type of event (e.g. IN_DELETE in this case). 875 2. Or/and provide methods for processing events by 'family', e.g. 876 process_IN_CLOSE method will process both IN_CLOSE_WRITE and 877 IN_CLOSE_NOWRITE events (if process_IN_CLOSE_WRITE and 878 process_IN_CLOSE_NOWRITE aren't defined though). 879 3. Or/and override process_default for catching and processing all 880 the remaining types of events. 881 """ 882 pevent = None 883
884 - def __init__(self, pevent=None, **kargs):
885 """ 886 Enable chaining of ProcessEvent instances. 887 888 @param pevent: Optional callable object, will be called on event 889 processing (before self). 890 @type pevent: callable 891 @param kargs: This constructor is implemented as a template method 892 delegating its optionals keyworded arguments to the 893 method my_init(). 894 @type kargs: dict 895 """ 896 self.pevent = pevent 897 self.my_init(**kargs)
898
899 - def my_init(self, **kargs):
900 """ 901 This method is called from ProcessEvent.__init__(). This method is 902 empty here and must be redefined to be useful. In effect, if you 903 need to specifically initialize your subclass' instance then you 904 just have to override this method in your subclass. Then all the 905 keyworded arguments passed to ProcessEvent.__init__() will be 906 transmitted as parameters to this method. Beware you MUST pass 907 keyword arguments though. 908 909 @param kargs: optional delegated arguments from __init__(). 910 @type kargs: dict 911 """ 912 pass
913
914 - def __call__(self, event):
915 stop_chaining = False 916 if self.pevent is not None: 917 # By default methods return None so we set as guideline 918 # that methods asking for stop chaining must explicitely 919 # return non None or non False values, otherwise the default 920 # behavior will be to accept chain call to the corresponding 921 # local method. 922 stop_chaining = self.pevent(event) 923 if not stop_chaining: 924 return _ProcessEvent.__call__(self, event)
925
926 - def nested_pevent(self):
927 return self.pevent
928
929 - def process_IN_Q_OVERFLOW(self, event):
930 """ 931 By default this method only reports warning messages, you can overredide 932 it by subclassing ProcessEvent and implement your own 933 process_IN_Q_OVERFLOW method. The actions you can take on receiving this 934 event is either to update the variable max_queued_events in order to 935 handle more simultaneous events or to modify your code in order to 936 accomplish a better filtering diminishing the number of raised events. 937 Because this method is defined, IN_Q_OVERFLOW will never get 938 transmitted as arguments to process_default calls. 939 940 @param event: IN_Q_OVERFLOW event. 941 @type event: dict 942 """ 943 log.warning('Event queue overflowed.')
944
945 - def process_default(self, event):
946 """ 947 Default processing event method. By default does nothing. Subclass 948 ProcessEvent and redefine this method in order to modify its behavior. 949 950 @param event: Event to be processed. Can be of any type of events but 951 IN_Q_OVERFLOW events (see method process_IN_Q_OVERFLOW). 952 @type event: Event instance 953 """ 954 pass
955
956 957 -class PrintAllEvents(ProcessEvent):
958 """ 959 Dummy class used to print events strings representations. For instance this 960 class is used from command line to print all received events to stdout. 961 """
962 - def my_init(self, out=None):
963 """ 964 @param out: Where events will be written. 965 @type out: Object providing a valid file object interface. 966 """ 967 if out is None: 968 out = sys.stdout 969 self._out = out
970
971 - def process_default(self, event):
972 """ 973 Writes event string representation to file object provided to 974 my_init(). 975 976 @param event: Event to be processed. Can be of any type of events but 977 IN_Q_OVERFLOW events (see method process_IN_Q_OVERFLOW). 978 @type event: Event instance 979 """ 980 self._out.write(str(event)) 981 self._out.write('\n') 982 self._out.flush()
983
984 985 -class ChainIfTrue(ProcessEvent):
986 """ 987 Makes conditional chaining depending on the result of the nested 988 processing instance. 989 """
990 - def my_init(self, func):
991 """ 992 Method automatically called from base class constructor. 993 """ 994 self._func = func
995
996 - def process_default(self, event):
997 return not self._func(event)
998
999 1000 -class Stats(ProcessEvent):
1001 """ 1002 Compute and display trivial statistics about processed events. 1003 """
1004 - def my_init(self):
1005 """ 1006 Method automatically called from base class constructor. 1007 """ 1008 self._start_time = time.time() 1009 self._stats = {} 1010 self._stats_lock = threading.Lock()
1011
1012 - def process_default(self, event):
1013 """ 1014 Processes |event|. 1015 """ 1016 self._stats_lock.acquire() 1017 try: 1018 events = event.maskname.split('|') 1019 for event_name in events: 1020 count = self._stats.get(event_name, 0) 1021 self._stats[event_name] = count + 1 1022 finally: 1023 self._stats_lock.release()
1024
1025 - def _stats_copy(self):
1026 self._stats_lock.acquire() 1027 try: 1028 return self._stats.copy() 1029 finally: 1030 self._stats_lock.release()
1031
1032 - def __repr__(self):
1033 stats = self._stats_copy() 1034 1035 elapsed = int(time.time() - self._start_time) 1036 elapsed_str = '' 1037 if elapsed < 60: 1038 elapsed_str = str(elapsed) + 'sec' 1039 elif 60 <= elapsed < 3600: 1040 elapsed_str = '%dmn%dsec' % (elapsed / 60, elapsed % 60) 1041 elif 3600 <= elapsed < 86400: 1042 elapsed_str = '%dh%dmn' % (elapsed / 3600, (elapsed % 3600) / 60) 1043 elif elapsed >= 86400: 1044 elapsed_str = '%dd%dh' % (elapsed / 86400, (elapsed % 86400) / 3600) 1045 stats['ElapsedTime'] = elapsed_str 1046 1047 l = [] 1048 for ev, value in sorted(stats.items(), key=lambda x: x[0]): 1049 l.append(' %s=%s' % (output_format.field_name(ev), 1050 output_format.field_value(value))) 1051 s = '<%s%s >' % (output_format.class_name(self.__class__.__name__), 1052 ''.join(l)) 1053 return s
1054
1055 - def dump(self, filename):
1056 """ 1057 Dumps statistics. 1058 1059 @param filename: filename where stats will be dumped, filename is 1060 created and must not exist prior to this call. 1061 @type filename: string 1062 """ 1063 flags = os.O_WRONLY|os.O_CREAT|os.O_NOFOLLOW|os.O_EXCL 1064 fd = os.open(filename, flags, 0600) 1065 os.write(fd, str(self)) 1066 os.close(fd)
1067
1068 - def __str__(self, scale=45):
1069 stats = self._stats_copy() 1070 if not stats: 1071 return '' 1072 1073 m = max(stats.values()) 1074 unity = float(scale) / m 1075 fmt = '%%-26s%%-%ds%%s' % (len(output_format.field_value('@' * scale)) 1076 + 1) 1077 def func(x): 1078 return fmt % (output_format.field_name(x[0]), 1079 output_format.field_value('@' * int(x[1] * unity)), 1080 output_format.simple('%d' % x[1], 'yellow'))
1081 s = '\n'.join(map(func, sorted(stats.items(), key=lambda x: x[0]))) 1082 return s
1083
1084 1085 -class NotifierError(PyinotifyError):
1086 """ 1087 Notifier Exception. Raised on Notifier error. 1088 1089 """
1090 - def __init__(self, err):
1091 """ 1092 @param err: Exception string's description. 1093 @type err: string 1094 """ 1095 PyinotifyError.__init__(self, err)
1096
1097 1098 -class Notifier:
1099 """ 1100 Read notifications, process events. 1101 1102 """
1103 - def __init__(self, watch_manager, default_proc_fun=None, read_freq=0, 1104 threshold=0, timeout=None):
1105 """ 1106 Initialization. read_freq, threshold and timeout parameters are used 1107 when looping. 1108 1109 @param watch_manager: Watch Manager. 1110 @type watch_manager: WatchManager instance 1111 @param default_proc_fun: Default processing method. If None, a new 1112 instance of PrintAllEvents will be assigned. 1113 @type default_proc_fun: instance of ProcessEvent 1114 @param read_freq: if read_freq == 0, events are read asap, 1115 if read_freq is > 0, this thread sleeps 1116 max(0, read_freq - (timeout / 1000)) seconds. But 1117 if timeout is None it may be different because 1118 poll is blocking waiting for something to read. 1119 @type read_freq: int 1120 @param threshold: File descriptor will be read only if the accumulated 1121 size to read becomes >= threshold. If != 0, you likely 1122 want to use it in combination with an appropriate 1123 value for read_freq because without that you would 1124 keep looping without really reading anything and that 1125 until the amount of events to read is >= threshold. 1126 At least with read_freq set you might sleep. 1127 @type threshold: int 1128 @param timeout: see read_freq above. If provided, it must be set in 1129 milliseconds. See 1130 https://docs.python.org/2/library/select.html#polling-objects 1131 @type timeout: int 1132 """ 1133 # Watch Manager instance 1134 self._watch_manager = watch_manager 1135 # File descriptor 1136 self._fd = self._watch_manager.get_fd() 1137 # Poll object and registration 1138 self._pollobj = select.poll() 1139 self._pollobj.register(self._fd, select.POLLIN) 1140 # This pipe is correctely initialized and used by ThreadedNotifier 1141 self._pipe = (-1, -1) 1142 # Event queue 1143 self._eventq = deque() 1144 # System processing functor, common to all events 1145 self._sys_proc_fun = _SysProcessEvent(self._watch_manager, self) 1146 # Default processing method 1147 self._default_proc_fun = default_proc_fun 1148 if default_proc_fun is None: 1149 self._default_proc_fun = PrintAllEvents() 1150 # Loop parameters 1151 self._read_freq = read_freq 1152 self._threshold = threshold 1153 self._timeout = timeout 1154 # Coalesce events option 1155 self._coalesce = False 1156 # set of str(raw_event), only used when coalesce option is True 1157 self._eventset = set()
1158
1159 - def append_event(self, event):
1160 """ 1161 Append a raw event to the event queue. 1162 1163 @param event: An event. 1164 @type event: _RawEvent instance. 1165 """ 1166 self._eventq.append(event)
1167
1168 - def proc_fun(self):
1169 return self._default_proc_fun
1170
1171 - def coalesce_events(self, coalesce=True):
1172 """ 1173 Coalescing events. Events are usually processed by batchs, their size 1174 depend on various factors. Thus, before processing them, events received 1175 from inotify are aggregated in a fifo queue. If this coalescing 1176 option is enabled events are filtered based on their unicity, only 1177 unique events are enqueued, doublons are discarded. An event is unique 1178 when the combination of its fields (wd, mask, cookie, name) is unique 1179 among events of a same batch. After a batch of events is processed any 1180 events is accepted again. By default this option is disabled, you have 1181 to explictly call this function to turn it on. 1182 1183 @param coalesce: Optional new coalescing value. True by default. 1184 @type coalesce: Bool 1185 """ 1186 self._coalesce = coalesce 1187 if not coalesce: 1188 self._eventset.clear()
1189
1190 - def check_events(self, timeout=None):
1191 """ 1192 Check for new events available to read, blocks up to timeout 1193 milliseconds. 1194 1195 @param timeout: If specified it overrides the corresponding instance 1196 attribute _timeout. timeout must be sepcified in 1197 milliseconds. 1198 @type timeout: int 1199 1200 @return: New events to read. 1201 @rtype: bool 1202 """ 1203 while True: 1204 try: 1205 # blocks up to 'timeout' milliseconds 1206 if timeout is None: 1207 timeout = self._timeout 1208 ret = self._pollobj.poll(timeout) 1209 except select.error, err: 1210 if err[0] == errno.EINTR: 1211 continue # interrupted, retry 1212 else: 1213 raise 1214 else: 1215 break 1216 1217 if not ret or (self._pipe[0] == ret[0][0]): 1218 return False 1219 # only one fd is polled 1220 return ret[0][1] & select.POLLIN
1221
1222 - def read_events(self):
1223 """ 1224 Read events from device, build _RawEvents, and enqueue them. 1225 """ 1226 buf_ = array.array('i', [0]) 1227 # get event queue size 1228 if fcntl.ioctl(self._fd, termios.FIONREAD, buf_, 1) == -1: 1229 return 1230 queue_size = buf_[0] 1231 if queue_size < self._threshold: 1232 log.debug('(fd: %d) %d bytes available to read but threshold is ' 1233 'fixed to %d bytes', self._fd, queue_size, 1234 self._threshold) 1235 return 1236 1237 try: 1238 # Read content from file 1239 r = os.read(self._fd, queue_size) 1240 except Exception, msg: 1241 raise NotifierError(msg) 1242 log.debug('Event queue size: %d', queue_size) 1243 rsum = 0 # counter 1244 while rsum < queue_size: 1245 s_size = 16 1246 # Retrieve wd, mask, cookie and fname_len 1247 wd, mask, cookie, fname_len = struct.unpack('iIII', 1248 r[rsum:rsum+s_size]) 1249 # Retrieve name 1250 fname, = struct.unpack('%ds' % fname_len, 1251 r[rsum + s_size:rsum + s_size + fname_len]) 1252 rawevent = _RawEvent(wd, mask, cookie, fname) 1253 if self._coalesce: 1254 # Only enqueue new (unique) events. 1255 raweventstr = str(rawevent) 1256 if raweventstr not in self._eventset: 1257 self._eventset.add(raweventstr) 1258 self._eventq.append(rawevent) 1259 else: 1260 self._eventq.append(rawevent) 1261 rsum += s_size + fname_len
1262
1263 - def process_events(self):
1264 """ 1265 Routine for processing events from queue by calling their 1266 associated proccessing method (an instance of ProcessEvent). 1267 It also does internal processings, to keep the system updated. 1268 """ 1269 while self._eventq: 1270 raw_event = self._eventq.popleft() # pop next event 1271 if self._watch_manager.ignore_events: 1272 log.debug("Event ignored: %s" % repr(raw_event)) 1273 continue 1274 watch_ = self._watch_manager.get_watch(raw_event.wd) 1275 if (watch_ is None) and not (raw_event.mask & IN_Q_OVERFLOW): 1276 if not (raw_event.mask & IN_IGNORED): 1277 # Not really sure how we ended up here, nor how we should 1278 # handle these types of events and if it is appropriate to 1279 # completly skip them (like we are doing here). 1280 log.warning("Unable to retrieve Watch object associated to %s", 1281 repr(raw_event)) 1282 continue 1283 revent = self._sys_proc_fun(raw_event) # system processings 1284 if watch_ and watch_.proc_fun: 1285 watch_.proc_fun(revent) # user processings 1286 else: 1287 self._default_proc_fun(revent) 1288 self._sys_proc_fun.cleanup() # remove olds MOVED_* events records 1289 if self._coalesce: 1290 self._eventset.clear()
1291
1292 - def __daemonize(self, pid_file=None, stdin=os.devnull, stdout=os.devnull, 1293 stderr=os.devnull):
1294 """ 1295 @param pid_file: file where the pid will be written. If pid_file=None 1296 the pid is written to 1297 /var/run/<sys.argv[0]|pyinotify>.pid, if pid_file=False 1298 no pid_file is written. 1299 @param stdin: 1300 @param stdout: 1301 @param stderr: files associated to common streams. 1302 """ 1303 if pid_file is None: 1304 dirname = '/var/run/' 1305 basename = os.path.basename(sys.argv[0]) or 'pyinotify' 1306 pid_file = os.path.join(dirname, basename + '.pid') 1307 1308 if pid_file != False and os.path.lexists(pid_file): 1309 err = 'Cannot daemonize: pid file %s already exists.' % pid_file 1310 raise NotifierError(err) 1311 1312 def fork_daemon(): 1313 # Adapted from Chad J. Schroeder's recipe 1314 # @see http://code.activestate.com/recipes/278731/ 1315 pid = os.fork() 1316 if (pid == 0): 1317 # parent 2 1318 os.setsid() 1319 pid = os.fork() 1320 if (pid == 0): 1321 # child 1322 os.chdir('/') 1323 os.umask(022) 1324 else: 1325 # parent 2 1326 os._exit(0) 1327 else: 1328 # parent 1 1329 os._exit(0) 1330 1331 fd_inp = os.open(stdin, os.O_RDONLY) 1332 os.dup2(fd_inp, 0) 1333 fd_out = os.open(stdout, os.O_WRONLY|os.O_CREAT, 0600) 1334 os.dup2(fd_out, 1) 1335 fd_err = os.open(stderr, os.O_WRONLY|os.O_CREAT, 0600) 1336 os.dup2(fd_err, 2)
1337 1338 # Detach task 1339 fork_daemon() 1340 1341 # Write pid 1342 if pid_file != False: 1343 flags = os.O_WRONLY|os.O_CREAT|os.O_NOFOLLOW|os.O_EXCL 1344 fd_pid = os.open(pid_file, flags, 0600) 1345 os.write(fd_pid, str(os.getpid()) + '\n') 1346 os.close(fd_pid) 1347 # Register unlink function 1348 atexit.register(lambda : os.unlink(pid_file))
1349
1350 - def _sleep(self, ref_time):
1351 # Only consider sleeping if read_freq is > 0 1352 if self._read_freq > 0: 1353 cur_time = time.time() 1354 sleep_amount = self._read_freq - (cur_time - ref_time) 1355 if sleep_amount > 0: 1356 log.debug('Now sleeping %d seconds', sleep_amount) 1357 time.sleep(sleep_amount)
1358
1359 - def loop(self, callback=None, daemonize=False, **args):
1360 """ 1361 Events are read only one time every min(read_freq, timeout) 1362 seconds at best and only if the size to read is >= threshold. 1363 After this method returns it must not be called again for the same 1364 instance. 1365 1366 @param callback: Functor called after each event processing iteration. 1367 Expects to receive the notifier object (self) as first 1368 parameter. If this function returns True the loop is 1369 immediately terminated otherwise the loop method keeps 1370 looping. 1371 @type callback: callable object or function 1372 @param daemonize: This thread is daemonized if set to True. 1373 @type daemonize: boolean 1374 @param args: Optional and relevant only if daemonize is True. Remaining 1375 keyworded arguments are directly passed to daemonize see 1376 __daemonize() method. If pid_file=None or is set to a 1377 pathname the caller must ensure the file does not exist 1378 before this method is called otherwise an exception 1379 pyinotify.NotifierError will be raised. If pid_file=False 1380 it is still daemonized but the pid is not written in any 1381 file. 1382 @type args: various 1383 """ 1384 if daemonize: 1385 self.__daemonize(**args) 1386 1387 # Read and process events forever 1388 while 1: 1389 try: 1390 self.process_events() 1391 if (callback is not None) and (callback(self) is True): 1392 break 1393 ref_time = time.time() 1394 # check_events is blocking 1395 if self.check_events(): 1396 self._sleep(ref_time) 1397 self.read_events() 1398 except KeyboardInterrupt: 1399 # Stop monitoring if sigint is caught (Control-C). 1400 log.debug('Pyinotify stops monitoring.') 1401 break 1402 # Close internals 1403 self.stop()
1404
1405 - def stop(self):
1406 """ 1407 Close inotify's instance (close its file descriptor). 1408 It destroys all existing watches, pending events,... 1409 This method is automatically called at the end of loop(). 1410 Afterward it is invalid to access this instance. 1411 """ 1412 if self._fd is not None: 1413 self._pollobj.unregister(self._fd) 1414 os.close(self._fd) 1415 self._fd = None 1416 self._sys_proc_fun = None
1417
1418 1419 -class ThreadedNotifier(threading.Thread, Notifier):
1420 """ 1421 This notifier inherits from threading.Thread for instanciating a separate 1422 thread, and also inherits from Notifier, because it is a threaded notifier. 1423 1424 Note that every functionality provided by this class is also provided 1425 through Notifier class. Moreover Notifier should be considered first because 1426 it is not threaded and could be easily daemonized. 1427 """
1428 - def __init__(self, watch_manager, default_proc_fun=None, read_freq=0, 1429 threshold=0, timeout=None):
1430 """ 1431 Initialization, initialize base classes. read_freq, threshold and 1432 timeout parameters are used when looping. 1433 1434 @param watch_manager: Watch Manager. 1435 @type watch_manager: WatchManager instance 1436 @param default_proc_fun: Default processing method. See base class. 1437 @type default_proc_fun: instance of ProcessEvent 1438 @param read_freq: if read_freq == 0, events are read asap, 1439 if read_freq is > 0, this thread sleeps 1440 max(0, read_freq - (timeout / 1000)) seconds. 1441 @type read_freq: int 1442 @param threshold: File descriptor will be read only if the accumulated 1443 size to read becomes >= threshold. If != 0, you likely 1444 want to use it in combination with an appropriate 1445 value set for read_freq because without that you would 1446 keep looping without really reading anything and that 1447 until the amount of events to read is >= threshold. At 1448 least with read_freq you might sleep. 1449 @type threshold: int 1450 @param timeout: see read_freq above. If provided, it must be set in 1451 milliseconds. See 1452 https://docs.python.org/2/library/select.html#select.poll.poll 1453 @type timeout: int 1454 """ 1455 # Init threading base class 1456 threading.Thread.__init__(self) 1457 # Stop condition 1458 self._stop_event = threading.Event() 1459 # Init Notifier base class 1460 Notifier.__init__(self, watch_manager, default_proc_fun, read_freq, 1461 threshold, timeout) 1462 # Create a new pipe used for thread termination 1463 self._pipe = os.pipe() 1464 self._pollobj.register(self._pipe[0], select.POLLIN)
1465
1466 - def stop(self):
1467 """ 1468 Stop notifier's loop. Stop notification. Join the thread. 1469 """ 1470 self._stop_event.set() 1471 os.write(self._pipe[1], 'stop') 1472 threading.Thread.join(self) 1473 Notifier.stop(self) 1474 self._pollobj.unregister(self._pipe[0]) 1475 os.close(self._pipe[0]) 1476 os.close(self._pipe[1])
1477
1478 - def loop(self):
1479 """ 1480 Thread's main loop. Don't meant to be called by user directly. 1481 Call inherited start() method instead. 1482 1483 Events are read only once time every min(read_freq, timeout) 1484 seconds at best and only if the size of events to read is >= threshold. 1485 """ 1486 # When the loop must be terminated .stop() is called, 'stop' 1487 # is written to pipe fd so poll() returns and .check_events() 1488 # returns False which make evaluate the While's stop condition 1489 # ._stop_event.isSet() wich put an end to the thread's execution. 1490 while not self._stop_event.isSet(): 1491 self.process_events() 1492 ref_time = time.time() 1493 if self.check_events(): 1494 self._sleep(ref_time) 1495 self.read_events()
1496
1497 - def run(self):
1498 """ 1499 Start thread's loop: read and process events until the method 1500 stop() is called. 1501 Never call this method directly, instead call the start() method 1502 inherited from threading.Thread, which then will call run() in 1503 its turn. 1504 """ 1505 self.loop()
1506
1507 1508 -class AsyncNotifier(asyncore.file_dispatcher, Notifier):
1509 """ 1510 This notifier inherits from asyncore.file_dispatcher in order to be able to 1511 use pyinotify along with the asyncore framework. 1512 1513 """
1514 - def __init__(self, watch_manager, default_proc_fun=None, read_freq=0, 1515 threshold=0, timeout=None, channel_map=None):
1516 """ 1517 Initializes the async notifier. The only additional parameter is 1518 'channel_map' which is the optional asyncore private map. See 1519 Notifier class for the meaning of the others parameters. 1520 1521 """ 1522 Notifier.__init__(self, watch_manager, default_proc_fun, read_freq, 1523 threshold, timeout) 1524 asyncore.file_dispatcher.__init__(self, self._fd, channel_map)
1525
1526 - def handle_read(self):
1527 """ 1528 When asyncore tells us we can read from the fd, we proceed processing 1529 events. This method can be overridden for handling a notification 1530 differently. 1531 1532 """ 1533 self.read_events() 1534 self.process_events()
1535
1536 1537 -class TornadoAsyncNotifier(Notifier):
1538 """ 1539 Tornado ioloop adapter. 1540 1541 """
1542 - def __init__(self, watch_manager, ioloop, callback=None, 1543 default_proc_fun=None, read_freq=0, threshold=0, timeout=None, 1544 channel_map=None):
1545 """ 1546 Note that if later you must call ioloop.close() be sure to let the 1547 default parameter to all_fds=False. 1548 1549 See example tornado_notifier.py for an example using this notifier. 1550 1551 @param ioloop: Tornado's IO loop. 1552 @type ioloop: tornado.ioloop.IOLoop instance. 1553 @param callback: Functor called at the end of each call to handle_read 1554 (IOLoop's read handler). Expects to receive the 1555 notifier object (self) as single parameter. 1556 @type callback: callable object or function 1557 """ 1558 self.io_loop = ioloop 1559 self.handle_read_callback = callback 1560 Notifier.__init__(self, watch_manager, default_proc_fun, read_freq, 1561 threshold, timeout) 1562 ioloop.add_handler(self._fd, self.handle_read, ioloop.READ)
1563
1564 - def stop(self):
1565 self.io_loop.remove_handler(self._fd) 1566 Notifier.stop(self)
1567
1568 - def handle_read(self, *args, **kwargs):
1569 """ 1570 See comment in AsyncNotifier. 1571 1572 """ 1573 self.read_events() 1574 self.process_events() 1575 if self.handle_read_callback is not None: 1576 self.handle_read_callback(self)
1577
1578 1579 -class AsyncioNotifier(Notifier):
1580 """ 1581 1582 asyncio/trollius event loop adapter. 1583 1584 """
1585 - def __init__(self, watch_manager, loop, callback=None, 1586 default_proc_fun=None, read_freq=0, threshold=0, timeout=None):
1587 """ 1588 1589 See examples/asyncio_notifier.py for an example usage. 1590 1591 @param loop: asyncio or trollius event loop instance. 1592 @type loop: asyncio.BaseEventLoop or trollius.BaseEventLoop instance. 1593 @param callback: Functor called at the end of each call to handle_read. 1594 Expects to receive the notifier object (self) as 1595 single parameter. 1596 @type callback: callable object or function 1597 1598 """ 1599 self.loop = loop 1600 self.handle_read_callback = callback 1601 Notifier.__init__(self, watch_manager, default_proc_fun, read_freq, 1602 threshold, timeout) 1603 loop.add_reader(self._fd, self.handle_read)
1604
1605 - def stop(self):
1606 self.loop.remove_reader(self._fd) 1607 Notifier.stop(self)
1608
1609 - def handle_read(self, *args, **kwargs):
1610 self.read_events() 1611 self.process_events() 1612 if self.handle_read_callback is not None: 1613 self.handle_read_callback(self)
1614
1615 1616 -class Watch:
1617 """ 1618 Represent a watch, i.e. a file or directory being watched. 1619 1620 """ 1621 __slots__ = ('wd', 'path', 'mask', 'proc_fun', 'auto_add', 1622 'exclude_filter', 'dir') 1623
1624 - def __init__(self, wd, path, mask, proc_fun, auto_add, exclude_filter):
1625 """ 1626 Initializations. 1627 1628 @param wd: Watch descriptor. 1629 @type wd: int 1630 @param path: Path of the file or directory being watched. 1631 @type path: str 1632 @param mask: Mask. 1633 @type mask: int 1634 @param proc_fun: Processing callable object. 1635 @type proc_fun: 1636 @param auto_add: Automatically add watches on new directories. 1637 @type auto_add: bool 1638 @param exclude_filter: Boolean function, used to exclude new 1639 directories from being automatically watched. 1640 See WatchManager.__init__ 1641 @type exclude_filter: callable object 1642 """ 1643 self.wd = wd 1644 self.path = path 1645 self.mask = mask 1646 self.proc_fun = proc_fun 1647 self.auto_add = auto_add 1648 self.exclude_filter = exclude_filter 1649 self.dir = os.path.isdir(self.path)
1650
1651 - def __repr__(self):
1652 """ 1653 @return: String representation. 1654 @rtype: str 1655 """ 1656 s = ' '.join(['%s%s%s' % (output_format.field_name(attr), 1657 output_format.punctuation('='), 1658 output_format.field_value(getattr(self, 1659 attr))) \ 1660 for attr in self.__slots__ if not attr.startswith('_')]) 1661 1662 s = '%s%s %s %s' % (output_format.punctuation('<'), 1663 output_format.class_name(self.__class__.__name__), 1664 s, 1665 output_format.punctuation('>')) 1666 return s
1667
1668 1669 -class ExcludeFilter:
1670 """ 1671 ExcludeFilter is an exclusion filter. 1672 1673 """
1674 - def __init__(self, arg_lst):
1675 """ 1676 Examples: 1677 ef1 = ExcludeFilter(["/etc/rc.*", "/etc/hostname"]) 1678 ef2 = ExcludeFilter("/my/path/exclude.lst") 1679 Where exclude.lst contains: 1680 /etc/rc.* 1681 /etc/hostname 1682 1683 Note: it is not possible to exclude a file if its encapsulating 1684 directory is itself watched. See this issue for more details 1685 https://github.com/seb-m/pyinotify/issues/31 1686 1687 @param arg_lst: is either a list of patterns or a filename from which 1688 patterns will be loaded. 1689 @type arg_lst: list of str or str 1690 """ 1691 if isinstance(arg_lst, str): 1692 lst = self._load_patterns_from_file(arg_lst) 1693 elif isinstance(arg_lst, list): 1694 lst = arg_lst 1695 else: 1696 raise TypeError 1697 1698 self._lregex = [] 1699 for regex in lst: 1700 self._lregex.append(re.compile(regex, re.UNICODE))
1701
1702 - def _load_patterns_from_file(self, filename):
1703 lst = [] 1704 file_obj = file(filename, 'r') 1705 try: 1706 for line in file_obj.readlines(): 1707 # Trim leading an trailing whitespaces 1708 pattern = line.strip() 1709 if not pattern or pattern.startswith('#'): 1710 continue 1711 lst.append(pattern) 1712 finally: 1713 file_obj.close() 1714 return lst
1715
1716 - def _match(self, regex, path):
1717 return regex.match(path) is not None
1718
1719 - def __call__(self, path):
1720 """ 1721 @param path: Path to match against provided regexps. 1722 @type path: str 1723 @return: Return True if path has been matched and should 1724 be excluded, False otherwise. 1725 @rtype: bool 1726 """ 1727 for regex in self._lregex: 1728 if self._match(regex, path): 1729 return True 1730 return False
1731
1732 1733 -class WatchManagerError(Exception):
1734 """ 1735 WatchManager Exception. Raised on error encountered on watches 1736 operations. 1737 1738 """
1739 - def __init__(self, msg, wmd):
1740 """ 1741 @param msg: Exception string's description. 1742 @type msg: string 1743 @param wmd: This dictionary contains the wd assigned to paths of the 1744 same call for which watches were successfully added. 1745 @type wmd: dict 1746 """ 1747 self.wmd = wmd 1748 Exception.__init__(self, msg)
1749
1750 1751 -class WatchManager:
1752 """ 1753 Provide operations for watching files and directories. Its internal 1754 dictionary is used to reference watched items. When used inside 1755 threaded code, one must instanciate as many WatchManager instances as 1756 there are ThreadedNotifier instances. 1757 1758 """
1759 - def __init__(self, exclude_filter=lambda path: False):
1760 """ 1761 Initialization: init inotify, init watch manager dictionary. 1762 Raise OSError if initialization fails, raise InotifyBindingNotFoundError 1763 if no inotify binding was found (through ctypes or from direct access to 1764 syscalls). 1765 1766 @param exclude_filter: boolean function, returns True if current 1767 path must be excluded from being watched. 1768 Convenient for providing a common exclusion 1769 filter for every call to add_watch. 1770 @type exclude_filter: callable object 1771 """ 1772 self._ignore_events = False 1773 self._exclude_filter = exclude_filter 1774 self._wmd = {} # watch dict key: watch descriptor, value: watch 1775 1776 self._inotify_wrapper = INotifyWrapper.create() 1777 if self._inotify_wrapper is None: 1778 raise InotifyBindingNotFoundError() 1779 1780 self._fd = self._inotify_wrapper.inotify_init() # file descriptor 1781 if self._fd < 0: 1782 err = 'Cannot initialize new instance of inotify, %s' 1783 raise OSError(err % self._inotify_wrapper.str_errno())
1784
1785 - def close(self):
1786 """ 1787 Close inotify's file descriptor, this action will also automatically 1788 remove (i.e. stop watching) all its associated watch descriptors. 1789 After a call to this method the WatchManager's instance become useless 1790 and cannot be reused, a new instance must then be instanciated. It 1791 makes sense to call this method in few situations for instance if 1792 several independant WatchManager must be instanciated or if all watches 1793 must be removed and no other watches need to be added. 1794 """ 1795 os.close(self._fd)
1796
1797 - def get_fd(self):
1798 """ 1799 Return assigned inotify's file descriptor. 1800 1801 @return: File descriptor. 1802 @rtype: int 1803 """ 1804 return self._fd
1805
1806 - def get_watch(self, wd):
1807 """ 1808 Get watch from provided watch descriptor wd. 1809 1810 @param wd: Watch descriptor. 1811 @type wd: int 1812 """ 1813 return self._wmd.get(wd)
1814
1815 - def del_watch(self, wd):
1816 """ 1817 Remove watch entry associated to watch descriptor wd. 1818 1819 @param wd: Watch descriptor. 1820 @type wd: int 1821 """ 1822 try: 1823 del self._wmd[wd] 1824 except KeyError, err: 1825 log.error('Cannot delete unknown watch descriptor %s' % str(err))
1826 1827 @property
1828 - def watches(self):
1829 """ 1830 Get a reference on the internal watch manager dictionary. 1831 1832 @return: Internal watch manager dictionary. 1833 @rtype: dict 1834 """ 1835 return self._wmd
1836
1837 - def __format_path(self, path):
1838 """ 1839 Format path to its internal (stored in watch manager) representation. 1840 """ 1841 # Unicode strings are converted back to strings, because it seems 1842 # that inotify_add_watch from ctypes does not work well when 1843 # it receives an ctypes.create_unicode_buffer instance as argument. 1844 # Therefore even wd are indexed with bytes string and not with 1845 # unicode paths. 1846 if isinstance(path, unicode): 1847 path = path.encode(sys.getfilesystemencoding()) 1848 return os.path.normpath(path)
1849
1850 - def __add_watch(self, path, mask, proc_fun, auto_add, exclude_filter):
1851 """ 1852 Add a watch on path, build a Watch object and insert it in the 1853 watch manager dictionary. Return the wd value. 1854 """ 1855 path = self.__format_path(path) 1856 if auto_add and not mask & IN_CREATE: 1857 mask |= IN_CREATE 1858 wd = self._inotify_wrapper.inotify_add_watch(self._fd, path, mask) 1859 if wd < 0: 1860 return wd 1861 watch = Watch(wd=wd, path=path, mask=mask, proc_fun=proc_fun, 1862 auto_add=auto_add, exclude_filter=exclude_filter) 1863 self._wmd[wd] = watch 1864 log.debug('New %s', watch) 1865 return wd
1866
1867 - def __glob(self, path, do_glob):
1868 if do_glob: 1869 return glob(path) 1870 else: 1871 return [path]
1872
1873 - def add_watch(self, path, mask, proc_fun=None, rec=False, 1874 auto_add=False, do_glob=False, quiet=True, 1875 exclude_filter=None):
1876 """ 1877 Add watch(s) on the provided |path|(s) with associated |mask| flag 1878 value and optionally with a processing |proc_fun| function and 1879 recursive flag |rec| set to True. 1880 Ideally |path| components should not be unicode objects. Note that 1881 although unicode paths are accepted there are converted to byte 1882 strings before a watch is put on that path. The encoding used for 1883 converting the unicode object is given by sys.getfilesystemencoding(). 1884 If |path| is already watched it is ignored, but if it is called with 1885 option rec=True a watch is put on each one of its not-watched 1886 subdirectory. 1887 1888 @param path: Path to watch, the path can either be a file or a 1889 directory. Also accepts a sequence (list) of paths. 1890 @type path: string or list of strings 1891 @param mask: Bitmask of events. 1892 @type mask: int 1893 @param proc_fun: Processing object. 1894 @type proc_fun: function or ProcessEvent instance or instance of 1895 one of its subclasses or callable object. 1896 @param rec: Recursively add watches from path on all its 1897 subdirectories, set to False by default (doesn't 1898 follows symlinks in any case). 1899 @type rec: bool 1900 @param auto_add: Automatically add watches on newly created 1901 directories in watched parent |path| directory. 1902 If |auto_add| is True, IN_CREATE is ored with |mask| 1903 when the watch is added. 1904 @type auto_add: bool 1905 @param do_glob: Do globbing on pathname (see standard globbing 1906 module for more informations). 1907 @type do_glob: bool 1908 @param quiet: if False raises a WatchManagerError exception on 1909 error. See example not_quiet.py. 1910 @type quiet: bool 1911 @param exclude_filter: predicate (boolean function), which returns 1912 True if the current path must be excluded 1913 from being watched. This argument has 1914 precedence over exclude_filter passed to 1915 the class' constructor. 1916 @type exclude_filter: callable object 1917 @return: dict of paths associated to watch descriptors. A wd value 1918 is positive if the watch was added sucessfully, 1919 otherwise the value is negative. If the path was invalid 1920 or was already watched it is not included into this returned 1921 dictionary. 1922 @rtype: dict of {str: int} 1923 """ 1924 ret_ = {} # return {path: wd, ...} 1925 1926 if exclude_filter is None: 1927 exclude_filter = self._exclude_filter 1928 1929 # normalize args as list elements 1930 for npath in self.__format_param(path): 1931 # unix pathname pattern expansion 1932 for apath in self.__glob(npath, do_glob): 1933 # recursively list subdirs according to rec param 1934 for rpath in self.__walk_rec(apath, rec): 1935 if not exclude_filter(rpath): 1936 wd = ret_[rpath] = self.__add_watch(rpath, mask, 1937 proc_fun, 1938 auto_add, 1939 exclude_filter) 1940 if wd < 0: 1941 err = ('add_watch: cannot watch %s WD=%d, %s' % \ 1942 (rpath, wd, 1943 self._inotify_wrapper.str_errno())) 1944 if quiet: 1945 log.error(err) 1946 else: 1947 raise WatchManagerError(err, ret_) 1948 else: 1949 # Let's say -2 means 'explicitely excluded 1950 # from watching'. 1951 ret_[rpath] = -2 1952 return ret_
1953
1954 - def __get_sub_rec(self, lpath):
1955 """ 1956 Get every wd from self._wmd if its path is under the path of 1957 one (at least) of those in lpath. Doesn't follow symlinks. 1958 1959 @param lpath: list of watch descriptor 1960 @type lpath: list of int 1961 @return: list of watch descriptor 1962 @rtype: list of int 1963 """ 1964 for d in lpath: 1965 root = self.get_path(d) 1966 if root is not None: 1967 # always keep root 1968 yield d 1969 else: 1970 # if invalid 1971 continue 1972 1973 # nothing else to expect 1974 if not os.path.isdir(root): 1975 continue 1976 1977 # normalization 1978 root = os.path.normpath(root) 1979 # recursion 1980 lend = len(root) 1981 for iwd in self._wmd.items(): 1982 cur = iwd[1].path 1983 pref = os.path.commonprefix([root, cur]) 1984 if root == os.sep or (len(pref) == lend and \ 1985 len(cur) > lend and \ 1986 cur[lend] == os.sep): 1987 yield iwd[1].wd
1988
1989 - def update_watch(self, wd, mask=None, proc_fun=None, rec=False, 1990 auto_add=False, quiet=True):
1991 """ 1992 Update existing watch descriptors |wd|. The |mask| value, the 1993 processing object |proc_fun|, the recursive param |rec| and the 1994 |auto_add| and |quiet| flags can all be updated. 1995 1996 @param wd: Watch Descriptor to update. Also accepts a list of 1997 watch descriptors. 1998 @type wd: int or list of int 1999 @param mask: Optional new bitmask of events. 2000 @type mask: int 2001 @param proc_fun: Optional new processing function. 2002 @type proc_fun: function or ProcessEvent instance or instance of 2003 one of its subclasses or callable object. 2004 @param rec: Optionally adds watches recursively on all 2005 subdirectories contained into |wd| directory. 2006 @type rec: bool 2007 @param auto_add: Automatically adds watches on newly created 2008 directories in the watch's path corresponding to |wd|. 2009 If |auto_add| is True, IN_CREATE is ored with |mask| 2010 when the watch is updated. 2011 @type auto_add: bool 2012 @param quiet: If False raises a WatchManagerError exception on 2013 error. See example not_quiet.py 2014 @type quiet: bool 2015 @return: dict of watch descriptors associated to booleans values. 2016 True if the corresponding wd has been successfully 2017 updated, False otherwise. 2018 @rtype: dict of {int: bool} 2019 """ 2020 lwd = self.__format_param(wd) 2021 if rec: 2022 lwd = self.__get_sub_rec(lwd) 2023 2024 ret_ = {} # return {wd: bool, ...} 2025 for awd in lwd: 2026 apath = self.get_path(awd) 2027 if not apath or awd < 0: 2028 err = 'update_watch: invalid WD=%d' % awd 2029 if quiet: 2030 log.error(err) 2031 continue 2032 raise WatchManagerError(err, ret_) 2033 2034 if mask: 2035 wd_ = self._inotify_wrapper.inotify_add_watch(self._fd, apath, 2036 mask) 2037 if wd_ < 0: 2038 ret_[awd] = False 2039 err = ('update_watch: cannot update %s WD=%d, %s' % \ 2040 (apath, wd_, self._inotify_wrapper.str_errno())) 2041 if quiet: 2042 log.error(err) 2043 continue 2044 raise WatchManagerError(err, ret_) 2045 2046 assert(awd == wd_) 2047 2048 if proc_fun or auto_add: 2049 watch_ = self._wmd[awd] 2050 2051 if proc_fun: 2052 watch_.proc_fun = proc_fun 2053 2054 if auto_add: 2055 watch_.auto_add = auto_add 2056 2057 ret_[awd] = True 2058 log.debug('Updated watch - %s', self._wmd[awd]) 2059 return ret_
2060
2061 - def __format_param(self, param):
2062 """ 2063 @param param: Parameter. 2064 @type param: string or int 2065 @return: wrap param. 2066 @rtype: list of type(param) 2067 """ 2068 if isinstance(param, list): 2069 for p_ in param: 2070 yield p_ 2071 else: 2072 yield param
2073
2074 - def get_wd(self, path):
2075 """ 2076 Returns the watch descriptor associated to path. This method 2077 presents a prohibitive cost, always prefer to keep the WD 2078 returned by add_watch(). If the path is unknown it returns None. 2079 2080 @param path: Path. 2081 @type path: str 2082 @return: WD or None. 2083 @rtype: int or None 2084 """ 2085 path = self.__format_path(path) 2086 for iwd in self._wmd.items(): 2087 if iwd[1].path == path: 2088 return iwd[0]
2089
2090 - def get_path(self, wd):
2091 """ 2092 Returns the path associated to WD, if WD is unknown it returns None. 2093 2094 @param wd: Watch descriptor. 2095 @type wd: int 2096 @return: Path or None. 2097 @rtype: string or None 2098 """ 2099 watch_ = self._wmd.get(wd) 2100 if watch_ is not None: 2101 return watch_.path
2102
2103 - def __walk_rec(self, top, rec):
2104 """ 2105 Yields each subdirectories of top, doesn't follow symlinks. 2106 If rec is false, only yield top. 2107 2108 @param top: root directory. 2109 @type top: string 2110 @param rec: recursive flag. 2111 @type rec: bool 2112 @return: path of one subdirectory. 2113 @rtype: string 2114 """ 2115 if not rec or os.path.islink(top) or not os.path.isdir(top): 2116 yield top 2117 else: 2118 for root, dirs, files in os.walk(top): 2119 yield root
2120
2121 - def rm_watch(self, wd, rec=False, quiet=True):
2122 """ 2123 Removes watch(s). 2124 2125 @param wd: Watch Descriptor of the file or directory to unwatch. 2126 Also accepts a list of WDs. 2127 @type wd: int or list of int. 2128 @param rec: Recursively removes watches on every already watched 2129 subdirectories and subfiles. 2130 @type rec: bool 2131 @param quiet: If False raises a WatchManagerError exception on 2132 error. See example not_quiet.py 2133 @type quiet: bool 2134 @return: dict of watch descriptors associated to booleans values. 2135 True if the corresponding wd has been successfully 2136 removed, False otherwise. 2137 @rtype: dict of {int: bool} 2138 """ 2139 lwd = self.__format_param(wd) 2140 if rec: 2141 lwd = self.__get_sub_rec(lwd) 2142 2143 ret_ = {} # return {wd: bool, ...} 2144 for awd in lwd: 2145 # remove watch 2146 wd_ = self._inotify_wrapper.inotify_rm_watch(self._fd, awd) 2147 if wd_ < 0: 2148 ret_[awd] = False 2149 err = ('rm_watch: cannot remove WD=%d, %s' % \ 2150 (awd, self._inotify_wrapper.str_errno())) 2151 if quiet: 2152 log.error(err) 2153 continue 2154 raise WatchManagerError(err, ret_) 2155 2156 # Remove watch from our dictionary 2157 if awd in self._wmd: 2158 del self._wmd[awd] 2159 ret_[awd] = True 2160 log.debug('Watch WD=%d (%s) removed', awd, self.get_path(awd)) 2161 return ret_
2162 2163
2164 - def watch_transient_file(self, filename, mask, proc_class):
2165 """ 2166 Watch a transient file, which will be created and deleted frequently 2167 over time (e.g. pid file). 2168 2169 @attention: Currently under the call to this function it is not 2170 possible to correctly watch the events triggered into the same 2171 base directory than the directory where is located this watched 2172 transient file. For instance it would be wrong to make these 2173 two successive calls: wm.watch_transient_file('/var/run/foo.pid', ...) 2174 and wm.add_watch('/var/run/', ...) 2175 2176 @param filename: Filename. 2177 @type filename: string 2178 @param mask: Bitmask of events, should contain IN_CREATE and IN_DELETE. 2179 @type mask: int 2180 @param proc_class: ProcessEvent (or of one of its subclass), beware of 2181 accepting a ProcessEvent's instance as argument into 2182 __init__, see transient_file.py example for more 2183 details. 2184 @type proc_class: ProcessEvent's instance or of one of its subclasses. 2185 @return: Same as add_watch(). 2186 @rtype: Same as add_watch(). 2187 """ 2188 dirname = os.path.dirname(filename) 2189 if dirname == '': 2190 return {} # Maintains coherence with add_watch() 2191 basename = os.path.basename(filename) 2192 # Assuming we are watching at least for IN_CREATE and IN_DELETE 2193 mask |= IN_CREATE | IN_DELETE 2194 2195 def cmp_name(event): 2196 if getattr(event, 'name') is None: 2197 return False 2198 return basename == event.name
2199 return self.add_watch(dirname, mask, 2200 proc_fun=proc_class(ChainIfTrue(func=cmp_name)), 2201 rec=False, 2202 auto_add=False, do_glob=False, 2203 exclude_filter=lambda path: False)
2204
2205 - def get_ignore_events(self):
2206 return self._ignore_events
2207
2208 - def set_ignore_events(self, nval):
2209 self._ignore_events = nval
2210 2211 ignore_events = property(get_ignore_events, set_ignore_events, 2212 "Make watch manager ignoring new events.") 2213
2214 2215 2216 -class RawOutputFormat:
2217 """ 2218 Format string representations. 2219 """
2220 - def __init__(self, format=None):
2221 self.format = format or {}
2222
2223 - def simple(self, s, attribute):
2224 if not isinstance(s, str): 2225 s = str(s) 2226 return (self.format.get(attribute, '') + s + 2227 self.format.get('normal', ''))
2228
2229 - def punctuation(self, s):
2230 """Punctuation color.""" 2231 return self.simple(s, 'normal')
2232
2233 - def field_value(self, s):
2234 """Field value color.""" 2235 return self.simple(s, 'purple')
2236
2237 - def field_name(self, s):
2238 """Field name color.""" 2239 return self.simple(s, 'blue')
2240
2241 - def class_name(self, s):
2242 """Class name color.""" 2243 return self.format.get('red', '') + self.simple(s, 'bold')
2244 2245 output_format = RawOutputFormat()
2246 2247 -class ColoredOutputFormat(RawOutputFormat):
2248 """ 2249 Format colored string representations. 2250 """
2251 - def __init__(self):
2252 f = {'normal': '\033[0m', 2253 'black': '\033[30m', 2254 'red': '\033[31m', 2255 'green': '\033[32m', 2256 'yellow': '\033[33m', 2257 'blue': '\033[34m', 2258 'purple': '\033[35m', 2259 'cyan': '\033[36m', 2260 'bold': '\033[1m', 2261 'uline': '\033[4m', 2262 'blink': '\033[5m', 2263 'invert': '\033[7m'} 2264 RawOutputFormat.__init__(self, f)
2265
2266 2267 -def compatibility_mode():
2268 """ 2269 Use this function to turn on the compatibility mode. The compatibility 2270 mode is used to improve compatibility with Pyinotify 0.7.1 (or older) 2271 programs. The compatibility mode provides additional variables 'is_dir', 2272 'event_name', 'EventsCodes.IN_*' and 'EventsCodes.ALL_EVENTS' as 2273 Pyinotify 0.7.1 provided. Do not call this function from new programs!! 2274 Especially if there are developped for Pyinotify >= 0.8.x. 2275 """ 2276 setattr(EventsCodes, 'ALL_EVENTS', ALL_EVENTS) 2277 for evname in globals(): 2278 if evname.startswith('IN_'): 2279 setattr(EventsCodes, evname, globals()[evname]) 2280 global COMPATIBILITY_MODE 2281 COMPATIBILITY_MODE = True
2282
2283 2284 -def command_line():
2285 """ 2286 By default the watched path is '/tmp' and all types of events are 2287 monitored. Events monitoring serves forever, type c^c to stop it. 2288 """ 2289 from optparse import OptionParser 2290 2291 usage = "usage: %prog [options] [path1] [path2] [pathn]" 2292 2293 parser = OptionParser(usage=usage) 2294 parser.add_option("-v", "--verbose", action="store_true", 2295 dest="verbose", help="Verbose mode") 2296 parser.add_option("-r", "--recursive", action="store_true", 2297 dest="recursive", 2298 help="Add watches recursively on paths") 2299 parser.add_option("-a", "--auto_add", action="store_true", 2300 dest="auto_add", 2301 help="Automatically add watches on new directories") 2302 parser.add_option("-g", "--glob", action="store_true", 2303 dest="glob", 2304 help="Treat paths as globs") 2305 parser.add_option("-e", "--events-list", metavar="EVENT[,...]", 2306 dest="events_list", 2307 help=("A comma-separated list of events to watch for - " 2308 "see the documentation for valid options (defaults" 2309 " to everything)")) 2310 parser.add_option("-s", "--stats", action="store_true", 2311 dest="stats", 2312 help="Display dummy statistics") 2313 parser.add_option("-V", "--version", action="store_true", 2314 dest="version", help="Pyinotify version") 2315 parser.add_option("-f", "--raw-format", action="store_true", 2316 dest="raw_format", 2317 help="Disable enhanced output format.") 2318 parser.add_option("-c", "--command", action="store", 2319 dest="command", 2320 help="Shell command to run upon event") 2321 2322 (options, args) = parser.parse_args() 2323 2324 if options.verbose: 2325 log.setLevel(10) 2326 2327 if options.version: 2328 print(__version__) 2329 2330 if not options.raw_format: 2331 global output_format 2332 output_format = ColoredOutputFormat() 2333 2334 if len(args) < 1: 2335 path = '/tmp' # default watched path 2336 else: 2337 path = args 2338 2339 # watch manager instance 2340 wm = WatchManager() 2341 # notifier instance and init 2342 if options.stats: 2343 notifier = Notifier(wm, default_proc_fun=Stats(), read_freq=5) 2344 else: 2345 notifier = Notifier(wm, default_proc_fun=PrintAllEvents()) 2346 2347 # What mask to apply 2348 mask = 0 2349 if options.events_list: 2350 events_list = options.events_list.split(',') 2351 for ev in events_list: 2352 evcode = EventsCodes.ALL_FLAGS.get(ev, 0) 2353 if evcode: 2354 mask |= evcode 2355 else: 2356 parser.error("The event '%s' specified with option -e" 2357 " is not valid" % ev) 2358 else: 2359 mask = ALL_EVENTS 2360 2361 # stats 2362 cb_fun = None 2363 if options.stats: 2364 def cb(s): 2365 sys.stdout.write(repr(s.proc_fun())) 2366 sys.stdout.write('\n') 2367 sys.stdout.write(str(s.proc_fun())) 2368 sys.stdout.write('\n') 2369 sys.stdout.flush()
2370 cb_fun = cb 2371 2372 # External command 2373 if options.command: 2374 def cb(s): 2375 subprocess.Popen(options.command, shell=True) 2376 cb_fun = cb 2377 2378 log.debug('Start monitoring %s, (press c^c to halt pyinotify)' % path) 2379 2380 wm.add_watch(path, mask, rec=options.recursive, auto_add=options.auto_add, do_glob=options.glob) 2381 # Loop forever (until sigint signal get caught) 2382 notifier.loop(callback=cb_fun) 2383 2384 2385 if __name__ == '__main__': 2386 command_line() 2387