1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """
22 pyinotify
23
24 @author: Sebastien Martini
25 @license: GPL 2
26 @contact: seb@dbzteam.org
27 """
28
30 """Indicates exceptions raised by a Pyinotify class."""
31
32
34 """
35 Raised for unsupported Python version.
36 """
38 """
39 @param version: Current Python version
40 @type version: string
41 """
42 PyinotifyError.__init__(self,
43 ('Python %s is unsupported, requires '
44 'at least Python 2.4') % version)
45
46
48 """
49 Raised for unsupported libc version.
50 """
52 """
53 @param version: Current Libc version
54 @type version: string
55 """
56 PyinotifyError.__init__(self,
57 ('Libc %s is unsupported, requires '
58 'at least Libc 2.4') % version)
59
60
61
62 import sys
63 if sys.version < '2.4':
64 raise UnsupportedPythonVersionError(sys.version)
65
66
67
68 import threading
69 import os
70 import select
71 import struct
72 import fcntl
73 import errno
74 import termios
75 import array
76 import logging
77 import atexit
78 from collections import deque
79 from datetime import datetime, timedelta
80 import time
81 import fnmatch
82 import re
83 import ctypes
84 import ctypes.util
85
86
87 __author__ = "seb@dbzteam.org (Sebastien Martini)"
88
89 __version__ = "0.8.5"
90
91 __metaclass__ = type
92
93
94
95 LIBC = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c'))
96
97
98
99
100 LIBC.gnu_get_libc_version.restype = ctypes.c_char_p
101 LIBC_VERSION = LIBC.gnu_get_libc_version()
102 if LIBC_VERSION < '2.4':
103 raise UnsupportedLibcVersionError(LIBC_VERSION)
104
105
106
107 log = logging.getLogger("pyinotify")
108 console_handler = logging.StreamHandler()
109 console_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
110 log.addHandler(console_handler)
111 log.setLevel(20)
112
113
114
115 try:
116 if False:
117 import psyco
118 psyco.full()
119 except ImportError:
120
121 pass
122
123
124
125
126
128 """
129 Access (read, write) inotify's variables through sysctl.
130
131 Examples:
132 - Read variable: myvar = max_queued_events.value
133 - Update variable: max_queued_events.value = 42
134 """
135
136 inotify_attrs = {'max_user_instances': 1,
137 'max_user_watches': 2,
138 'max_queued_events': 3}
139
144
146 """
147 @return: stored value.
148 @rtype: int
149 """
150 oldv = ctypes.c_int(0)
151 size = ctypes.c_int(ctypes.sizeof(oldv))
152 LIBC.sysctl(self._attr, 3,
153 ctypes.c_voidp(ctypes.addressof(oldv)),
154 ctypes.addressof(size),
155 None, 0)
156 return oldv.value
157
159 """
160 @param nval: set to nval.
161 @type nval: int
162 """
163 oldv = ctypes.c_int(0)
164 sizeo = ctypes.c_int(ctypes.sizeof(oldv))
165 newv = ctypes.c_int(nval)
166 sizen = ctypes.c_int(ctypes.sizeof(newv))
167 LIBC.sysctl(self._attr, 3,
168 ctypes.c_voidp(ctypes.addressof(oldv)),
169 ctypes.addressof(sizeo),
170 ctypes.c_voidp(ctypes.addressof(newv)),
171 ctypes.addressof(sizen))
172
173 value = property(get_val, set_val)
174
176 return '<%s=%d>' % (self._attrname, self.get_val())
177
178
179
180
181
182
183
184 for attrname in ('max_queued_events', 'max_user_instances', 'max_user_watches'):
185 globals()[attrname] = SysCtlINotify(attrname)
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
209 if not has_magic(pathname):
210 if hasattr(os.path, 'lexists'):
211 if os.path.lexists(pathname):
212 yield pathname
213 else:
214 if os.path.islink(pathname) or os.path.exists(pathname):
215 yield pathname
216 return
217 dirname, basename = os.path.split(pathname)
218
219 if not dirname:
220 return
221
222 if has_magic(dirname):
223 dirs = iglob(dirname)
224 else:
225 dirs = [dirname]
226 if has_magic(basename):
227 glob_in_dir = glob1
228 else:
229 glob_in_dir = glob0
230 for dirname in dirs:
231 for name in glob_in_dir(dirname, basename):
232 yield os.path.join(dirname, name)
233
234 -def glob1(dirname, pattern):
235 if not dirname:
236 dirname = os.curdir
237 try:
238 names = os.listdir(dirname)
239 except os.error:
240 return []
241 return fnmatch.filter(names, pattern)
242
243 -def glob0(dirname, basename):
244 if basename == '' and os.path.isdir(dirname):
245
246
247 return [basename]
248 if hasattr(os.path, 'lexists'):
249 if os.path.lexists(os.path.join(dirname, basename)):
250 return [basename]
251 else:
252 if (os.path.islink(os.path.join(dirname, basename)) or
253 os.path.exists(os.path.join(dirname, basename))):
254 return [basename]
255 return []
256
257 magic_check = re.compile('[*?[]')
258
261
262
263
264
265
266
268 """
269 Set of codes corresponding to each kind of events.
270 Some of these flags are used to communicate with inotify, whereas
271 the others are sent to userspace by inotify notifying some events.
272
273 @cvar IN_ACCESS: File was accessed.
274 @type IN_ACCESS: int
275 @cvar IN_MODIFY: File was modified.
276 @type IN_MODIFY: int
277 @cvar IN_ATTRIB: Metadata changed.
278 @type IN_ATTRIB: int
279 @cvar IN_CLOSE_WRITE: Writtable file was closed.
280 @type IN_CLOSE_WRITE: int
281 @cvar IN_CLOSE_NOWRITE: Unwrittable file closed.
282 @type IN_CLOSE_NOWRITE: int
283 @cvar IN_OPEN: File was opened.
284 @type IN_OPEN: int
285 @cvar IN_MOVED_FROM: File was moved from X.
286 @type IN_MOVED_FROM: int
287 @cvar IN_MOVED_TO: File was moved to Y.
288 @type IN_MOVED_TO: int
289 @cvar IN_CREATE: Subfile was created.
290 @type IN_CREATE: int
291 @cvar IN_DELETE: Subfile was deleted.
292 @type IN_DELETE: int
293 @cvar IN_DELETE_SELF: Self (watched item itself) was deleted.
294 @type IN_DELETE_SELF: int
295 @cvar IN_MOVE_SELF: Self (watched item itself) was moved.
296 @type IN_MOVE_SELF: int
297 @cvar IN_UNMOUNT: Backing fs was unmounted.
298 @type IN_UNMOUNT: int
299 @cvar IN_Q_OVERFLOW: Event queued overflowed.
300 @type IN_Q_OVERFLOW: int
301 @cvar IN_IGNORED: File was ignored.
302 @type IN_IGNORED: int
303 @cvar IN_ONLYDIR: only watch the path if it is a directory (new
304 in kernel 2.6.15).
305 @type IN_ONLYDIR: int
306 @cvar IN_DONT_FOLLOW: don't follow a symlink (new in kernel 2.6.15).
307 IN_ONLYDIR we can make sure that we don't watch
308 the target of symlinks.
309 @type IN_DONT_FOLLOW: int
310 @cvar IN_MASK_ADD: add to the mask of an already existing watch (new
311 in kernel 2.6.14).
312 @type IN_MASK_ADD: int
313 @cvar IN_ISDIR: Event occurred against dir.
314 @type IN_ISDIR: int
315 @cvar IN_ONESHOT: Only send event once.
316 @type IN_ONESHOT: int
317 @cvar ALL_EVENTS: Alias for considering all of the events.
318 @type ALL_EVENTS: int
319 """
320
321
322
323
324 FLAG_COLLECTIONS = {'OP_FLAGS': {
325 'IN_ACCESS' : 0x00000001,
326 'IN_MODIFY' : 0x00000002,
327 'IN_ATTRIB' : 0x00000004,
328 'IN_CLOSE_WRITE' : 0x00000008,
329 'IN_CLOSE_NOWRITE' : 0x00000010,
330 'IN_OPEN' : 0x00000020,
331 'IN_MOVED_FROM' : 0x00000040,
332 'IN_MOVED_TO' : 0x00000080,
333 'IN_CREATE' : 0x00000100,
334 'IN_DELETE' : 0x00000200,
335 'IN_DELETE_SELF' : 0x00000400,
336
337 'IN_MOVE_SELF' : 0x00000800,
338 },
339 'EVENT_FLAGS': {
340 'IN_UNMOUNT' : 0x00002000,
341 'IN_Q_OVERFLOW' : 0x00004000,
342 'IN_IGNORED' : 0x00008000,
343 },
344 'SPECIAL_FLAGS': {
345 'IN_ONLYDIR' : 0x01000000,
346
347 'IN_DONT_FOLLOW' : 0x02000000,
348 'IN_MASK_ADD' : 0x20000000,
349
350 'IN_ISDIR' : 0x40000000,
351 'IN_ONESHOT' : 0x80000000,
352 },
353 }
354
356 """
357 Return the event name associated to mask. IN_ISDIR is appended when
358 appropriate. Note: only one event is returned, because only one is
359 raised once at a time.
360
361 @param mask: mask.
362 @type mask: int
363 @return: event name.
364 @rtype: str
365 """
366 ms = mask
367 name = '%s'
368 if mask & IN_ISDIR:
369 ms = mask - IN_ISDIR
370 name = '%s|IN_ISDIR'
371 return name % EventsCodes.ALL_VALUES[ms]
372
373 maskname = staticmethod(maskname)
374
375
376
377 EventsCodes.ALL_FLAGS = {}
378 EventsCodes.ALL_VALUES = {}
379 for flagc, valc in EventsCodes.FLAG_COLLECTIONS.iteritems():
380
381
382 setattr(EventsCodes, flagc, valc)
383
384
385 EventsCodes.ALL_FLAGS.update(valc)
386
387
388
389 for name, val in valc.iteritems():
390 globals()[name] = val
391 EventsCodes.ALL_VALUES[val] = name
392
393
394
395 ALL_EVENTS = reduce(lambda x, y: x | y, EventsCodes.OP_FLAGS.itervalues())
396 EventsCodes.ALL_FLAGS['ALL_EVENTS'] = ALL_EVENTS
397 EventsCodes.ALL_VALUES[ALL_EVENTS] = 'ALL_EVENTS'
398
399
401 """
402 Event structure, represent events raised by the system. This
403 is the base class and should be subclassed.
404
405 """
407 """
408 Attach attributes (contained in dict_) to self.
409 """
410 for tpl in dict_.iteritems():
411 setattr(self, *tpl)
412
414 """
415 @return: String representation.
416 @rtype: str
417 """
418 s = ''
419 for attr, value in sorted(self.__dict__.items(), key=lambda x: x[0]):
420 if attr.startswith('_'):
421 continue
422 if attr == 'mask':
423 value = hex(getattr(self, attr))
424 elif isinstance(value, str) and not value:
425 value ="''"
426 s += ' %s%s%s' % (color_theme.field_name(attr),
427 color_theme.punct('='),
428 color_theme.field_value(value))
429
430 s = '%s%s%s %s' % (color_theme.punct('<'),
431 color_theme.class_name(self.__class__.__name__),
432 s,
433 color_theme.punct('>'))
434 return s
435
436
438 """
439 Raw event, it contains only the informations provided by the system.
440 It doesn't infer anything.
441 """
442 - def __init__(self, wd, mask, cookie, name):
443 """
444 @param wd: Watch Descriptor.
445 @type wd: int
446 @param mask: Bitmask of events.
447 @type mask: int
448 @param cookie: Cookie.
449 @type cookie: int
450 @param name: Basename of the file or directory against which the
451 event was raised, in case where the watched directory
452 is the parent directory. None if the event was raised
453 on the watched item itself.
454 @type name: string or None
455 """
456
457 super(_RawEvent, self).__init__({'wd': wd,
458 'mask': mask,
459 'cookie': cookie,
460 'name': name.rstrip('\0')})
461 log.debug(repr(self))
462
463
465 """
466 This class contains all the useful informations about the observed
467 event. However, the incorporation of each field is not guaranteed and
468 depends on the type of event. In effect, some fields are irrelevant
469 for some kind of event (for example 'cookie' is meaningless for
470 IN_CREATE whereas it is useful for IN_MOVE_TO).
471
472 The possible fields are:
473 - wd (int): Watch Descriptor.
474 - mask (int): Mask.
475 - maskname (str): Readable event name.
476 - path (str): path of the file or directory being watched.
477 - name (str): Basename of the file or directory against which the
478 event was raised, in case where the watched directory
479 is the parent directory. None if the event was raised
480 on the watched item itself. This field is always provided
481 even if the string is ''.
482 - pathname (str): absolute path of: path + name
483 - cookie (int): Cookie.
484 - dir (bool): is the event raised against directory.
485
486 """
488 """
489 Concretely, this is the raw event plus inferred infos.
490 """
491 _Event.__init__(self, raw)
492 self.maskname = EventsCodes.maskname(self.mask)
493 try:
494 if self.name:
495 self.pathname = os.path.abspath(os.path.join(self.path,
496 self.name))
497 else:
498 self.pathname = os.path.abspath(self.path)
499 except AttributeError:
500 pass
501
502
504 """
505 ProcessEventError Exception. Raised on ProcessEvent error.
506 """
508 """
509 @param err: Exception error description.
510 @type err: string
511 """
512 PyinotifyError.__init__(self, err)
513
514
516 """
517 Abstract processing event class.
518 """
520 """
521 To behave like a functor the object must be callable.
522 This method is a dispatch method. Lookup order:
523 1. process_MASKNAME method
524 2. process_FAMILY_NAME method
525 3. otherwise call process_default
526
527 @param event: Event to be processed.
528 @type event: Event object
529 @return: By convention when used from the ProcessEvent class:
530 - Returning False or None (default value) means keep on
531 executing next chained functors (see chain.py example).
532 - Returning True instead means do not execute next
533 processing functions.
534 @rtype: bool
535 @raise ProcessEventError: Event object undispatchable,
536 unknown event.
537 """
538 stripped_mask = event.mask - (event.mask & IN_ISDIR)
539 maskname = EventsCodes.ALL_VALUES.get(stripped_mask)
540 if maskname is None:
541 raise ProcessEventError("Unknown mask 0x%08x" % stripped_mask)
542
543
544 meth = getattr(self, 'process_' + maskname, None)
545 if meth is not None:
546 return meth(event)
547
548 meth = getattr(self, 'process_IN_' + maskname.split('_')[1], None)
549 if meth is not None:
550 return meth(event)
551
552 return self.process_default(event)
553
555 return '<%s>' % self.__class__.__name__
556
557
559 """
560 There is three kind of processing according to each event:
561
562 1. special handling (deletion from internal container, bug, ...).
563 2. default treatment: which is applied to most of events.
564 4. IN_ISDIR is never sent alone, he is piggybacked with a standart
565 event, he is not processed as the others events, instead, its
566 value is captured and appropriately aggregated to dst event.
567 """
569 """
570
571 @param wm: Watch Manager.
572 @type wm: WatchManager instance
573 @param notifier: notifier.
574 @type notifier: Instance of Notifier.
575 """
576 self._watch_manager = wm
577 self._notifier = notifier
578 self._mv_cookie = {}
579 self._mv = {}
580
582 """
583 Cleanup (delete) old (>1mn) records contained in self._mv_cookie
584 and self._mv.
585 """
586 date_cur_ = datetime.now()
587 for seq in [self._mv_cookie, self._mv]:
588 for k in seq.keys():
589 if (date_cur_ - seq[k][1]) > timedelta(minutes=1):
590 log.debug('cleanup: deleting entry %s' % seq[k][0])
591 del seq[k]
592
594 """
595 If the event concerns a directory and the auto_add flag of the
596 targetted watch is set to True, a new watch is added on this
597 new directory, with the same attributes's values than those of
598 this watch.
599 """
600 if raw_event.mask & IN_ISDIR:
601 watch_ = self._watch_manager._wmd.get(raw_event.wd)
602 if watch_.auto_add:
603 addw = self._watch_manager.add_watch
604 newwd = addw(os.path.join(watch_.path, raw_event.name),
605 watch_.mask, proc_fun=watch_.proc_fun,
606 rec=False, auto_add=watch_.auto_add)
607
608
609
610
611
612 base = os.path.join(watch_.path, raw_event.name)
613 if newwd[base] > 0:
614 for name in os.listdir(base):
615 inner = os.path.join(base, name)
616 if (os.path.isdir(inner) and
617 self._watch_manager.get_wd(inner) is None):
618
619
620 rawevent = _RawEvent(newwd[base],
621 IN_CREATE | IN_ISDIR,
622 0, name)
623 self._notifier._eventq.append(rawevent)
624 return self.process_default(raw_event)
625
627 """
628 Map the cookie with the source path (+ date for cleaning).
629 """
630 watch_ = self._watch_manager._wmd.get(raw_event.wd)
631 path_ = watch_.path
632 src_path = os.path.normpath(os.path.join(path_, raw_event.name))
633 self._mv_cookie[raw_event.cookie] = (src_path, datetime.now())
634 return self.process_default(raw_event, {'cookie': raw_event.cookie})
635
637 """
638 Map the source path with the destination path (+ date for
639 cleaning).
640 """
641 watch_ = self._watch_manager._wmd.get(raw_event.wd)
642 path_ = watch_.path
643 dst_path = os.path.normpath(os.path.join(path_, raw_event.name))
644 mv_ = self._mv_cookie.get(raw_event.cookie)
645 if mv_:
646 self._mv[mv_[0]] = (dst_path, datetime.now())
647 return self.process_default(raw_event, {'cookie': raw_event.cookie})
648
650 """
651 STATUS: the following bug has been fixed in the recent kernels (fixme:
652 which version ?). Now it raises IN_DELETE_SELF instead.
653
654 Old kernels are bugged, this event is raised when the watched item
655 was moved, so we must update its path, but under some circumstances it
656 can be impossible: if its parent directory and its destination
657 directory aren't watched. The kernel (see include/linux/fsnotify.h)
658 doesn't bring us enough informations like the destination path of
659 moved items.
660 """
661 watch_ = self._watch_manager._wmd.get(raw_event.wd)
662 src_path = watch_.path
663 mv_ = self._mv.get(src_path)
664 if mv_:
665 watch_.path = mv_[0]
666 else:
667 log.error("The path %s of this watch %s must not "
668 "be trusted anymore" % (watch_.path, watch_))
669 if not watch_.path.endswith('-wrong-path'):
670 watch_.path += '-wrong-path'
671
672 return self.process_default(raw_event)
673
675 """
676 Only signal overflow, most of the common flags are irrelevant
677 for this event (path, wd, name).
678 """
679 return Event({'mask': raw_event.mask})
680
682 """
683 The watch descriptor raised by this event is now ignored (forever),
684 it can be safely deleted from watch manager dictionary.
685 After this event we can be sure that neither the event queue
686 neither the system will raise an event associated to this wd.
687 """
688 event_ = self.process_default(raw_event)
689 try:
690 del self._watch_manager._wmd[raw_event.wd]
691 except KeyError, err:
692 log.error(err)
693 return event_
694
696 """
697 Common handling for the following events:
698
699 IN_ACCESS, IN_MODIFY, IN_ATTRIB, IN_CLOSE_WRITE, IN_CLOSE_NOWRITE,
700 IN_OPEN, IN_DELETE, IN_DELETE_SELF, IN_UNMOUNT.
701 """
702 ret = None
703 watch_ = self._watch_manager._wmd.get(raw_event.wd)
704 if raw_event.mask & (IN_DELETE_SELF | IN_MOVE_SELF):
705
706 dir_ = watch_.dir
707 else:
708 dir_ = bool(raw_event.mask & IN_ISDIR)
709 dict_ = {'wd': raw_event.wd,
710 'mask': raw_event.mask,
711 'path': watch_.path,
712 'name': raw_event.name,
713 'dir': dir_}
714 dict_.update(to_append)
715 return Event(dict_)
716
717
719 """
720 Process events objects, can be specialized via subclassing, thus its
721 behavior can be overriden:
722
723 Note: you should not override __init__ in your subclass instead define
724 a my_init() method, this method will be called from the constructor of
725 this class with optional parameters.
726
727 1. Provide methods, e.g. process_IN_DELETE for processing a given kind
728 of event (eg. IN_DELETE in this case).
729 2. Or/and provide methods for processing events by 'family', e.g.
730 process_IN_CLOSE method will process both IN_CLOSE_WRITE and
731 IN_CLOSE_NOWRITE events (if process_IN_CLOSE_WRITE and
732 process_IN_CLOSE_NOWRITE aren't defined).
733 3. Or/and override process_default for processing the remaining kind of
734 events.
735 """
736 pevent = None
737
738 - def __init__(self, pevent=None, **kargs):
739 """
740 Enable chaining of ProcessEvent instances.
741
742 @param pevent: optional callable object, will be called on event
743 processing (before self).
744 @type pevent: callable
745 @param kargs: optional arguments delagated to template method my_init
746 @type kargs: dict
747 """
748 self.pevent = pevent
749 self.my_init(**kargs)
750
752 """
753 Override this method when subclassing if you want to achieve
754 custom initialization of your subclass' instance. You MUST pass
755 keyword arguments. This method does nothing by default.
756
757 @param kargs: optional arguments delagated to template method my_init
758 @type kargs: dict
759 """
760 pass
761
763 stop_chaining = False
764 if self.pevent is not None:
765
766
767
768
769
770 stop_chaining = self.pevent(event)
771 if not stop_chaining:
772 return _ProcessEvent.__call__(self, event)
773
776
778 """
779 Default default processing event method. Print event
780 on standart output.
781
782 @param event: Event to be processed.
783 @type event: Event instance
784 """
785 print(repr(event))
786
787
789 """
790 Makes conditional chaining depending on the result of the nested
791 processing instance.
792 """
795
797 return not self._func(event)
798
799
800 -class Stats(ProcessEvent):
802 self._start_time = time.time()
803 self._stats = {}
804 self._stats_lock = threading.Lock()
805
807 self._stats_lock.acquire()
808 try:
809 events = event.maskname.split('|')
810 for event_name in events:
811 count = self._stats.get(event_name, 0)
812 self._stats[event_name] = count + 1
813 finally:
814 self._stats_lock.release()
815
817 self._stats_lock.acquire()
818 try:
819 return self._stats.copy()
820 finally:
821 self._stats_lock.release()
822
824 stats = self._stats_copy()
825
826 t = int(time.time() - self._start_time)
827 if t < 60:
828 ts = str(t) + 'sec'
829 elif 60 <= t < 3600:
830 ts = '%dmn%dsec' % (t / 60, t % 60)
831 elif 3600 <= t < 86400:
832 ts = '%dh%dmn' % (t / 3600, (t % 3600) / 60)
833 elif t >= 86400:
834 ts = '%dd%dh' % (t / 86400, (t % 86400) / 3600)
835 stats['ElapsedTime'] = ts
836
837 l = []
838 for ev, value in sorted(stats.items(), key=lambda x: x[0]):
839 l.append(' %s=%s' % (color_theme.field_name(ev),
840 color_theme.field_value(value)))
841 s = '<%s%s >' % (color_theme.class_name(self.__class__.__name__),
842 ''.join(l))
843 return s
844
845 - def dump(self, filename):
846 fo = file(filename, 'wb')
847 try:
848 fo.write(str(self))
849 finally:
850 fo.close()
851
853 stats = self._stats_copy()
854 if not stats:
855 return ''
856
857 m = max(stats.values())
858 unity = int(round(float(m) / scale)) or 1
859 fmt = '%%-26s%%-%ds%%s' % (len(color_theme.field_value('@' * scale))
860 + 1)
861 def func(x):
862 return fmt % (color_theme.field_name(x[0]),
863 color_theme.field_value('@' * (x[1] / unity)),
864 color_theme.yellow('%d' % x[1]))
865 s = '\n'.join(map(func, sorted(stats.items(), key=lambda x: x[0])))
866 return s
867
868
870 """
871 Notifier Exception. Raised on Notifier error.
872
873 """
875 """
876 @param err: Exception string's description.
877 @type err: string
878 """
879 PyinotifyError.__init__(self, err)
880
881
883 """
884 Read notifications, process events.
885
886 """
887 - def __init__(self, watch_manager, default_proc_fun=ProcessEvent(),
888 read_freq=0, treshold=0, timeout=None):
889 """
890 Initialization. read_freq, treshold and timeout parameters are used
891 when looping.
892
893 @param watch_manager: Watch Manager.
894 @type watch_manager: WatchManager instance
895 @param default_proc_fun: Default processing method.
896 @type default_proc_fun: instance of ProcessEvent
897 @param read_freq: if read_freq == 0, events are read asap,
898 if read_freq is > 0, this thread sleeps
899 max(0, read_freq - timeout) seconds. But if
900 timeout is None it can be different because
901 poll is blocking waiting for something to read.
902 @type read_freq: int
903 @param treshold: File descriptor will be read only if its size to
904 read is >= treshold. If != 0, you likely want to
905 use it in combination with read_freq because
906 without that you keep looping without really reading
907 anything and that until the amount to read
908 is >= treshold. At least with read_freq you may sleep.
909 @type treshold: int
910 @param timeout:
911 http://docs.python.org/lib/poll-objects.html#poll-objects
912 @type timeout: int
913 """
914
915 self._watch_manager = watch_manager
916
917 self._fd = self._watch_manager._fd
918
919 self._pollobj = select.poll()
920 self._pollobj.register(self._fd, select.POLLIN)
921
922 self._pipe = (-1, -1)
923
924 self._eventq = deque()
925
926 self._sys_proc_fun = _SysProcessEvent(self._watch_manager, self)
927
928 self._default_proc_fun = default_proc_fun
929
930 self._read_freq = read_freq
931 self._treshold = treshold
932 self._timeout = timeout
933
935 return self._default_proc_fun
936
938 """
939 Check for new events available to read, blocks up to timeout
940 milliseconds.
941
942 @return: New events to read.
943 @rtype: bool
944 """
945 while True:
946 try:
947
948 ret = self._pollobj.poll(self._timeout)
949 except select.error, err:
950 if err[0] == errno.EINTR:
951 continue
952 else:
953 raise
954 else:
955 break
956
957 if not ret or (self._pipe[0] == ret[0][0]):
958 return False
959
960 return ret[0][1] & select.POLLIN
961
963 """
964 Read events from device, build _RawEvents, and enqueue them.
965 """
966 buf_ = array.array('i', [0])
967
968 if fcntl.ioctl(self._fd, termios.FIONREAD, buf_, 1) == -1:
969 return
970 queue_size = buf_[0]
971 if queue_size < self._treshold:
972 log.debug('(fd: %d) %d bytes available to read but '
973 'treshold is fixed to %d bytes' % (self._fd,
974 queue_size,
975 self._treshold))
976 return
977
978 try:
979
980 r = os.read(self._fd, queue_size)
981 except Exception, msg:
982 raise NotifierError(msg)
983 log.debug('event queue size: %d' % queue_size)
984 rsum = 0
985 while rsum < queue_size:
986 s_size = 16
987
988 s_ = struct.unpack('iIII', r[rsum:rsum+s_size])
989
990 fname_len = s_[3]
991
992 s_ = s_[:-1]
993
994 s_ += struct.unpack('%ds' % fname_len,
995 r[rsum + s_size:rsum + s_size + fname_len])
996 self._eventq.append(_RawEvent(*s_))
997 rsum += s_size + fname_len
998
1000 """
1001 Routine for processing events from queue by calling their
1002 associated proccessing function (instance of ProcessEvent).
1003 It also do internal processings, to keep the system updated.
1004 """
1005 while self._eventq:
1006 raw_event = self._eventq.popleft()
1007 watch_ = self._watch_manager._wmd.get(raw_event.wd)
1008 revent = self._sys_proc_fun(raw_event)
1009 if watch_ and watch_.proc_fun:
1010 watch_.proc_fun(revent)
1011 else:
1012 self._default_proc_fun(revent)
1013 self._sys_proc_fun.cleanup()
1014
1015
1016 - def __daemonize(self, pid_file=None, force_kill=False, stdin=os.devnull,
1017 stdout=os.devnull, stderr=os.devnull):
1018 """
1019 pid_file: file to which pid will be written.
1020 force_kill: if True kill the process associated to pid_file.
1021 stdin, stdout, stderr: files associated to common streams.
1022 """
1023 if pid_file is None:
1024 dirname = '/var/run/'
1025 basename = sys.argv[0] or 'pyinotify'
1026 pid_file = os.path.join(dirname, basename + '.pid')
1027
1028 if os.path.exists(pid_file):
1029 fo = file(pid_file, 'rb')
1030 try:
1031 try:
1032 pid = int(fo.read())
1033 except ValueError:
1034 pid = None
1035 if pid is not None:
1036 try:
1037 os.kill(pid, 0)
1038 except OSError, err:
1039 pass
1040 else:
1041 if not force_kill:
1042 s = 'There is already a pid file %s with pid %d'
1043 raise NotifierError(s % (pid_file, pid))
1044 else:
1045 os.kill(pid, 9)
1046 finally:
1047 fo.close()
1048
1049
1050 def fork_daemon():
1051
1052 pid = os.fork()
1053 if (pid == 0):
1054
1055 os.setsid()
1056 pid = os.fork()
1057 if (pid == 0):
1058
1059 os.chdir('/')
1060 os.umask(0)
1061 else:
1062
1063 os._exit(0)
1064 else:
1065
1066 os._exit(0)
1067
1068 fd_inp = os.open(stdin, os.O_RDONLY)
1069 os.dup2(fd_inp, 0)
1070 fd_out = os.open(stdout, os.O_WRONLY|os.O_CREAT)
1071 os.dup2(fd_out, 1)
1072 fd_err = os.open(stderr, os.O_WRONLY|os.O_CREAT)
1073 os.dup2(fd_err, 2)
1074
1075
1076 fork_daemon()
1077
1078
1079 fo = file(pid_file, 'wb')
1080 try:
1081 fo.write(str(os.getpid()) + '\n')
1082 finally:
1083 fo.close()
1084
1085 atexit.register(lambda : os.unlink(pid_file))
1086
1087
1089
1090 if self._read_freq > 0:
1091 cur_time = time.time()
1092 sleep_amount = self._read_freq - (cur_time - ref_time)
1093 if sleep_amount > 0:
1094 log.debug('Now sleeping %d seconds' % sleep_amount)
1095 time.sleep(sleep_amount)
1096
1097
1098 - def loop(self, callback=None, daemonize=False, **args):
1099 """
1100 Events are read only once time every min(read_freq, timeout)
1101 seconds at best and only if the size to read is >= treshold.
1102
1103 @param callback: Functor called after each event processing. Expects
1104 to receive notifier object (self) as first parameter.
1105 @type callback: callable
1106 @param daemonize: This thread is daemonized if set to True.
1107 @type daemonize: boolean
1108 """
1109 if daemonize:
1110 self.__daemonize(**args)
1111
1112
1113 while 1:
1114 try:
1115 self.process_events()
1116 if callback is not None:
1117 callback(self)
1118 ref_time = time.time()
1119
1120 if self.check_events():
1121 self._sleep(ref_time)
1122 self.read_events()
1123 except KeyboardInterrupt:
1124
1125 log.debug('Pyinotify stops monitoring.')
1126
1127 self.stop()
1128 break
1129
1131 """
1132 Close the inotify's instance (close its file descriptor).
1133 It destroys all existing watches, pending events,...
1134 """
1135 self._pollobj.unregister(self._fd)
1136 os.close(self._fd)
1137
1138
1140 """
1141 This notifier inherits from threading.Thread for instantiating a separate
1142 thread, and also inherits from Notifier, because it is a threaded notifier.
1143
1144 Note that everything possible with this class is also possible through
1145 Notifier. Moreover Notifier is _better_ under many aspects: not threaded,
1146 can be easily daemonized.
1147 """
1148 - def __init__(self, watch_manager, default_proc_fun=ProcessEvent(),
1149 read_freq=0, treshold=0, timeout=None):
1150 """
1151 Initialization, initialize base classes. read_freq, treshold and
1152 timeout parameters are used when looping.
1153
1154 @param watch_manager: Watch Manager.
1155 @type watch_manager: WatchManager instance
1156 @param default_proc_fun: Default processing method.
1157 @type default_proc_fun: instance of ProcessEvent
1158 @param read_freq: if read_freq == 0, events are read asap,
1159 if read_freq is > 0, this thread sleeps
1160 max(0, read_freq - timeout) seconds.
1161 @type read_freq: int
1162 @param treshold: File descriptor will be read only if its size to
1163 read is >= treshold. If != 0, you likely want to
1164 use it in combination with read_freq because
1165 without that you keep looping without really reading
1166 anything and that until the amount to read
1167 is >= treshold. At least with read_freq you may sleep.
1168 @type treshold: int
1169 @param timeout:
1170 see http://docs.python.org/lib/poll-objects.html#poll-objects
1171 Read the corresponding comment in the source code before changing
1172 it.
1173 @type timeout: int
1174 """
1175
1176 threading.Thread.__init__(self)
1177
1178 self._stop_event = threading.Event()
1179
1180 Notifier.__init__(self, watch_manager, default_proc_fun, read_freq,
1181 treshold, timeout)
1182
1183 self._pipe = os.pipe()
1184 self._pollobj.register(self._pipe[0], select.POLLIN)
1185
1187 """
1188 Stop the notifier's loop. Stop notification. Join the thread.
1189 """
1190 self._stop_event.set()
1191 os.write(self._pipe[1], 'stop')
1192 threading.Thread.join(self)
1193 Notifier.stop(self)
1194 self._pollobj.unregister(self._pipe[0])
1195 os.close(self._pipe[0])
1196 os.close(self._pipe[1])
1197
1199 """
1200 Thread's main loop. Don't meant to be called by user directly.
1201 Call start() instead.
1202
1203 Events are read only once time every min(read_freq, timeout)
1204 seconds at best and only if the size of events to read is >= treshold.
1205 """
1206
1207
1208
1209
1210 while not self._stop_event.isSet():
1211 self.process_events()
1212 ref_time = time.time()
1213 if self.check_events():
1214 self._sleep(ref_time)
1215 self.read_events()
1216
1218 """
1219 Start the thread's loop: read and process events until the method
1220 stop() is called.
1221 Never call this method directly, instead call the start() method
1222 inherited from threading.Thread, which then will call run().
1223 """
1224 self.loop()
1225
1226
1228 """
1229 Represent a watch, i.e. a file or directory being watched.
1230
1231 """
1233 """
1234 Initializations.
1235
1236 @param wd: Watch descriptor.
1237 @type wd: int
1238 @param path: Path of the file or directory being watched.
1239 @type path: str
1240 @param mask: Mask.
1241 @type mask: int
1242 @param proc_fun: Processing callable object.
1243 @type proc_fun:
1244 @param auto_add: Automatically add watches on new directories.
1245 @type auto_add: bool
1246 """
1247 for k, v in keys.iteritems():
1248 setattr(self, k, v)
1249 self.dir = os.path.isdir(self.path)
1250
1252 """
1253 @return: String representation.
1254 @rtype: str
1255 """
1256 s = ' '.join(['%s%s%s' % (color_theme.field_name(attr),
1257 color_theme.punct('='),
1258 color_theme.field_value(getattr(self,
1259 attr))) \
1260 for attr in self.__dict__ if not attr.startswith('_')])
1261
1262 s = '%s%s %s %s' % (color_theme.punct('<'),
1263 color_theme.class_name(self.__class__.__name__),
1264 s,
1265 color_theme.punct('>'))
1266 return s
1267
1268
1270 """
1271 ExcludeFilter is an exclusion filter.
1272 """
1273
1275 """
1276 @param arg_lst: is either a list or dict of patterns:
1277 [pattern1, ..., patternn]
1278 {'filename1': (list1, listn), ...} where list1 is
1279 a list of patterns
1280 @type arg_lst: list or dict
1281 """
1282 if isinstance(arg_lst, dict):
1283 lst = self._load_patterns(arg_lst)
1284 elif isinstance(arg_lst, list):
1285 lst = arg_lst
1286 else:
1287 raise TypeError
1288
1289 self._lregex = []
1290 for regex in lst:
1291 self._lregex.append(re.compile(regex, re.UNICODE))
1292
1294 lst = []
1295 for path, varnames in dct.iteritems():
1296 loc = {}
1297 execfile(path, {}, loc)
1298 for varname in varnames:
1299 lst.extend(loc.get(varname, []))
1300 return lst
1301
1302 - def _match(self, regex, path):
1303 return regex.match(path) is not None
1304
1306 """
1307 @param path: path to match against regexps.
1308 @type path: str
1309 @return: return True is path has been matched and should
1310 be excluded, False otherwise.
1311 @rtype: bool
1312 """
1313 for regex in self._lregex:
1314 if self._match(regex, path):
1315 return True
1316 return False
1317
1318
1320 """
1321 WatchManager Exception. Raised on error encountered on watches
1322 operations.
1323
1324 """
1326 """
1327 @param msg: Exception string's description.
1328 @type msg: string
1329 @param wmd: Results of previous operations made by the same function
1330 on previous wd or paths. It also contains the item which
1331 raised this exception.
1332 @type wmd: dict
1333 """
1334 self.wmd = wmd
1335 Exception.__init__(self, msg)
1336
1337
1339 """
1340 Provide operations for watching files and directories. Integrated
1341 dictionary is used to reference watched items.
1342 """
1343 - def __init__(self, exclude_filter=lambda path: False):
1344 """
1345 Initialization: init inotify, init watch manager dictionary.
1346 Raise OSError if initialization fails.
1347
1348 @param exclude_filter: boolean function, returns True if current
1349 path must be excluded from being watched.
1350 Convenient for providing a common exclusion
1351 filter for every call to add_watch.
1352 @type exclude_filter: bool
1353 """
1354 self._exclude_filter = exclude_filter
1355 self._wmd = {}
1356 self._fd = LIBC.inotify_init()
1357 if self._fd < 0:
1358 raise OSError()
1359
1360 - def __add_watch(self, path, mask, proc_fun, auto_add):
1361 """
1362 Add a watch on path, build a Watch object and insert it in the
1363 watch manager dictionary. Return the wd value.
1364 """
1365 wd_ = LIBC.inotify_add_watch(self._fd,
1366 ctypes.create_string_buffer(path),
1367 mask)
1368 if wd_ < 0:
1369 return wd_
1370 watch_ = Watch(wd=wd_, path=os.path.normpath(path), mask=mask,
1371 proc_fun=proc_fun, auto_add=auto_add)
1372 self._wmd[wd_] = watch_
1373 log.debug('New %s' % watch_)
1374 return wd_
1375
1376 - def __glob(self, path, do_glob):
1377 if do_glob:
1378 return iglob(path)
1379 else:
1380 return [path]
1381
1382 - def add_watch(self, path, mask, proc_fun=None, rec=False,
1383 auto_add=False, do_glob=False, quiet=True,
1384 exclude_filter=None):
1385 """
1386 Add watch(s) on given path(s) with the specified mask and
1387 optionnally with a processing function and recursive flag.
1388
1389 @param path: Path to watch, the path can either be a file or a
1390 directory. Also accepts a sequence (list) of paths.
1391 @type path: string or list of string
1392 @param mask: Bitmask of events.
1393 @type mask: int
1394 @param proc_fun: Processing object.
1395 @type proc_fun: function or ProcessEvent instance or instance of
1396 one of its subclasses or callable object.
1397 @param rec: Recursively add watches from path on all its
1398 subdirectories, set to False by default (doesn't
1399 follows symlinks).
1400 @type rec: bool
1401 @param auto_add: Automatically add watches on newly created
1402 directories in the watch's path.
1403 @type auto_add: bool
1404 @param do_glob: Do globbing on pathname.
1405 @type do_glob: bool
1406 @param quiet: if True raise an WatchManagerError exception on
1407 error. See example not_quiet.py
1408 @type quiet: bool
1409 @param exclude_filter: boolean function, returns True if current
1410 path must be excluded from being watched.
1411 Has precedence on exclude_filter defined
1412 into __init__.
1413 @type exclude_filter: bool
1414 @return: dict of paths associated to watch descriptors. A wd value
1415 is positive if the watch has been sucessfully added,
1416 otherwise the value is negative. If the path is invalid
1417 it will be not included into this dict.
1418 @rtype: dict of {str: int}
1419 """
1420 ret_ = {}
1421
1422 if exclude_filter is None:
1423 exclude_filter = self._exclude_filter
1424
1425
1426 for npath in self.__format_param(path):
1427
1428 for apath in self.__glob(npath, do_glob):
1429
1430 for rpath in self.__walk_rec(apath, rec):
1431 if not exclude_filter(rpath):
1432 wd = ret_[rpath] = self.__add_watch(rpath, mask,
1433 proc_fun,
1434 auto_add)
1435 if wd < 0:
1436 err = 'add_watch: cannot watch %s (WD=%d)'
1437 err = err % (rpath, wd)
1438 if quiet:
1439 log.error(err)
1440 else:
1441 raise WatchManagerError(err, ret_)
1442 else:
1443
1444
1445 ret_[rpath] = -2
1446 return ret_
1447
1449 """
1450 Get every wd from self._wmd if its path is under the path of
1451 one (at least) of those in lpath. Doesn't follow symlinks.
1452
1453 @param lpath: list of watch descriptor
1454 @type lpath: list of int
1455 @return: list of watch descriptor
1456 @rtype: list of int
1457 """
1458 for d in lpath:
1459 root = self.get_path(d)
1460 if root:
1461
1462 yield d
1463 else:
1464
1465 continue
1466
1467
1468 if not os.path.isdir(root):
1469 continue
1470
1471
1472 root = os.path.normpath(root)
1473
1474 lend = len(root)
1475 for iwd in self._wmd.items():
1476 cur = iwd[1].path
1477 pref = os.path.commonprefix([root, cur])
1478 if root == os.sep or (len(pref) == lend and \
1479 len(cur) > lend and \
1480 cur[lend] == os.sep):
1481 yield iwd[1].wd
1482
1483 - def update_watch(self, wd, mask=None, proc_fun=None, rec=False,
1484 auto_add=False, quiet=True):
1485 """
1486 Update existing watch(s). Both the mask and the processing
1487 object can be modified.
1488
1489 @param wd: Watch Descriptor to update. Also accepts a list of
1490 watch descriptors.
1491 @type wd: int or list of int
1492 @param mask: Optional new bitmask of events.
1493 @type mask: int
1494 @param proc_fun: Optional new processing function.
1495 @type proc_fun: function or ProcessEvent instance or instance of
1496 one of its subclasses or callable object.
1497 @param rec: Recursively update watches on every already watched
1498 subdirectories and subfiles.
1499 @type rec: bool
1500 @param auto_add: Automatically add watches on newly created
1501 directories in the watch's path.
1502 @type auto_add: bool
1503 @param quiet: if True raise an WatchManagerError exception on
1504 error. See example not_quiet.py
1505 @type quiet: bool
1506 @return: dict of watch descriptors associated to booleans values.
1507 True if the corresponding wd has been successfully
1508 updated, False otherwise.
1509 @rtype: dict of int: bool
1510 """
1511 lwd = self.__format_param(wd)
1512 if rec:
1513 lwd = self.__get_sub_rec(lwd)
1514
1515 ret_ = {}
1516 for awd in lwd:
1517 apath = self.get_path(awd)
1518 if not apath or awd < 0:
1519 err = 'update_watch: invalid WD=%d' % awd
1520 if quiet:
1521 log.error(err)
1522 continue
1523 raise WatchManagerError(err, ret_)
1524
1525 if mask:
1526 addw = LIBC.inotify_add_watch
1527 wd_ = addw(self._fd,
1528 ctypes.create_string_buffer(apath),
1529 mask)
1530 if wd_ < 0:
1531 ret_[awd] = False
1532 err = 'update_watch: cannot update WD=%d (%s)' % (wd_,
1533 apath)
1534 if quiet:
1535 log.error(err)
1536 continue
1537 raise WatchManagerError(err, ret_)
1538
1539 assert(awd == wd_)
1540
1541 if proc_fun or auto_add:
1542 watch_ = self._wmd[awd]
1543
1544 if proc_fun:
1545 watch_.proc_fun = proc_fun
1546
1547 if auto_add:
1548 watch_.proc_fun = auto_add
1549
1550 ret_[awd] = True
1551 log.debug('Updated watch - %s' % self._wmd[awd])
1552 return ret_
1553
1566
1568 """
1569 Returns the watch descriptor associated to path. This method
1570 has an prohibitive cost, always prefer to keep the WD.
1571 If path is unknown None is returned.
1572
1573 @param path: path.
1574 @type path: str
1575 @return: WD or None.
1576 @rtype: int or None
1577 """
1578 path = os.path.normpath(path)
1579 for iwd in self._wmd.iteritems():
1580 if iwd[1].path == path:
1581 return iwd[0]
1582 log.debug('get_wd: unknown path %s' % path)
1583
1585 """
1586 Returns the path associated to WD, if WD is unknown
1587 None is returned.
1588
1589 @param wd: watch descriptor.
1590 @type wd: int
1591 @return: path or None.
1592 @rtype: string or None
1593 """
1594 watch_ = self._wmd.get(wd)
1595 if watch_:
1596 return watch_.path
1597 log.debug('get_path: unknown WD %d' % wd)
1598
1600 """
1601 Yields each subdirectories of top, doesn't follow symlinks.
1602 If rec is false, only yield top.
1603
1604 @param top: root directory.
1605 @type top: string
1606 @param rec: recursive flag.
1607 @type rec: bool
1608 @return: path of one subdirectory.
1609 @rtype: string
1610 """
1611 if not rec or os.path.islink(top) or not os.path.isdir(top):
1612 yield top
1613 else:
1614 for root, dirs, files in os.walk(top):
1615 yield root
1616
1617 - def rm_watch(self, wd, rec=False, quiet=True):
1618 """
1619 Removes watch(s).
1620
1621 @param wd: Watch Descriptor of the file or directory to unwatch.
1622 Also accepts a list of WDs.
1623 @type wd: int or list of int.
1624 @param rec: Recursively removes watches on every already watched
1625 subdirectories and subfiles.
1626 @type rec: bool
1627 @param quiet: if True raise an WatchManagerError exception on
1628 error. See example not_quiet.py
1629 @type quiet: bool
1630 @return: dict of watch descriptors associated to booleans values.
1631 True if the corresponding wd has been successfully
1632 removed, False otherwise.
1633 @rtype: dict of int: bool
1634 """
1635 lwd = self.__format_param(wd)
1636 if rec:
1637 lwd = self.__get_sub_rec(lwd)
1638
1639 ret_ = {}
1640 for awd in lwd:
1641
1642 wd_ = LIBC.inotify_rm_watch(self._fd, awd)
1643 if wd_ < 0:
1644 ret_[awd] = False
1645 err = 'rm_watch: cannot remove WD=%d' % awd
1646 if quiet:
1647 log.error(err)
1648 continue
1649 raise WatchManagerError(err, ret_)
1650
1651 ret_[awd] = True
1652 log.debug('watch WD=%d (%s) removed' % (awd, self.get_path(awd)))
1653 return ret_
1654
1655
1657 """
1658 Watch a transient file, which will be created and deleted frequently
1659 over time (e.g. pid file).
1660
1661 @attention: Under the call to this function it will be impossible
1662 to correctly watch the events triggered into the same
1663 base directory than the directory where is located this watched
1664 transient file. For instance it would actually be wrong to make these
1665 two successive calls: wm.watch_transient_file('/var/run/foo.pid', ...)
1666 and wm.add_watch('/var/run/', ...)
1667
1668 @param filename: Filename.
1669 @type filename: string
1670 @param mask: Bitmask of events, should contain IN_CREATE and IN_DELETE.
1671 @type mask: int
1672 @param proc_class: ProcessEvent (or of one of its subclass), beware of
1673 accepting a ProcessEvent's instance as argument into
1674 __init__, see transient_file.py example for more
1675 details.
1676 @type proc_class: ProcessEvent's instance or of one of its subclasses.
1677 @return: See add_watch().
1678 @rtype: See add_watch().
1679 """
1680 dirname = os.path.dirname(filename)
1681 if dirname == '':
1682 return {}
1683 basename = os.path.basename(filename)
1684
1685 mask |= IN_CREATE | IN_DELETE
1686
1687 def cmp_name(event):
1688 return basename == event.name
1689 return self.add_watch(dirname, mask,
1690 proc_fun=proc_class(ChainIfTrue(func=cmp_name)),
1691 rec=False,
1692 auto_add=False, do_glob=False)
1693
1694
1695
1696
1697
1698
1699
1700
1716
1719 return "<%s>" % self.__class__.__name__
1722
1725
1728 if attr.startswith("__"):
1729 raise AttributeError(attr)
1730 s = "style_%s" % attr
1731 if s in self.__class__.__dict__:
1732 before = getattr(self, s)
1733 after = self.style_normal
1734 else:
1735 before = after = ""
1736
1737 def do_style(val, fmt=None, before=before, after=after):
1738 if fmt is None:
1739 if type(val) is not str:
1740 val = str(val)
1741 else:
1742 val = fmt % val
1743 return before+val+after
1744 return do_style
1745
1746
1747 style_normal = ""
1748 style_prompt = ""
1749 style_punct = ""
1750 style_id = ""
1751 style_not_printable = ""
1752 style_class_name = ""
1753 style_field_name = ""
1754 style_field_value = ""
1755 style_emph_field_name = ""
1756 style_emph_field_value = ""
1757 style_watchlist_name = ""
1758 style_watchlist_type = ""
1759 style_watchlist_value = ""
1760 style_fail = ""
1761 style_success = ""
1762 style_odd = ""
1763 style_even = ""
1764 style_yellow = ""
1765 style_active = ""
1766 style_closed = ""
1767 style_left = ""
1768 style_right = ""
1769
1772
1795
1796 color_theme = DefaultTheme()
1797
1798
1799
1801
1802
1803
1804
1805
1806 from optparse import OptionParser
1807
1808 usage = "usage: %prog [options] [path1] [path2] [pathn]"
1809
1810 parser = OptionParser(usage=usage)
1811 parser.add_option("-v", "--verbose", action="store_true",
1812 dest="verbose", help="Verbose mode")
1813 parser.add_option("-r", "--recursive", action="store_true",
1814 dest="recursive",
1815 help="Add watches recursively on paths")
1816 parser.add_option("-a", "--auto_add", action="store_true",
1817 dest="auto_add",
1818 help="Automatically add watches on new directories")
1819 parser.add_option("-e", "--events-list", metavar="EVENT[,...]",
1820 dest="events_list",
1821 help=("A comma-separated list of events to watch for - "
1822 "see the documentation for valid options (defaults"
1823 " to everything)"))
1824 parser.add_option("-s", "--stats", action="store_true",
1825 dest="stats",
1826 help="Display statistics")
1827
1828 (options, args) = parser.parse_args()
1829
1830 if options.verbose:
1831 log.setLevel(10)
1832
1833 if len(args) < 1:
1834 path = '/tmp'
1835 else:
1836 path = args
1837
1838
1839 wm = WatchManager()
1840
1841 if options.stats:
1842 notifier = Notifier(wm, default_proc_fun=Stats(), read_freq=5)
1843 else:
1844 notifier = Notifier(wm)
1845
1846
1847 mask = 0
1848 if options.events_list:
1849 events_list = options.events_list.split(',')
1850 for ev in events_list:
1851 evcode = EventsCodes.ALL_FLAGS.get(ev, 0)
1852 if evcode:
1853 mask |= evcode
1854 else:
1855 parser.error("The event '%s' specified with option -e"
1856 " is not valid" % ev)
1857 else:
1858 mask = ALL_EVENTS
1859
1860
1861 cb_fun = None
1862 if options.stats:
1863 def cb(s):
1864 print('%s\n%s\n' % (repr(s.proc_fun()),
1865 s.proc_fun()))
1866 cb_fun = cb
1867
1868 log.debug('Start monitoring %s, (press c^c to halt pyinotify)' % path)
1869
1870 wm.add_watch(path, mask, rec=options.recursive, auto_add=options.auto_add)
1871
1872 notifier.loop(callback=cb_fun)
1873
1874
1875 if __name__ == '__main__':
1876 command_line()
1877