Skip to main content

A lightweight, asyncio-friendly, cross-platform file watching library.

Project description

🚀 yyds-fswatch

中文文档 (Chinese Version)

yyds-fswatch is a lightweight, asyncio-friendly, and developer-friendly file system watching library for Python. It offers a compact alternative to observer/handler-style APIs while keeping platform fallbacks explicit and observable.


🌟 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 File watches monitor the parent directory and normalize inbound replacement moves to target-file events Behavior depends on the observer and handler configuration
Debounce & Coalesce Built-in 50ms sliding window state machine No debouncing; saving one file triggers dozens of redundant modified events
GitIgnore Pruning Dynamically reloads nested .gitignore files and supports the commonly used glob subset Requires application-level filtering
Rename Sync Uses Linux cookies, Windows packets, FSEvents heuristics, and inode pairing in Polling Backend-dependent
Overflow Resilience Emits overflow; Linux rebuilds its recursive watch tree after kernel overflow Backend-dependent
Root Self-Healing Restarts the backend when watched roots disappear or reappear Usually requires observer recreation

📦 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)
# Blocks until Ctrl+C or until another thread calls w.stop().
w.start()

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 with modern async frameworks (FastAPI, Sanic, Tornado). Linux uses the caller's asyncio loop directly; Windows/macOS native backends use an OS-waiting helper thread.

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())

Lifecycle, backpressure, and backend controls

import threading
from yyds_fswatch import FSWatcher

w = FSWatcher(
    "./watch_dir",
    max_queue_size=4096,   # 0 opts into an unbounded queue
    backend="auto",       # auto | native | polling
    recovery_interval=2.0,
)
thread = threading.Thread(target=w.start)
thread.start()
w.wait_until_ready(timeout=5)

# ...later
w.stop()                  # public, idempotent, and thread-safe
thread.join()

Use target_type="file" when a watched file does not exist yet. Set ignore_common_dirs=False when changes inside dist, build, node_modules, and other common generated directories must be reported.


🛡️ Deep Technical Optimization Details

  1. Atomic Save Normalization: For single-file watches (e.g. data.json), the system monitors the parent directory. A temporary file moved onto an existing target is emitted as modified on the target path.
  2. Cached Path Filtering: Absolute-root prefix checks and a bounded 4,096-entry decision cache reduce repeated filtering work. No unverified throughput number is claimed; benchmark on the intended directory tree and platform.
  3. Lifecycle & Memory Controls: Context managers and the public stop() method release backend resources. User-space queues are bounded to 4,096 events by default and emit overflow when consumers fall behind.
  4. System Buffer Overflow Handling: OS queue saturation emits an overflow event. Linux also recreates its inotify instance and recursive watch tree so missed directory registrations do not remain permanent.
  5. Hierarchical Nested .gitignore Support: Nested files are invalidated when they change. The built-in parser supports common *, ?, **, anchoring, directory, and negation patterns; it is intentionally documented as a practical subset rather than byte-for-byte Git equivalence.
  6. Self-Healing Root Recovery: If a watched root directory is deleted or unmounted, the watcher pauses native OS watches and falls back to a low-overhead retry loop. Monitoring is automatically re-established the moment the path reappears.

📈 Resource and Long-Running Process Controls

yyds-fswatch includes controls useful for long-running processes. Production adoption should still be validated on every target OS and file-system type:

  • Bounded queues and cache: Event queues and the .gitignore decision cache default to 4,096 entries. Backend descriptor maps naturally scale with the number of watched directories.
  • Low idle overhead: Native drivers wait on OS facilities. A lightweight lifecycle heartbeat supports prompt cross-thread shutdown, a configurable root-health probe runs every two seconds by default, and the Polling backend scans at its configured interval.
  • 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.2.0.tar.gz (41.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.2.0-py3-none-any.whl (28.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: yyds_fswatch-0.2.0.tar.gz
  • Upload date:
  • Size: 41.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.2.0.tar.gz
Algorithm Hash digest
SHA256 a32a681d91005fa27ac7f0ceae2d84a2c1c4527140152374bc52ba3cd8693e62
MD5 b5f147e29545f551fb23ceea1b86b25e
BLAKE2b-256 778f6ba0348e6ddf22861861f75bb8eaff739f6c6ab25297a8c93768221cbe90

See more details on using hashes here.

File details

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

File metadata

  • Download URL: yyds_fswatch-0.2.0-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.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4e68a7729e149524c26eb89459a7d676993c586ce13b50d12b75769991ac0206
MD5 e8c426747515dc41cd4615f73f36b622
BLAKE2b-256 1c22d19b6a5253dad22c1cda9fc86d74794e186946c46b77920fcc596e80a2d2

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