67 lines
		
	
	
		
			2.0 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			67 lines
		
	
	
		
			2.0 KiB
		
	
	
	
		
			Python
		
	
	
	
from __future__ import annotations
 | 
						|
 | 
						|
import logging
 | 
						|
import threading
 | 
						|
from typing import TYPE_CHECKING
 | 
						|
 | 
						|
from watchdog.utils import BaseThread
 | 
						|
 | 
						|
if TYPE_CHECKING:
 | 
						|
    from typing import Callable
 | 
						|
 | 
						|
    from watchdog.events import FileSystemEvent
 | 
						|
 | 
						|
logger = logging.getLogger(__name__)
 | 
						|
 | 
						|
 | 
						|
class EventDebouncer(BaseThread):
 | 
						|
    """Background thread for debouncing event handling.
 | 
						|
 | 
						|
    When an event is received, wait until the configured debounce interval
 | 
						|
    passes before calling the callback.  If additional events are received
 | 
						|
    before the interval passes, reset the timer and keep waiting.  When the
 | 
						|
    debouncing interval passes, the callback will be called with a list of
 | 
						|
    events in the order in which they were received.
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(
 | 
						|
        self,
 | 
						|
        debounce_interval_seconds: int,
 | 
						|
        events_callback: Callable[[list[FileSystemEvent]], None],
 | 
						|
    ) -> None:
 | 
						|
        super().__init__()
 | 
						|
        self.debounce_interval_seconds = debounce_interval_seconds
 | 
						|
        self.events_callback = events_callback
 | 
						|
 | 
						|
        self._events: list[FileSystemEvent] = []
 | 
						|
        self._cond = threading.Condition()
 | 
						|
 | 
						|
    def handle_event(self, event: FileSystemEvent) -> None:
 | 
						|
        with self._cond:
 | 
						|
            self._events.append(event)
 | 
						|
            self._cond.notify()
 | 
						|
 | 
						|
    def stop(self) -> None:
 | 
						|
        with self._cond:
 | 
						|
            super().stop()
 | 
						|
            self._cond.notify()
 | 
						|
 | 
						|
    def run(self) -> None:
 | 
						|
        with self._cond:
 | 
						|
            while True:
 | 
						|
                # Wait for first event (or shutdown).
 | 
						|
                self._cond.wait()
 | 
						|
 | 
						|
                if self.debounce_interval_seconds:
 | 
						|
                    # Wait for additional events (or shutdown) until the debounce interval passes.
 | 
						|
                    while self.should_keep_running():
 | 
						|
                        if not self._cond.wait(timeout=self.debounce_interval_seconds):
 | 
						|
                            break
 | 
						|
 | 
						|
                if not self.should_keep_running():
 | 
						|
                    break
 | 
						|
 | 
						|
                events = self._events
 | 
						|
                self._events = []
 | 
						|
                self.events_callback(events)
 |