Сценарий Python для мониторинга каталога запускается дважды при обновлении файла. Зачем? - PullRequest
0 голосов
/ 06 ноября 2018

Короткая версия: Сценарий Python, который я написал / адаптировал для отслеживания изменений в каталоге, запускается дважды при изменении файла. Почему?

Длинная версия:

Я пишу некоторый код Python для отслеживания изменений в каталоге и его подкаталогах.

Я начинаю с примера, найденного в разделе «Использование API ReadDirectoryChanges» http://timgolden.me.uk/python/win32_how_do_i/watch_directory_for_changes.html

(По корпоративным ИТ-причинам использование пакета Python Watchdog для меня не вариант.)

Вырезание и вставка из примера там:

import os

import win32file
import win32con

ACTIONS = {
  1 : "Created",
  2 : "Deleted",
  3 : "Updated",
  4 : "Renamed from something",
  5 : "Renamed to something"
}
# Thanks to Claudio Grondi for the correct set of numbers
FILE_LIST_DIRECTORY = 0x0001

path_to_watch = "."
hDir = win32file.CreateFile (
  path_to_watch,
  FILE_LIST_DIRECTORY,
  win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE | win32con.FILE_SHARE_DELETE,
  None,
  win32con.OPEN_EXISTING,
  win32con.FILE_FLAG_BACKUP_SEMANTICS,
  None
)
while 1:
  #
  # ReadDirectoryChangesW takes a previously-created
  # handle to a directory, a buffer size for results,
  # a flag to indicate whether to watch subtrees and
  # a filter of what changes to notify.
  #
  # NB Tim Juchcinski reports that he needed to up
  # the buffer size to be sure of picking up all
  # events when a large number of files were
  # deleted at once.
  #
  results = win32file.ReadDirectoryChangesW (
    hDir,
    1024,
    True,
    win32con.FILE_NOTIFY_CHANGE_FILE_NAME |
     win32con.FILE_NOTIFY_CHANGE_DIR_NAME |
     win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES |
     win32con.FILE_NOTIFY_CHANGE_SIZE |
     win32con.FILE_NOTIFY_CHANGE_LAST_WRITE |
     win32con.FILE_NOTIFY_CHANGE_SECURITY,
    None,
    None
  )
  for action, file in results:
    full_filename = os.path.join (path_to_watch, file)
    print full_filename, ACTIONS.get (action, "Unknown")

В общем, этот пример работает нормально и делает то, что я хочу. В частности, он прекрасно работает при создании файла. Однако, когда файл редактируется / изменяется / обновляется, оператор print в конце (который заменяет действие, которое я действительно хочу выполнить) срабатывает дважды.

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

В случае возможной связанной проблемы, где я могу найти документацию по пакету win32con и API-интерфейсу Microsoft ReadDirectoryChanges? Я немного погуглил, но не нашел ничего, что считаю полезным.

О да, я использую Python 3.5.1 в Windows 7 Enterprise.

EDIT: Хорошо, похоже, что то, что я вижу, может быть присуще ReadDirectoryChangesW (). Я обнаружил этот поток StackOverflow, который, кажется, в основном та же проблема, за исключением того, что оригинальный постер использует C ++, а не Python. C ++ WinApi: ReadDirectoryChangesW () Получение двойных уведомлений

1 Ответ

0 голосов
/ 06 ноября 2018

У меня не установлен Python 2.7, поэтому я не могу проверить это самостоятельно, посмотрев на эту часть

 win32con.FILE_NOTIFY_CHANGE_FILE_NAME |
 win32con.FILE_NOTIFY_CHANGE_DIR_NAME |
 win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES |
 win32con.FILE_NOTIFY_CHANGE_SIZE | #this will change when someone writes or deletes to the file
 win32con.FILE_NOTIFY_CHANGE_LAST_WRITE |#this will change when someone modifies the file
 win32con.FILE_NOTIFY_CHANGE_SECURITY,

видите, как оба условия могут быть вызваны одним и тем же действием? возможно, именно это и вызывает ваше поведение, попробуйте удалить один из двух

Изменить, чтобы уточнить комментарии ниже:

Чтобы получить представление о поведении для каждого типа изменений, попробуйте следующее:

results = {}
results['FName_Change'] = win32file.ReadDirectoryChangesW (
                        hDir,
                        1024,
                        True,
                        win32con.FILE_NOTIFY_CHANGE_FILE_NAME,
                        None,
                        None
                      )
results['DName_Change'] = win32file.ReadDirectoryChangesW (
                        hDir,
                        1024,
                        True,
                         win32con.FILE_NOTIFY_CHANGE_DIR_NAME, 
                        None,
                        None
                        )
  results['Attributes_Change'] = win32file.ReadDirectoryChangesW (
                        hDir,
                        1024,
                        True,
                         win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES, 
                        None,
                        None

манипулируя этой структурой данных, вы сможете извлечь информацию о типе поведения из изменений

...