Skip to main content

A high-performance, asyncio-native, and developer-friendly file watching library.

Project description

🚀 yyds-fswatch

中文文档 (Chinese Version)

yyds-fswatch is a high-performance, asyncio-native, and developer-friendly file system watching library for Python. It is designed to fully replace and outperform the traditional watchdog library.


🌟 Core Advantages (vs Watchdog)

Feature yyds-fswatch (This Library) watchdog
API Usability 4-level progressive API (Decorators / Callbacks / Sync Iter / Async Iter) Verbose Observer + Handler class inheritance system
Async Integration Native coroutine support (async with and async for) Requires manual threadpools or loop.call_soon_threadsafe bridging
Linux Performance Zero additional threads (ctypes non-blocking inotify + loop.add_reader) Starts an independent Python blocking thread for each Emitter
Atomic Save Support $100%$ compatible. File watches automatically fall back to parent directory + top-level filter Direct file watching fails and loses watch handle upon Vim/VSCode atomic saves
Debounce & Coalesce Built-in 50ms sliding window state machine No debouncing; saving one file triggers dozens of redundant modified events
GitIgnore Pruning Automatically parses .gitignore and prunes the watch tree at registration, avoiding ENOSPC No automatic pruning; watching large repos (e.g. node_modules) easily exhausts descriptors
Rename Sync Strong cookie pairing. Automatically synchronizes path mappings for renamed subdirectories Subdirectory path mappings can get out of sync, reporting stale paths for subsequent events

📦 Installation

To install yyds-fswatch via pip:

pip install -U yyds-fswatch

Or install from source inside the project directory:

pip install .

🛠️ Progressive API Usage Examples

yyds-fswatch provides four API levels to suit different integration needs.

1. Level 1: Decorator Mode

The most intuitive mode, supporting both synchronous and asynchronous callback functions.

from yyds_fswatch import FSWatcher

w = FSWatcher("./watch_dir", recursive=True, debounce=0.05)

@w.on("created")
def on_created(event):
    print(f"🔥 [CREATED] {'Directory' if event.is_directory else 'File'}: {event.src_path}")

@w.on("modified")
def on_modified(event):
    print(f"⚡ [MODIFIED]: {event.src_path}")

@w.on("deleted")
def on_deleted(event):
    print(f"🗑️ [DELETED]: {event.src_path}")

@w.on("moved")
def on_moved(event):
    print(f"📦 [MOVED]: {event.src_path} -> {event.dest_path}")

# Start watching (blocks current thread; run in a separate thread if non-blocking is needed)
try:
    w.start()
except KeyboardInterrupt:
    w.stop()

2. Level 2: One-Liner Callback

Ideal for quick scripts and debugging.

from yyds_fswatch import watch

# Blocks and runs callback on any file changes
watch("./watch_dir", on_change=lambda event: print(f"⚡ Event: {event.event_type} on {event.src_path}"))

3. Level 3: Sync Standard Iterator

Uses the with context manager to process file events sequentially like a generator.

from yyds_fswatch import FSWatcher

# Starts a background thread within the context and auto-cleans resources upon exit
with FSWatcher("./watch_dir", debounce=0.05) as w:
    for event in w:
        print(f"🔄 [Sync Iter] {event.event_type.upper()}: {event.src_path}")
        if event.event_type == "deleted" and "stop_trigger" in event.src_path:
            break  # Exiting the loop will cleanly terminate the watcher

4. Level 4: Async Native Iterator - ✨ Recommended

Integrates seamlessly with modern async frameworks (FastAPI, Sanic, Tornado). Pure asyncio, zero extra threads.

import asyncio
from yyds_fswatch import FSWatcher

async def main():
    # Pure async context manager and iterator
    async with FSWatcher("./watch_dir", debounce=0.05) as w:
        async for event in w:
            print(f"👀 [Async Iter] {event.event_type.upper()}: {event.src_path}")

asyncio.run(main())

💻 Command Line Interface (CLI)

yyds-fswatch provides a command-line tool with rich-colored logging output out of the box.

Usage

# Watch the current directory
yyds-fswatch .

