Python看门狗:有没有办法暂停观察者?

7

我正在使用Watchdog监控一个目录,并将其与Dropbox保持同步。

我面临的情况是,每次从Dropbox下载文件时,我都会触发上传事件,因为我需要写入Watchdog正在监视的目录。这是我正在使用的代码。

event_handler = UploadHandler.UploadHandler()
observer = Observer()
observer.schedule(event_handler, path=APP_PATH, recursive=True)
observer.start()

try:
    while True:
        # Apply download here   
        time.sleep(20)

except KeyboardInterrupt:
    observer.stop()

observer.join()

有没有一种方法可以在我应用下载时“暂停”观察者,然后在完成时再“恢复”它?

我遇到了类似的问题,正在研究Observer API以尝试找出解决办法:https://github.com/gorakhargosh/watchdog/blob/master/src/watchdog/observers/api.py - grammar31
4个回答

2
我需要暂停功能,所以我正在使用以下观察者:
import time
import contextlib
import watchdog.observers


class PausingObserver(watchdog.observers.Observer):
    def dispatch_events(self, *args, **kwargs):
        if not getattr(self, '_is_paused', False):
            super(PausingObserver, self).dispatch_events(*args, **kwargs)

    def pause(self):
        self._is_paused = True

    def resume(self):
        time.sleep(self.timeout)  # allow interim events to be queued
        self.event_queue.queue.clear()
        self._is_paused = False

    @contextlib.contextmanager
    def ignore_events(self):
        self.pause()
        yield
        self.resume()

我可以使用pause()resume()方法直接暂停观察器,但我的主要用例是当我只想忽略由于对我正在监视的目录进行写操作而引起的任何事件时,我会使用上下文管理器:
import os
import datetime
import watchdog.events


class MyHandler(watchdog.events.FileSystemEventHandler):
    def on_modified(self, event):
        with OBSERVER.ignore_events():
            with open('./watchdir/modifications.log', 'a') as f:
                f.write(datetime.datetime.now().strftime("%H:%M:%S") + '\n')

if __name__ == '__main__':
    watchdir = 'watchdir'
    if not os.path.exists(watchdir):
        os.makedirs(watchdir)

    OBSERVER = PausingObserver()
    OBSERVER.schedule(MyHandler(), watchdir, recursive=True)
    OBSERVER.start()
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        OBSERVER.stop()
    OBSERVER.join()

您可以通过将两个代码块保存到文件中,运行它,并在创建的“watchdir”目录中添加/编辑/删除文件来测试它。 您所做的修改的时间戳将附加到“watchdir / modifications.log”中。
这个功能似乎是内置在pyinotify中的(链接),但是该库只适用于Linux,而watchdog是独立于操作系统的。

1

在花费了很多时间(现在是凌晨5点,对于任何打字错误和其他问题表示歉意)查看这两个答案之后,我一无所获,感到很愚蠢,并意识到了一个更简单的解决方案。

摘要(下面是代码)

  1. 我没有使用自定义观察者,但您可以使用。
  2. 确保您的事件处理程序与您声明观察者的文件位于同一位置。
  3. 创建一个名为PAUSED或类似名称的global
  4. on_modified或您在事件处理程序中使用的任何事件函数内部,检查PAUSED是否为True
  5. 如果为True,则退出;如果不是,则继续并暂停。然后完成后恢复。

将此放在文件顶部:

global PAUSED
PAUSED = False

将此代码放入EventHandler.on_modified()或您的事件处理程序中,以及您正在使用的任何事件函数:

# Retrieve global
global PAUSED

# If PAUSED, exit
if PAUSED is True:
  return

# If not, pause anything else from running and continue
PAUSED = True

# Do stuff here

# Once finished, allow other things to run
PAUSED = False
原因: 这将完全忽略任何在暂停期间尝试运行的内容。我需要这个功能,因为看门狗会在每个“文件修改”事件中启动最多5次,因为文件仍在被写入。这确保它只会触发一次,暂停,并且在那些其他重复事件过期之前不会恢复。

1
吹毛求疵:最好使用if PAUSE而不是if PAUSE is True - undefined

0
在处理事件时暂停观察者(例如,当事件处理程序修改文件时):
class PausableObserver(Observer):
    @contextmanager
    def pause(self):
        orig_put = self.event_queue.put
        self.event_queue.put = lambda x : None
        try:
            yield
        finally:
            self.event_queue.put = orig_put

使用方法:

with observer.pause():
    modify_file()
    sleep(.1)

其他方法会将事件保留在队列中,因为没有并发调度事件。

0

您可以重写类Observer的方法dispatch_events,以跳过您不想分派的事件。以下是如何实现的示例:

class SkipObserver(watchdog.observers.Observer):
    def __init__(self, *args):
        Observer.__init__(self, *args)
        self._skip_list = []

    def skip(self, event):
        with self._lock:
            self._skip_list.append(event)

    def dispatch_events(self, event_queue, timeout):
        event, watch = event_queue.get(block=True, timeout=timeout)
        try:
            if event in self._skip_list:
                self._skip_list.remove(event)
            else:
                self._dispatch_event(event, watch)
        except KeyError:
            pass
        event_queue.task_done()

现在将您的示例中的Observer替换为SkipObserver,并使用方法skip跳过事件。请注意,在处理程序中使用skip会导致死锁,因为此处实现的skip是阻塞的。在处理事件时,观察者被锁定。

如果要为每个处理程序指定跳过,请使用与上述方法类似的方法。


我认为这会导致他失去一些事件。我不认为 OP 愿意失去一些信号。 - manu
@manu:如果您的意思是在使用skip时丢失一些事件(因为它是阻塞的),那么不会发生。 - boxdot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接