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