Python Snippets

Real-time File System Monitor with Pattern Matching

import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import re
from pathlib import Path
from typing import List, Callable, Optional

class PatternFileMonitor(FileSystemEventHandler):
    """
    Monitors file system events and filters them based on file patterns.
    """
    
    def __init__(self, patterns: List[str], callback: Callable[[str, str], None]):
        """
        Initialize the monitor with patterns and callback function.
        
        Args:
            patterns: List of file patterns to match (e.g., ['*.txt', '*.py'])
            callback: Function to call when matching files are modified
                     Takes parameters (event_type, file_path)
        """
        self.patterns = patterns
        self.callback = callback
        # Compile regex patterns for better performance
        self.compiled_patterns = [re.compile(pattern.replace('*', '.*')) for pattern in patterns]
    
    def matches_pattern(self, file_path: str) -> bool:
        """Check if file path matches any of the configured patterns."""
        path = Path(file_path)
        return any(pattern.match(path.name) for pattern in self.compiled_patterns)
    
    def on_modified(self, event):
        """Handle file modification events."""
        if not event.is_directory and self.matches_pattern(event.src_path):
            self.callback("modified", event.src_path)
    
    def on_created(self, event):
        """Handle file creation events."""
        if not event.is_directory and self.matches_pattern(event.src_path):
            self.callback("created", event.src_path)
    
    def on_deleted(self, event):
        """Handle file deletion events."""
        if not event.is_directory and self.matches_pattern(event.src_path):
            self.callback("deleted", event.src_path)

def monitor_directory(
    directory: str = ".", 
    patterns: List[str] = None, 
    recursive: bool = True,
    callback: Optional[Callable[[str, str], None]] = None
):
    """
    Monitor a directory for file changes matching specific patterns.
    
    Args:
        directory: Directory to monitor (default: current directory)
        patterns: File patterns to watch (default: all files)
        recursive: Whether to monitor subdirectories
        callback: Custom function to handle events
    """
    
    if patterns is None:
        patterns = ["*"]  # Match all files
    
    if callback is None:
        # Default callback function
        def default_callback(event_type, file_path):
            timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
            print(f"[{timestamp}] {event_type.upper()}: {file_path}")
        callback = default_callback
    
    # Create event handler
    event_handler = PatternFileMonitor(patterns, callback)
    
    # Create observer
    observer = Observer()
    observer.schedule(event_handler, directory, recursive=recursive)
    
    # Start monitoring
    observer.start()
    print(f"Monitoring {directory} for files matching: {patterns}")
    print("Press Ctrl+C to stop...")
    
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
        print("\nMonitoring stopped.")
    
    observer.join()

# Example usage with custom callback
def custom_file_handler(event_type: str, file_path: str):
    """Custom handler for specific file types."""
    file_size = Path(file_path).stat().st_size if Path(file_path).exists() else 0
    
    if event_type == "created":
        print(f"πŸ“„ NEW FILE: {file_path} ({file_size} bytes)")
    elif event_type == "modified":
        print(f"✏️  UPDATED: {file_path} ({file_size} bytes)")
    elif event_type == "deleted":
        print(f"πŸ—‘οΈ  DELETED: {file_path}")

# Example: Monitor Python files in current directory
if __name__ == "__main__":
    # Monitor only Python and text files in current directory
    monitor_directory(
        directory=".", 
        patterns=["*.py", "*.txt", "*.md"],
        callback=custom_file_handler
    )

What This Code Does

This snippet implements a real-time file system monitor that watches for changes to files matching specific patterns. It uses the watchdog library to efficiently track file system events (creation, modification, deletion) and filters them based on file name patterns.

Key Features

  1. Pattern Matching: Monitor only files that match specific patterns (e.g., *.py, config.*)
  2. Real-time Monitoring: Instantly detects file changes as they happen
  3. Custom Callbacks: Define your own response to file events
  4. Recursive Monitoring: Optionally monitor subdirectories
  5. Performance Optimized: Uses compiled regex patterns for efficient matching

Why It’s Useful

File system monitoring is essential for many development and operational tasks:

How to Run It

  1. Install Dependencies:
    pip install watchdog
    
  2. Basic Usage:
    # Monitor all files in current directory
    monitor_directory()
       
    # Monitor only Python files recursively
    monitor_directory(patterns=["*.py"], recursive=True)
       
    # Monitor specific directory with custom handler
    monitor_directory("/path/to/dir", patterns=["*.log", "*.txt"], callback=my_handler)
    
  3. Custom Handler Example:
    def my_handler(event_type, file_path):
        if event_type == "modified" and file_path.endswith(".py"):
            print(f"Python file changed: {file_path}")
            # Trigger automated testing, compilation, etc.
    

The script will run continuously until interrupted with Ctrl+C, providing real-time feedback on file system changes that match your criteria.