PyGTK / GIO: рекурсивно отслеживать каталог изменений - PullRequest
4 голосов
/ 18 июня 2010

Возьмите следующий демонстрационный код (из ответа GIO на этот вопрос), который использует GIO FileMonitor для отслеживания изменений в каталоге:

import gio

def directory_changed(monitor, file1, file2, evt_type):
    print "Changed:", file1, file2, evt_type

gfile = gio.File(".")
monitor = gfile.monitor_directory(gio.FILE_MONITOR_NONE, None)
monitor.connect("changed", directory_changed) 

import glib
ml = glib.MainLoop()
ml.run()

После запуска этого кода я могу создавать и изменять дочерние узлы и получать уведомления об изменениях.Тем не менее, это работает только для непосредственных детей (я знаю, что документы не говорят иначе).Последняя из следующих команд оболочки не приведет к уведомлению:

touch one
mkdir two
touch two/three

Есть ли простой способ сделать его рекурсивным?Я бы предпочел не кодировать вручную что-то, что ищет создание каталогов и добавляет монитор, удаляя их при удалении и т. Д.

Предполагается использовать расширение браузера файлов VCS, чтобы иметь возможность кэшировать состоянияфайлы в рабочей копии и обновлять их индивидуально при изменениях.Таким образом, можно отслеживать от десятков до тысяч (или более) каталогов.Я хотел бы просто найти корень рабочей копии и добавить туда файловый монитор.

Я знаю о pyinotify , но я избегаю его, чтобы это работало подЯдра Linux, такие как FreeBSD или ... другие.Насколько мне известно, GIO FileMonitor использует inotify внизу, где это возможно, и я могу понять, не подчеркивая реализацию для поддержания некоторой степени абстракции, но он предложил мне, чтобы это было возможно.

(В случае, если это имеет значение, я изначально разместил это в PyGTK списке рассылки .)

Ответы [ 2 ]

2 голосов
/ 29 июня 2010

«Есть ли простой способ сделать его рекурсивным?»

Я не знаю ни одного «простого» способа добиться этого.Базовые системы, такие как inotify в Linux или kqueue на BSD, не предоставляют средств для автоматического добавления рекурсивных наблюдений.Я также не знаю ни о какой иерархии библиотек, что вы хотите поверх GIO.

Так что вам, скорее всего, придется создавать это самостоятельно.Поскольку в некоторых случаях (например, mkdir -p foo/bar/baz) это может быть немного хитрым, я бы посоветовал посмотреть, как pynotify реализует свою функциональность auto_add (grep через источник pynotify ) и перенести ее на GIO.

1 голос
/ 29 июня 2010

Я не уверен, что GIO позволяет вам иметь более одного монитора одновременно, но если это так, нет * причины, по которой вы не можете сделать что-то вроде этого:

import gio
import os

def directory_changed(monitor, file1, file2, evt_type):
    if os.path.isdir(file2):    #maybe this needs to be file1?
        add_monitor(file2) 
    print "Changed:", file1, file2, evt_type

def add_monitor(dir):
    gfile = gio.File(dir)
    monitor = gfile.monitor_directory(gio.FILE_MONITOR_NONE, None)
    monitor.connect("changed", directory_changed) 

add_monitor('.')

import glib
ml = glib.MainLoop()
ml.run()

*, когда я говорю «без причины», есть вероятность, что это может стать источником ресурсов, хотя с почти нулевым знанием о GIO я не могу сказать. Также вполне возможно накатить свой собственный в Python с помощью нескольких команд (os.listdir среди других). Это может выглядеть примерно так

import time
import os

class Watcher(object):
    def __init__(self):
        self.dirs = []
        self.snapshots = {}

    def add_dir(self, dir):
        self.dirs.append(dir)

    def check_for_changes(self, dir):
        snapshot = self.snapshots.get(dir)
        curstate = os.listdir(dir)
        if not snapshot:
            self.snapshots[dir] = curstate
        else:
            if not snapshot == curstate:
                print 'Changes: ',
                for change in set(curstate).symmetric_difference(set(snapshot)):
                    if os.path.isdir(change):
                        print "isdir"
                        self.add_dir(change)
                    print change,

                self.snapshots[dir] = curstate
                print

    def mainloop(self):
        if len(self.dirs) < 1:
            print "ERROR: Please add a directory with add_dir()"
            return

        while True:
            for dir in self.dirs:
                self.check_for_changes(dir)
            time.sleep(4) # Don't want to be a resource hog

w = Watcher()
w.add_dir('.')


w.mainloop()
...