Источник: http://code.activestate.com/recipes/215418-watching-a-directory-tree-on-unix/
Функция watch_directories () берет список путей и вызываемый объект, а затем многократно просматривает деревья каталогов, укорененных в этих путях, отслеживая файлы, которые были удалены или имеютвремя модификации изменилось.Затем вызываемому объекту передаются два списка, содержащие файлы, которые были изменены, и файлы, которые были удалены.
from __future__ import nested_scopes
import os, time
def watch_directories (paths, func, delay=1.0):
"""(paths:[str], func:callable, delay:float)
Continuously monitors the paths and their subdirectories
for changes. If any files or directories are modified,
the callable 'func' is called with a list of the modified paths of both
files and directories. 'func' can return a Boolean value
for rescanning; if it returns True, the directory tree will be
rescanned without calling func() for any found changes.
(This is so func() can write changes into the tree and prevent itself
from being immediately called again.)
"""
# Basic principle: all_files is a dictionary mapping paths to
# modification times. We repeatedly crawl through the directory
# tree rooted at 'path', doing a stat() on each file and comparing
# the modification time.
all_files = {}
def f (unused, dirname, files):
# Traversal function for directories
for filename in files:
path = os.path.join(dirname, filename)
try:
t = os.stat(path)
except os.error:
# If a file has been deleted between os.path.walk()
# scanning the directory and now, we'll get an
# os.error here. Just ignore it -- we'll report
# the deletion on the next pass through the main loop.
continue
mtime = remaining_files.get(path)
if mtime is not None:
# Record this file as having been seen
del remaining_files[path]
# File's mtime has been changed since we last looked at it.
if t.st_mtime > mtime:
changed_list.append(path)
else:
# No recorded modification time, so it must be
# a brand new file.
changed_list.append(path)
# Record current mtime of file.
all_files[path] = t.st_mtime
# Main loop
rescan = False
while True:
changed_list = []
remaining_files = all_files.copy()
all_files = {}
for path in paths:
os.path.walk(path, f, None)
removed_list = remaining_files.keys()
if rescan:
rescan = False
elif changed_list or removed_list:
rescan = func(changed_list, removed_list)
time.sleep(delay)
if __name__ == '__main__':
def f (changed_files, removed_files):
print changed_files
print 'Removed', removed_files
watch_directories(['.'], f, 1)
Этот рецепт полезен, если вы хотите каким-либо образом отправить задания демону, ноне хочу использовать какой-либо механизм IPC, такой как сокеты или каналы.Вместо этого демон может сидеть и наблюдать каталог отправки, а задания можно отправить, перетащив файл или каталог в каталог отправки.
Блокировка не учитывается.Сама функция watch_directories () на самом деле не нуждается в блокировке;если он пропустит модификацию на одном проходе, он заметит это на следующем проходе.Однако, если задания записываются непосредственно в наблюдаемый каталог, вызываемый объект может запускаться, пока файл задания наполовину записан.Чтобы решить эту проблему, вы можете использовать файл блокировки;вызываемый должен получить блокировку при запуске, а отправители должны получить блокировку, когда они хотят добавить новую работу.Более простой подход - полагаться на то, что системный вызов rename () является атомарным: записать задание во временный каталог, который не отслеживается, и как только файл будет завершен, используйте os.rename (), чтобы переместить его в каталог отправки.