# Watch a specific path recursively with 100ms debounce
yyds-fswatch /path/to/project --recursive --debounce 0.1

# Disable .gitignore filtering
yyds-fswatch . --no-gitignore

Example Output

yyds-fswatch v0.1.0 starting up...
Watching paths: /home/user/project
Options: recursive=True, debounce=0.05s, gitignore=True
Press Ctrl+C to stop.

[09:15:20] CREATED  /home/user/project/new_file.py
[09:15:20] MODIFIED /home/user/project/new_file.py
[09:15:24] MOVED    /home/user/project/new_file.py -> /home/user/project/archived.py
[09:15:30] DELETED  /home/user/project/archived.py

🛡️ Deep Technical Optimization Details

  1. Atomic Save Filtering: For file watches (e.g. data.json), the system automatically monitors its parent directory and intercepts MOVED_TO and CREATE events. It filters out intermediate temporary files, guaranteeing that updates are $100%$ captured regardless of the editor's write mechanism.
  2. High-Performance Prefix Matching: Bypasses the expensive try-except ValueError and Path.resolve() system calls during event filtering. Uses in-memory absolute path checks and precomputed string prefix matching to achieve a throughput of over 100,000 checks per second.
  3. Graceful Lifecycle Management: When exiting async for loops or catching KeyboardInterrupt, the library automatically pushes a _STOP_SENTINEL to cleanly close async event queue hooks, preventing coroutine or thread leaks.

📈 Long-Term Resource Stability (Production Ready)

yyds-fswatch is designed for mission-critical, long-running daemon processes (e.g., chat bots, automated deployment monitors, web servers). It enforces strict resource bounds to run indefinitely (months/years) without memory drift or handle leaks:

  • Bounded LRU Cache: The .gitignore filter is backed by a cached lookup capped at a maximum of 4,096 entries (lru_cache(maxsize=4096)). Memory consumption remains constant ($O(1)$) and never leaks even when scanning millions of unique files.
  • Idle CPU Overhead is $0.00%$: All native platform drivers (Linux inotify, macOS FSEvents, Windows ReadDirectoryChangesW) are event-driven and wait in the OS kernel until events occur. No polling threads or busy-waiting loops are active when idle.
  • Auto-Expiring State Machine: Temporary states (such as rename cookie matching and debouncing queues) auto-expire and purge themselves within 50ms, maintaining a baseline size of zero when idle.
  • Watches Pruning & Handle Safety: Subdirectory watches are pruned dynamically at startup based on .gitignore rules (avoiding ENOSPC descriptor limits). System handles/file descriptors are synchronized dynamically when folders are deleted, and are safely closed upon stop.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

yyds_fswatch-0.1.7.tar.gz (25.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

yyds_fswatch-0.1.7-py3-none-any.whl (28.6 kB view details)

Uploaded Python 3

File details

Details for the file yyds_fswatch-0.1.7.tar.gz.

File metadata

  • Download URL: yyds_fswatch-0.1.7.tar.gz
  • Upload date:
  • Size: 25.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.11

File hashes

Hashes for yyds_fswatch-0.1.7.tar.gz
Algorithm Hash digest
SHA256 211840337bc7fdaa51e080635a78fa81a03629a2144f1235f78e5cbd8cef78f4
MD5 e2d60d3468cec17db7219e8df650b1d3
BLAKE2b-256 879c7d2c85aa9478812ac9a0f80ad51c7d85b0e82ab3d1a914c1cfc28310a5ab

See more details on using hashes here.

File details

Details for the file yyds_fswatch-0.1.7-py3-none-any.whl.

File metadata

  • Download URL: yyds_fswatch-0.1.7-py3-none-any.whl
  • Upload date:
  • Size: 28.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.11

File hashes

Hashes for yyds_fswatch-0.1.7-py3-none-any.whl
Algorithm Hash digest
SHA256 bd167ebda6f6c4d95a7c91db58696115fcbc8600ab118b7d19c94af263673159
MD5 d44ca7c340508c626d464034ea1e4fe3
BLAKE2b-256 8c99629631cd3c156198979407191e4690b46cfad59c39441e11b0ec3cc85485

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page