polling.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  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.polling
  20. :synopsis: Polling emitter implementation.
  21. :author: yesudeep@google.com (Yesudeep Mangalapilly)
  22. Classes
  23. -------
  24. .. autoclass:: PollingObserver
  25. :members:
  26. :show-inheritance:
  27. .. autoclass:: PollingObserverVFS
  28. :members:
  29. :show-inheritance:
  30. :special-members:
  31. """
  32. from __future__ import with_statement
  33. import threading
  34. from functools import partial
  35. from watchdog.utils import stat as default_stat
  36. from watchdog.utils.dirsnapshot import DirectorySnapshot, DirectorySnapshotDiff
  37. from watchdog.observers.api import (
  38. EventEmitter,
  39. BaseObserver,
  40. DEFAULT_OBSERVER_TIMEOUT,
  41. DEFAULT_EMITTER_TIMEOUT
  42. )
  43. from watchdog.events import (
  44. DirMovedEvent,
  45. DirDeletedEvent,
  46. DirCreatedEvent,
  47. DirModifiedEvent,
  48. FileMovedEvent,
  49. FileDeletedEvent,
  50. FileCreatedEvent,
  51. FileModifiedEvent
  52. )
  53. try:
  54. from os import scandir
  55. except ImportError:
  56. from os import listdir as scandir
  57. class PollingEmitter(EventEmitter):
  58. """
  59. Platform-independent emitter that polls a directory to detect file
  60. system changes.
  61. """
  62. def __init__(self, event_queue, watch, timeout=DEFAULT_EMITTER_TIMEOUT,
  63. stat=default_stat, listdir=scandir):
  64. EventEmitter.__init__(self, event_queue, watch, timeout)
  65. self._snapshot = None
  66. self._lock = threading.Lock()
  67. self._take_snapshot = lambda: DirectorySnapshot(
  68. self.watch.path, self.watch.is_recursive, stat=stat, listdir=listdir)
  69. def on_thread_start(self):
  70. self._snapshot = self._take_snapshot()
  71. def queue_events(self, timeout):
  72. # We don't want to hit the disk continuously.
  73. # timeout behaves like an interval for polling emitters.
  74. if self.stopped_event.wait(timeout):
  75. return
  76. with self._lock:
  77. if not self.should_keep_running():
  78. return
  79. # Get event diff between fresh snapshot and previous snapshot.
  80. # Update snapshot.
  81. try:
  82. new_snapshot = self._take_snapshot()
  83. except OSError:
  84. self.queue_event(DirDeletedEvent(self.watch.path))
  85. self.stop()
  86. return
  87. events = DirectorySnapshotDiff(self._snapshot, new_snapshot)
  88. self._snapshot = new_snapshot
  89. # Files.
  90. for src_path in events.files_deleted:
  91. self.queue_event(FileDeletedEvent(src_path))
  92. for src_path in events.files_modified:
  93. self.queue_event(FileModifiedEvent(src_path))
  94. for src_path in events.files_created:
  95. self.queue_event(FileCreatedEvent(src_path))
  96. for src_path, dest_path in events.files_moved:
  97. self.queue_event(FileMovedEvent(src_path, dest_path))
  98. # Directories.
  99. for src_path in events.dirs_deleted:
  100. self.queue_event(DirDeletedEvent(src_path))
  101. for src_path in events.dirs_modified:
  102. self.queue_event(DirModifiedEvent(src_path))
  103. for src_path in events.dirs_created:
  104. self.queue_event(DirCreatedEvent(src_path))
  105. for src_path, dest_path in events.dirs_moved:
  106. self.queue_event(DirMovedEvent(src_path, dest_path))
  107. class PollingObserver(BaseObserver):
  108. """
  109. Platform-independent observer that polls a directory to detect file
  110. system changes.
  111. """
  112. def __init__(self, timeout=DEFAULT_OBSERVER_TIMEOUT):
  113. BaseObserver.__init__(self, emitter_class=PollingEmitter, timeout=timeout)
  114. class PollingObserverVFS(BaseObserver):
  115. """
  116. File system independent observer that polls a directory to detect changes.
  117. """
  118. def __init__(self, stat, listdir, polling_interval=1):
  119. """
  120. :param stat: stat function. See ``os.stat`` for details.
  121. :param listdir: listdir function. See ``os.listdir`` for details.
  122. :type polling_interval: float
  123. :param polling_interval: interval in seconds between polling the file system.
  124. """
  125. emitter_cls = partial(PollingEmitter, stat=stat, listdir=listdir)
  126. BaseObserver.__init__(self, emitter_class=emitter_cls, timeout=polling_interval)