File & directory watcher with line-level, word-level, and JSON patch diffs plus a CLI tool
Project description
diffwatch
Watch any file or directory for changes and get structured diffs — line-level, word-level, JSON Patch (RFC 6902), callbacks, and a CLI tool. Zero dependencies.
Installation
pip install diffwatch
No dependencies. Requires Python 3.8+.
Quick Start
from diffwatch import DiffWatch
watcher = DiffWatch()
@watcher.on_change
def handle(event):
print(f"{event.type}: {event.path}")
print(event.diff.unified_diff())
watcher.watch("config.yaml").start(block=True)
CLI
# Watch a file with unified diff output (default)
diffwatch config.yaml
# Watch a directory recursively, only .py files
diffwatch ./src --recursive --ext .py
# Word-level inline diff
diffwatch README.md --format word
# JSON Patch output for JSON files
diffwatch settings.json --format json-patch
# Summary only (lines added/removed)
diffwatch . --recursive --format summary
CLI Options
| Option | Description |
|---|---|
--format |
unified (default), line, word, json-patch, summary |
--recursive, -r |
Watch directories recursively |
--ext .py .yaml |
Only watch files with these extensions |
--interval 0.5 |
Polling interval in seconds |
--context 3 |
Context lines for unified/line diff |
Python API
Watch files and directories
from diffwatch import DiffWatch
watcher = DiffWatch(interval=1.0)
# Watch a single file
watcher.watch("config.yaml")
# Watch a directory (non-recursive)
watcher.watch("./configs")
# Watch recursively, only .json files
watcher.watch("./src", recursive=True, extensions=[".json", ".yaml"])
watcher.start() # background thread
watcher.stop() # graceful stop
watcher.start(block=True) # block until Ctrl+C
Callbacks
# Any change (modified, created, deleted)
@watcher.on_change
def on_any(event):
print(event.type, event.path)
# Creation only
@watcher.on_create
def on_create(event):
print("New file:", event.path)
# Deletion only
@watcher.on_delete
def on_delete(event):
print("Deleted:", event.path)
# Register without decorator
watcher.on_change(my_callback)
Diff formats
diff = event.diff # DiffResult object
# Unified diff string (like diff -u)
print(diff.unified_diff(context=3))
# Structured line diff
for chunk in diff.line_diff():
if chunk["type"] != "equal":
print(chunk["type"], chunk["old_lines"], "→", chunk["new_lines"])
# Structured word diff
for token in diff.word_diff():
print(token["type"], repr(token["value"]))
# Inline word diff string
from diffwatch.diff import word_diff_inline
print(word_diff_inline(old, new))
# "Hello [-world-] [+Python+]!"
# JSON Patch (RFC 6902) — only for JSON files
ops = diff.json_patch()
# [{"op": "replace", "path": "/port", "value": 9090}]
# Quick summary
print(diff.summary())
# {"path": "...", "lines_added": 3, "lines_removed": 1, ...}
One-shot check (no background thread)
events = watcher.check_once()
for event in events:
print(event.type, event.path)
API Reference
DiffWatch
| Method | Description |
|---|---|
watch(path, recursive, extensions) |
Register a file or directory |
on_change(func) |
Callback for any change (decorator or direct) |
on_create(func) |
Callback for file creation |
on_delete(func) |
Callback for file deletion |
start(block=False) |
Start background watcher thread |
stop() |
Stop the watcher |
check_once() |
Poll once and return list of FileEvents |
FileEvent
| Attribute | Type | Description |
|---|---|---|
type |
str |
"modified", "created", or "deleted" |
path |
str |
Absolute path of the changed file |
diff |
DiffResult |
Diff result with old and new content |
occurred_at |
datetime |
When the change was detected |
DiffResult
| Method | Returns | Description |
|---|---|---|
line_diff(context=3) |
list[dict] |
Structured line diff |
word_diff() |
list[dict] |
Structured word diff |
unified_diff(context=3) |
str |
Unified diff string |
json_patch() |
list[dict] |
RFC 6902 patch ops |
summary() |
dict |
Lines added/removed summary |
Running Tests
pip install pytest
pytest tests/ -v
License
MIT © prabhay759
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 diffwatch-1.0.0.tar.gz.
File metadata
- Download URL: diffwatch-1.0.0.tar.gz
- Upload date:
- Size: 12.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
53f95ae5d8cfd7c247575ac0b4e18ae731c66b26c7ea44b5e48f17da99c4f017
|
|
| MD5 |
11f0275e5b654b2e6f45ff37eeeef1fd
|
|
| BLAKE2b-256 |
50fae532eaa55c4c93ca896d8ae90df99a4feeaaf66d3f8463490484a293a88e
|
Provenance
The following attestation bundles were made for diffwatch-1.0.0.tar.gz:
Publisher:
publish.yml on prabhay759/diffwatch
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
diffwatch-1.0.0.tar.gz -
Subject digest:
53f95ae5d8cfd7c247575ac0b4e18ae731c66b26c7ea44b5e48f17da99c4f017 - Sigstore transparency entry: 1220491092
- Sigstore integration time:
-
Permalink:
prabhay759/diffwatch@2e9e2021d8d56f0af9aecb02ede9f75d611cb076 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/prabhay759
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2e9e2021d8d56f0af9aecb02ede9f75d611cb076 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file diffwatch-1.0.0-py3-none-any.whl.
File metadata
- Download URL: diffwatch-1.0.0-py3-none-any.whl
- Upload date:
- Size: 10.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8caec5037718006f8d00f43405a120cb2c82c3a559cb64ec9b29e95e8af15bc8
|
|
| MD5 |
8050f72689b775310a882514732eaa90
|
|
| BLAKE2b-256 |
0d2c879161d9dff12049de5272dffbe9a044ec558250d6e09556ec39a1af0162
|
Provenance
The following attestation bundles were made for diffwatch-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on prabhay759/diffwatch
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
diffwatch-1.0.0-py3-none-any.whl -
Subject digest:
8caec5037718006f8d00f43405a120cb2c82c3a559cb64ec9b29e95e8af15bc8 - Sigstore transparency entry: 1220491153
- Sigstore integration time:
-
Permalink:
prabhay759/diffwatch@2e9e2021d8d56f0af9aecb02ede9f75d611cb076 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/prabhay759
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2e9e2021d8d56f0af9aecb02ede9f75d611cb076 -
Trigger Event:
workflow_dispatch
-
Statement type: