watchfiles
watchfiles copied to clipboard
Possible to watch single file for modifications?
I'm wanting to watch a single txt file for modifications, but when using the default example:
import asyncio
from watchfiles import awatch
async def main():
async for changes in awatch('/home/user/test_file.txt'):
print(changes)
asyncio.run(main())
When I modify test_file.txt, it will print the changes. However, subsequent changes no longer print any changes. This is likely to do with the file being deleted/re-added when one modifies the file and awatch probably thinks the file is now gone.
I have to put the test_file in it's own directory, then watch that directory for the changes which works and I can deal with that, but I'm just wondering if there is a way to continuously just watch a single file for modifications like in my above example?
Fundamentally there's not much more watchfiles can do if notify/the OS's fs events don't show anything.
But try debug=True
and see what's happening, maybe there's something we can fix here.
Also worth saying what OS you're on.
Linux Python 3.10
Here is the output with debug=True:
watcher: INotifyWatcher { channel: Sender { .. }, waker: Waker { inner: Waker { fd: File { fd: 8, path: "anon_inode:[eventfd]", read: true, write: true } } } }
raw-event: Event { kind: Access(Close(Write)), paths: ["/home/xxxx/test.txt"], attr:tracker: None, attr:flag: None, attr:info: None, attr:source: None }
raw-event: Event { kind: Modify(Metadata(Any)), paths: ["/home/xxxx/test.txt"], attr:tracker: None, attr:flag: None, attr:info: None, attr:source: None }
raw-event: Event { kind: Remove(File), paths: ["/home/xxxx/test.txt"], attr:tracker: None, attr:flag: None, attr:info: None, attr:source: None }
{(<Change.modified: 2>, '/home/xxxx/test.txt'), (<Change.deleted: 3>, '/home/xxxx/test.txt')}
It only prints the changes when I modify/save text.txt the first time. Subsequent modifications and saves does not print anything else.
EDIT: It seems it depends on what application I'm modifying the file with. When using the mousepad text editor within XFCE, this problem happens. If I simply use nano, it continues to show the changes.
Is there a particular raw event which you see with XFCE which is being logged but not prompting a change?
It's very likely that your editor is using atomic file saving (i.e, create new temp file, delete old file, then move the temp file to old file's path), due to this line:
raw-event: Event { kind: Remove(File), paths: ["/home/xxxx/test.txt"], attr:tracker: None, attr:flag: None, attr:info: None, attr:source: None }
My educated guest is that inotify
tracks changes to an inode
, not file path. So when a file was atomically saved, the original inode will be replaced with a new inode. And since inotify
only subscribes to changes of the original inode (since you ask watchfiles
to watch only one file), you won't see any more changes after the first save.
You can either:
- Watch the parent directory and subclass
watchfiles.DefaultFilter
to watch only the desired file path. - Turn off atomic save in your editor (can cause lost of data if interrupted mid-save)
The other option might be to use the force_polling=True
kwarg (added recently) which will force notify to use file polling which might detect the change more reliably.
Closing since we have force_polling
, any further issue will probably be a problem with notify
.