Ergonomic, decorator-based wrapper around watchfiles with a beautiful Rich/Typer CLI.
Project description
FlowWatch
FlowWatch is a tiny ergonomic layer on top of watchfiles
that makes it easy to build file-driven workflows using simple decorators and a pretty
Rich + Typer powered CLI.
Instead of wiring watchfiles.watch() manually in every project, you declare:
- what folder(s) you want to watch
- which patterns you care about (e.g.
*.mxf,*.json) - which function should run for a given event (created / modified / deleted)
FlowWatch takes care of:
- subscribing to all roots in a single watcher loop
- debouncing and recursive watching
- dispatching events to handlers with a small thread pool
- optional processing of existing files on startup
- nicely formatted logs and a CLI overview of registered handlers
Installation
FlowWatch is published as a normal Python package.
Using uv:
uv add flowwatch
Using pip:
pip install flowwatch
Core concepts
1. FileEvent
Handlers receive a FileEvent object describing what happened:
event.change– awatchfiles.Change(added,modified,deleted)event.path–pathlib.Pathpointing to the fileevent.root– the root folder you registeredevent.pattern– the pattern that matched (if any)
It also has convenience properties:
event.is_createdevent.is_modifiedevent.is_deleted
2. Decorators
You register handlers using decorators from flowwatch:
@on_created(root, pattern="*.txt", process_existing=True)@on_modified(root, pattern="*.json")@on_deleted(root, pattern="*.bak")@on_any(root, pattern="*.*")
Behind the scenes these attach to a global FlowWatchApp instance, which you can run
using flowwatch.run() or via the CLI.
Basic usage (embedded in your code)
The decorator + runner pattern is the simplest:
from pathlib import Path
from flowwatch import FileEvent, on_created, run
WATCH_DIR = Path("inbox")
WATCH_DIR.mkdir(exist_ok=True)
@on_created(str(WATCH_DIR), pattern="*.txt", process_existing=True)
def handle_new_text(event: FileEvent) -> None:
print(f"New text file: {event.path}")
print("Was it created?", event.is_created)
if __name__ == "__main__":
run() # blocks until Ctrl+C
Run it:
python my_script.py
Then drop *.txt files into inbox/ and watch the handler fire.
CLI usage (Typer + Rich)
FlowWatch also ships with a small CLI, exposed as the flowwatch command.
You typically:
- Create a watchers module that only defines handlers.
- Call
flowwatch run your_module.path.
1. Create a watchers module
For example, myproject/watchers.py:
from pathlib import Path
from flowwatch import FileEvent, on_created
BASE = Path("/media/incoming")
@on_created(str(BASE), pattern="*.mxf", process_existing=True)
def handle_mxf(event: FileEvent) -> None:
print(f"[handler] New MXF at {event.path}")
2. Run via CLI
flowwatch run myproject.watchers
The CLI will:
- import
myproject.watchers - discover all handlers registered via decorators
- show a Rich table with handlers, roots, events, patterns, and priorities
- start the watcher loop and stream pretty logs to your terminal
You can customize:
flowwatch run myproject.watchers \
--debounce 8 \
--max-workers 8 \
--no-recursive \
--log-level DEBUG
Using FlowWatch with Docker
A common pattern is to run FlowWatch as its own worker container:
services:
backend:
build: ./backend
volumes:
- media:/media
flowwatch:
build: ./backend
command: flowwatch run myproject.watchers
depends_on:
- backend
volumes:
- media:/media
restart: unless-stopped
volumes:
media:
Where myproject/watchers.py inside the image contains your handlers and watches
paths under /media (shared volume with the backend).
FlowWatchApp (advanced / custom apps)
If you need more control than the global decorators/CLI, you can instantiate your
own FlowWatchApp:
from pathlib import Path
from watchfiles import Change
from flowwatch import FileEvent, FlowWatchApp
app = FlowWatchApp(name="my-custom-app", debounce=0.7, max_workers=8)
def handle_any(event: FileEvent) -> None:
print(event.change, event.path)
app.add_handler(
handle_any,
root=Path("data"),
events=[Change.added, Change.modified, Change.deleted],
pattern="*.*",
process_existing=True,
)
app.run()
This is the same engine used under the hood by the decorators and CLI.
When should you use FlowWatch?
FlowWatch is a good fit when you want:
- simple file pipelines like:
- "When a new MXF appears here, run this ingester."
- "When a JSON config changes, reload some state."
- "When a sidecar file is deleted, clean up something else."
- readable, declarative code:
- your intent is obvious from the decorators
- a pretty terminal UX when running workers in Docker, k8s, or bare metal
It is not trying to be a full-blown workflow engine. Think of it as a thin,
Pythonic glue layer over watchfiles.
Roadmap / ideas
Potential future additions:
asyncmode usingwatchfiles.awatch- optional structured JSON logs for production
- pattern-based routing helpers (e.g. per-extension multiplexing)
- more first-class Docker/Kubernetes examples
If you end up using FlowWatch in your own projects, feel free to open issues or PRs with real-world improvements.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flowwatch-0.1.0.tar.gz.
File metadata
- Download URL: flowwatch-0.1.0.tar.gz
- Upload date:
- Size: 19.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.5.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bc17ee7908815358154310ac515cda36c4312ec997c9a55de5b3c1b33d8fae20
|
|
| MD5 |
8adf3351ec39d54881dda17ef7996f03
|
|
| BLAKE2b-256 |
91f5dd0c370763934c29cd48d837474649a45e6aff73dadb491b574a3f9c82ad
|
File details
Details for the file flowwatch-0.1.0-py3-none-any.whl.
File metadata
- Download URL: flowwatch-0.1.0-py3-none-any.whl
- Upload date:
- Size: 10.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.5.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
06dc4493bba90b54be4213ad7b67afb3a3307c38c6bf86f68684898ecca8f789
|
|
| MD5 |
bfaffa93d059d386d8ae8f2607d305b8
|
|
| BLAKE2b-256 |
cd491da75af9d8ebb5f31eb366324e9b11b90d5f9a54509011c3893c2079931e
|