mirror of http://git.sairate.top/sairate/doc.git
112 lines
4.1 KiB
Python
112 lines
4.1 KiB
Python
from __future__ import annotations
|
|
|
|
import os.path
|
|
import platform
|
|
import threading
|
|
from typing import TYPE_CHECKING
|
|
|
|
from watchdog.events import (
|
|
DirCreatedEvent,
|
|
DirDeletedEvent,
|
|
DirModifiedEvent,
|
|
DirMovedEvent,
|
|
FileCreatedEvent,
|
|
FileDeletedEvent,
|
|
FileModifiedEvent,
|
|
FileMovedEvent,
|
|
generate_sub_created_events,
|
|
generate_sub_moved_events,
|
|
)
|
|
from watchdog.observers.api import DEFAULT_EMITTER_TIMEOUT, DEFAULT_OBSERVER_TIMEOUT, BaseObserver, EventEmitter
|
|
from watchdog.observers.winapi import close_directory_handle, get_directory_handle, read_events
|
|
|
|
if TYPE_CHECKING:
|
|
from ctypes.wintypes import HANDLE
|
|
|
|
from watchdog.events import FileSystemEvent
|
|
from watchdog.observers.api import EventQueue, ObservedWatch
|
|
from watchdog.observers.winapi import WinAPINativeEvent
|
|
|
|
|
|
class WindowsApiEmitter(EventEmitter):
|
|
"""Windows API-based emitter that uses ReadDirectoryChangesW
|
|
to detect file system changes for a watch.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
event_queue: EventQueue,
|
|
watch: ObservedWatch,
|
|
*,
|
|
timeout: float = DEFAULT_EMITTER_TIMEOUT,
|
|
event_filter: list[type[FileSystemEvent]] | None = None,
|
|
) -> None:
|
|
super().__init__(event_queue, watch, timeout=timeout, event_filter=event_filter)
|
|
self._lock = threading.Lock()
|
|
self._whandle: HANDLE | None = None
|
|
|
|
def on_thread_start(self) -> None:
|
|
self._whandle = get_directory_handle(self.watch.path)
|
|
|
|
if platform.python_implementation() == "PyPy":
|
|
|
|
def start(self) -> None:
|
|
"""PyPy needs some time before receiving events, see #792."""
|
|
from time import sleep
|
|
|
|
super().start()
|
|
sleep(0.01)
|
|
|
|
def on_thread_stop(self) -> None:
|
|
if self._whandle:
|
|
close_directory_handle(self._whandle)
|
|
|
|
def _read_events(self) -> list[WinAPINativeEvent]:
|
|
if not self._whandle:
|
|
return []
|
|
return read_events(self._whandle, self.watch.path, recursive=self.watch.is_recursive)
|
|
|
|
def queue_events(self, timeout: float) -> None:
|
|
winapi_events = self._read_events()
|
|
with self._lock:
|
|
last_renamed_src_path = ""
|
|
for winapi_event in winapi_events:
|
|
src_path = os.path.join(self.watch.path, winapi_event.src_path)
|
|
|
|
if winapi_event.is_renamed_old:
|
|
last_renamed_src_path = src_path
|
|
elif winapi_event.is_renamed_new:
|
|
dest_path = src_path
|
|
src_path = last_renamed_src_path
|
|
if os.path.isdir(dest_path):
|
|
self.queue_event(DirMovedEvent(src_path, dest_path))
|
|
if self.watch.is_recursive:
|
|
for sub_moved_event in generate_sub_moved_events(src_path, dest_path):
|
|
self.queue_event(sub_moved_event)
|
|
else:
|
|
self.queue_event(FileMovedEvent(src_path, dest_path))
|
|
elif winapi_event.is_modified:
|
|
cls = DirModifiedEvent if os.path.isdir(src_path) else FileModifiedEvent
|
|
self.queue_event(cls(src_path))
|
|
elif winapi_event.is_added:
|
|
isdir = os.path.isdir(src_path)
|
|
cls = DirCreatedEvent if isdir else FileCreatedEvent
|
|
self.queue_event(cls(src_path))
|
|
if isdir and self.watch.is_recursive:
|
|
for sub_created_event in generate_sub_created_events(src_path):
|
|
self.queue_event(sub_created_event)
|
|
elif winapi_event.is_removed:
|
|
self.queue_event(FileDeletedEvent(src_path))
|
|
elif winapi_event.is_removed_self:
|
|
self.queue_event(DirDeletedEvent(self.watch.path))
|
|
self.stop()
|
|
|
|
|
|
class WindowsApiObserver(BaseObserver):
|
|
"""Observer thread that schedules watching directories and dispatches
|
|
calls to event handlers.
|
|
"""
|
|
|
|
def __init__(self, *, timeout: float = DEFAULT_OBSERVER_TIMEOUT) -> None:
|
|
super().__init__(WindowsApiEmitter, timeout=timeout)
|