inotify.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. #
  4. # Copyright 2011 Yesudeep Mangalapilly <yesudeep@gmail.com>
  5. # Copyright 2012 Google, Inc.
  6. #
  7. # Licensed under the Apache License, Version 2.0 (the "License");
  8. # you may not use this file except in compliance with the License.
  9. # You may obtain a copy of the License at
  10. #
  11. # http://www.apache.org/licenses/LICENSE-2.0
  12. #
  13. # Unless required by applicable law or agreed to in writing, software
  14. # distributed under the License is distributed on an "AS IS" BASIS,
  15. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. # See the License for the specific language governing permissions and
  17. # limitations under the License.
  18. """
  19. :module: watchdog.observers.inotify
  20. :synopsis: ``inotify(7)`` based emitter implementation.
  21. :author: Sebastien Martini <seb@dbzteam.org>
  22. :author: Luke McCarthy <luke@iogopro.co.uk>
  23. :author: yesudeep@google.com (Yesudeep Mangalapilly)
  24. :author: Tim Cuthbertson <tim+github@gfxmonk.net>
  25. :platforms: Linux 2.6.13+.
  26. .. ADMONITION:: About system requirements
  27. Recommended minimum kernel version: 2.6.25.
  28. Quote from the inotify(7) man page:
  29. "Inotify was merged into the 2.6.13 Linux kernel. The required library
  30. interfaces were added to glibc in version 2.4. (IN_DONT_FOLLOW,
  31. IN_MASK_ADD, and IN_ONLYDIR were only added in version 2.5.)"
  32. Therefore, you must ensure the system is running at least these versions
  33. appropriate libraries and the kernel.
  34. .. ADMONITION:: About recursiveness, event order, and event coalescing
  35. Quote from the inotify(7) man page:
  36. If successive output inotify events produced on the inotify file
  37. descriptor are identical (same wd, mask, cookie, and name) then they
  38. are coalesced into a single event if the older event has not yet been
  39. read (but see BUGS).
  40. The events returned by reading from an inotify file descriptor form
  41. an ordered queue. Thus, for example, it is guaranteed that when
  42. renaming from one directory to another, events will be produced in
  43. the correct order on the inotify file descriptor.
  44. ...
  45. Inotify monitoring of directories is not recursive: to monitor
  46. subdirectories under a directory, additional watches must be created.
  47. This emitter implementation therefore automatically adds watches for
  48. sub-directories if running in recursive mode.
  49. Some extremely useful articles and documentation:
  50. .. _inotify FAQ: http://inotify.aiken.cz/?section=inotify&page=faq&lang=en
  51. .. _intro to inotify: http://www.linuxjournal.com/article/8478
  52. """
  53. from __future__ import with_statement
  54. import os
  55. import threading
  56. from .inotify_buffer import InotifyBuffer
  57. from watchdog.observers.api import (
  58. EventEmitter,
  59. BaseObserver,
  60. DEFAULT_EMITTER_TIMEOUT,
  61. DEFAULT_OBSERVER_TIMEOUT
  62. )
  63. from watchdog.events import (
  64. DirDeletedEvent,
  65. DirModifiedEvent,
  66. DirMovedEvent,
  67. DirCreatedEvent,
  68. FileDeletedEvent,
  69. FileModifiedEvent,
  70. FileMovedEvent,
  71. FileCreatedEvent,
  72. generate_sub_moved_events,
  73. generate_sub_created_events,
  74. )
  75. from watchdog.utils import unicode_paths
  76. class InotifyEmitter(EventEmitter):
  77. """
  78. inotify(7)-based event emitter.
  79. :param event_queue:
  80. The event queue to fill with events.
  81. :param watch:
  82. A watch object representing the directory to monitor.
  83. :type watch:
  84. :class:`watchdog.observers.api.ObservedWatch`
  85. :param timeout:
  86. Read events blocking timeout (in seconds).
  87. :type timeout:
  88. ``float``
  89. """
  90. def __init__(self, event_queue, watch, timeout=DEFAULT_EMITTER_TIMEOUT):
  91. EventEmitter.__init__(self, event_queue, watch, timeout)
  92. self._lock = threading.Lock()
  93. self._inotify = None
  94. def on_thread_start(self):
  95. path = unicode_paths.encode(self.watch.path)
  96. self._inotify = InotifyBuffer(path, self.watch.is_recursive)
  97. def on_thread_stop(self):
  98. if self._inotify:
  99. self._inotify.close()
  100. def queue_events(self, timeout, full_events=False):
  101. # If "full_events" is true, then the method will report unmatched move events as seperate events
  102. # This behavior is by default only called by a InotifyFullEmitter
  103. with self._lock:
  104. event = self._inotify.read_event()
  105. if event is None:
  106. return
  107. if isinstance(event, tuple):
  108. move_from, move_to = event
  109. src_path = self._decode_path(move_from.src_path)
  110. dest_path = self._decode_path(move_to.src_path)
  111. cls = DirMovedEvent if move_from.is_directory else FileMovedEvent
  112. self.queue_event(cls(src_path, dest_path))
  113. self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
  114. self.queue_event(DirModifiedEvent(os.path.dirname(dest_path)))
  115. if move_from.is_directory and self.watch.is_recursive:
  116. for sub_event in generate_sub_moved_events(src_path, dest_path):
  117. self.queue_event(sub_event)
  118. return
  119. src_path = self._decode_path(event.src_path)
  120. if event.is_moved_to:
  121. if full_events:
  122. cls = DirMovedEvent if event.is_directory else FileMovedEvent
  123. self.queue_event(cls(None, src_path))
  124. else:
  125. cls = DirCreatedEvent if event.is_directory else FileCreatedEvent
  126. self.queue_event(cls(src_path))
  127. self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
  128. if event.is_directory and self.watch.is_recursive:
  129. for sub_event in generate_sub_created_events(src_path):
  130. self.queue_event(sub_event)
  131. elif event.is_attrib:
  132. cls = DirModifiedEvent if event.is_directory else FileModifiedEvent
  133. self.queue_event(cls(src_path))
  134. elif event.is_modify:
  135. cls = DirModifiedEvent if event.is_directory else FileModifiedEvent
  136. self.queue_event(cls(src_path))
  137. elif event.is_delete or (event.is_moved_from and not full_events):
  138. cls = DirDeletedEvent if event.is_directory else FileDeletedEvent
  139. self.queue_event(cls(src_path))
  140. self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
  141. elif event.is_moved_from and full_events:
  142. cls = DirMovedEvent if event.is_directory else FileMovedEvent
  143. self.queue_event(cls(src_path, None))
  144. self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
  145. elif event.is_create:
  146. cls = DirCreatedEvent if event.is_directory else FileCreatedEvent
  147. self.queue_event(cls(src_path))
  148. self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
  149. def _decode_path(self, path):
  150. """ Decode path only if unicode string was passed to this emitter. """
  151. if isinstance(self.watch.path, bytes):
  152. return path
  153. return unicode_paths.decode(path)
  154. class InotifyFullEmitter(InotifyEmitter):
  155. """
  156. inotify(7)-based event emitter. By default this class produces move events even if they are not matched
  157. Such move events will have a ``None`` value for the unmatched part.
  158. :param event_queue:
  159. The event queue to fill with events.
  160. :param watch:
  161. A watch object representing the directory to monitor.
  162. :type watch:
  163. :class:`watchdog.observers.api.ObservedWatch`
  164. :param timeout:
  165. Read events blocking timeout (in seconds).
  166. :type timeout:
  167. ``float``
  168. """
  169. def __init__(self, event_queue, watch, timeout=DEFAULT_EMITTER_TIMEOUT):
  170. InotifyEmitter.__init__(self, event_queue, watch, timeout)
  171. def queue_events(self, timeout, events=True):
  172. InotifyEmitter.queue_events(self, timeout, full_events=events)
  173. class InotifyObserver(BaseObserver):
  174. """
  175. Observer thread that schedules watching directories and dispatches
  176. calls to event handlers.
  177. """
  178. def __init__(self, timeout=DEFAULT_OBSERVER_TIMEOUT, generate_full_events=False):
  179. if (generate_full_events):
  180. BaseObserver.__init__(self, emitter_class=InotifyFullEmitter, timeout=timeout)
  181. else:
  182. BaseObserver.__init__(self, emitter_class=InotifyEmitter,
  183. timeout=timeout)