Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KeyError in __handle_inotify_event #1

Closed
backbord opened this issue Jun 13, 2015 · 9 comments
Closed

KeyError in __handle_inotify_event #1

backbord opened this issue Jun 13, 2015 · 9 comments

Comments

@backbord
Copy link
Contributor

Hi, I've recently started to use your nice little project in version 0.2.3 (I used pyinotify before).
However, I stumbled across the following error and hope that you can help me.

Traceback (most recent call last):
  File \"test.py\", line 88, in <module>
    sys.exit(main())
  File \"test.py\", line 82, in main
    handle_events(path)
  File \"test.py\", line 49, in handle_events
    for event in notifier.event_gen():
  File \"/foo/bar/lib/python2.7/site-packages/inotify/adapters.py\", line 193, in event_gen
    for event in self.__i.event_gen():
  File \"/foo/bar/lib/python2.7/site-packages/inotify/adapters.py\", line 148, in event_gen
    in self.__handle_inotify_event(fd, event_type):
  File \"/foo/bar/lib/python2.7/site-packages/inotify/adapters.py\", line 134, in __handle_inotify_event
    path = self.__watches_r[header.wd]
KeyError: 226

The number varies. I've seen all from 9 to 10 and from 220 to 229.
Have you seen this error before and can advice?

The function handle_events(path) is intended to announce all files under a path whenever they are closed after writing. It is defined as follows.

def handle_events(path):

    # we intend to react on files being closed after writing and directories being created
    mask = inotify.constants.IN_CLOSE_WRITE | inotify.constants.IN_CREATE

    # block_duration_s is the pause [s] between calls to the kernel
    # cycle: (poll for events, yield events sequentially, yield None, sleep for block_duration_s, loop)
    notifier = inotify.adapters.InotifyTree(path=path, mask=mask, block_duration_s=1)

    for event in notifier.event_gen():
        if event is None:
            continue

        (header, _type_names, watch_path, filename) = event

        # InotifyTree gives IN_ISDIR, IN_CREATE, IN_DELETE regardless of the actual mask for technical reasons
        # we ignore everything not in our mask
        if not header.mask & mask:
            continue

        # a file has been closed after writing. something to announce.
        if header.mask & inotify.constants.IN_CLOSE_WRITE:
            fpath = os.path.join(watch_path, filename)
            announce_file(fpath)

        # inotify adds a watch for the newly created path automatically (and removes it if the path is deleted)
        # but there might already be some files in the folder before the watch is active. let's make sure to announce them
        if header.mask & inotify.constants.IN_CREATE & inotify.constants.IN_ISDIR:
            announce_all(watch_path)

Best regards,
Tim

@dsoprea
Copy link
Owner

dsoprea commented Jun 13, 2015

I'll have to take a look a it later today. This is a dictionary of all
current watches (using the "wd" a descriptor returned by the kernel for
each). It looks like the watch is being cleaned-up due to some other event.

On Sat, Jun 13, 2015 at 5:25 AM, backbord notifications@github.com wrote:

Hi, I've recently started to use your nice little project in version 0.2.3
(I used pyinotify before).
However, I stumbled across the following error and hope that you can help
me.

Traceback (most recent call last):
File "test.py", line 88, in
sys.exit(main())
File "test.py", line 82, in main
handle_events(path)
File "test.py", line 49, in handle_events
for event in notifier.event_gen():
File "/foo/bar/lib/python2.7/site-packages/inotify/adapters.py", line 193, in event_gen
for event in self.__i.event_gen():
File "/foo/bar/lib/python2.7/site-packages/inotify/adapters.py", line 148, in event_gen
in self.__handle_inotify_event(fd, event_type):
File "/foo/bar/lib/python2.7/site-packages/inotify/adapters.py", line 134, in __handle_inotify_event
path = self.__watches_r[header.wd]
KeyError: 226

The number varies. I've seen all from 9 to 10 and from 220 to 229.
Have you seen this error before and can advice?

The function handle_events(path) is intended to announce all files under
a path whenever they are closed after writing. It is defined as follows.

def handle_events(path):

# we intend to react on files being closed after writing and directories being created
mask = inotify.constants.IN_CLOSE_WRITE | inotify.constants.IN_CREATE

# block_duration_s is the pause [s] between calls to the kernel
# cycle: (poll for events, yield events sequentially, yield None, sleep for block_duration_s, loop)
notifier = inotify.adapters.InotifyTree(path=path, mask=mask, block_duration_s=1)

for event in notifier.event_gen():
    if event is None:
        continue

    (header, _type_names, watch_path, filename) = event

    # InotifyTree gives IN_ISDIR, IN_CREATE, IN_DELETE regardless of the actual mask for technical reasons
    # we ignore everything not in our mask
    if not header.mask & mask:
        continue

    # a file has been closed after writing. something to announce.
    if header.mask & inotify.constants.IN_CLOSE_WRITE:
        fpath = os.path.join(watch_path, filename)
        announce_file(fpath)

    # inotify adds a watch for the newly created path automatically (and removes it if the path is deleted)
    # but there might already be some files in the folder before the watch is active. let's make sure to announce them
    if header.mask & inotify.constants.IN_CREATE & inotify.constants.IN_ISDIR:
        announce_all(watch_path)

Best regards,
Tim


Reply to this email directly or view it on GitHub
#1.

@backbord
Copy link
Contributor Author

At closer inspection, the error pops up regularly after my cleanup process removed old files and empty subdirectories from the watched directory $HOME/mirror.

find $HOME/mirror -mmin +2880 -type f -exec rm -f {} \;
find $HOME/mirror -depth -type d -empty -exec rmdir {} \;

Could it be the case that the IN_DELETE events for files and directories which inotify creates are out of order (or read out of order by PyInotify) so that the watch for a subdir is removed (with del self.__watches_r[wd] in line 71 of adapters.py) before the IN_DELETE for a file in that subdir is processed?

Please let me know if I can help to find the cause. :-)

@backbord
Copy link
Contributor Author

Hm, the watched $HOME/mirror gives PyInotify some trouble and as event_gen() is a generator, I'm having trouble handling exceptions from the outside.

From the traceback in my first post and the following one, I identified two places where removed watches appear to cause problems.

Traceback (most recent call last):
  File \"test.py\", line 88, in <module>
    sys.exit(main())
  File \"test.py\", line 82, in main
    handle_events(path)
  File \"test.py\", line 49, in handle_events
    for event in notifier.event_gen():
  File \"/foo/bar/lib/python2.7/site-packages/inotify/adapters.py\", line 215, in event_gen
    self.__i.remove_watch(full_path, superficial=True)
  File \"/foo/bar/lib/python2.7/site-packages/inotify/adapters.py\", line 68, in remove_watch
    wd = self.__watches[path]
KeyError: 'some/deleted/directory'

I'll let my program run with this patch to PyInotify and will get back to you if I see anything interesting.

--- inotify/adapters.py
+++ inotify/adapters.py
@@ -65,7 +65,9 @@
         our tracking since inotify already cleans-up the watch.
         """

-        wd = self.__watches[path]
+        wd = self.__watches.get(path)
+        if wd is None:
+            return

         del self.__watches[path]
         del self.__watches_r[wd]
@@ -131,7 +133,9 @@

             self.__buffer = self.__buffer[event_length:]

-            path = self.__watches_r[header.wd]
+            path = self.__watches_r.get(header.wd)
+            if path is None:
+                break
             yield (header, type_names, path, filename)

             buffer_length = len(self.__buffer)

Again, thanks for putting together such a nice straightforward code!

Best regards,
Tim

@dsoprea
Copy link
Owner

dsoprea commented Jun 16, 2015

Hmm. I prefer very strict code. However, as you mentioned before, things
might be coming in out-of-order (child events being received after the
child subdirectory was already removed).
On Jun 16, 2015 3:13 AM, "backbord" notifications@github.com wrote:

Hm, the watched $HOME/mirror gives PyInotify some trouble and as
event_gen() is a generator, I'm having trouble handling exceptions from
the outside.

From the traceback in my first post and the following one, I identified
two places where removed watches appear to cause problems.

Traceback (most recent call last):
File "test.py", line 88, in
sys.exit(main())
File "test.py", line 82, in main
handle_events(path)
File "test.py", line 49, in handle_events
for event in notifier.event_gen():
File "/foo/bar/lib/python2.7/site-packages/inotify/adapters.py", line 215, in event_gen
self.__i.remove_watch(full_path, superficial=True)
File "/foo/bar/lib/python2.7/site-packages/inotify/adapters.py", line 68, in remove_watch
wd = self.__watches[path]
KeyError: 'some/deleted/directory'

I'll let my program run with this patch to PyInotify and will get back to
you if I see anything interesting.

--- inotify/adapters.py+++ inotify/adapters.py@@ -65,7 +65,9 @@
our tracking since inotify already cleans-up the watch.
"""

  •    wd = self.__watches[path]+        wd = self.__watches.get(path)+        if wd is None:+            return
    
     del self.__watches[path]
     del self.__watches_r[wd]@@ -131,7 +133,9 @@
    
         self.__buffer = self.__buffer[event_length:]
    
  •        path = self.__watches_r[header.wd]+            path = self.__watches_r.get(header.wd)+            if path is None:+                break
         yield (header, type_names, path, filename)
    
         buffer_length = len(self.__buffer)
    

Again, thanks for putting together such a nice straightforward code!

Best regards,
Tim


Reply to this email directly or view it on GitHub
#1 (comment).

@backbord
Copy link
Contributor Author

Hi Dustin,
I can neither catch nor work around an exception thrown from the generator. (See for example http://stackoverflow.com/a/11366139/3985756)
However, I agree, that strict code is the way to go -- do you have an idea on how to cope with events being handled out-of-order?
My python is a bit too limited to come up with a straight-up answer. :-)

Best regards,
Tim

@dsoprea
Copy link
Owner

dsoprea commented Jun 16, 2015

I think your way is already the best way for IDs that aren't going to be
reappearing.

Dustin
On Jun 16, 2015 3:59 AM, "backbord" notifications@github.com wrote:

Hi Dustin,
I can neither catch nor work around an exception thrown from the
generator. (See for example http://stackoverflow.com/a/11366139/3985756)
However, I agree, that strict code is the way to go -- do you have an idea
on how to cope with events being handled out-of-order?
My python is a bit too limited to come up with a straight-up answer. :-)

Best regards,
Tim


Reply to this email directly or view it on GitHub
#1 (comment).

@dsoprea
Copy link
Owner

dsoprea commented Jun 16, 2015

If this seems to work, please submit a PR.

Dustin

On Tue, Jun 16, 2015 at 4:06 AM, Dustin Oprea myselfasunder@gmail.com
wrote:

I think your way is already the best way for IDs that aren't going to be
reappearing.

Dustin
On Jun 16, 2015 3:59 AM, "backbord" notifications@github.com wrote:

Hi Dustin,
I can neither catch nor work around an exception thrown from the
generator. (See for example http://stackoverflow.com/a/11366139/3985756)
However, I agree, that strict code is the way to go -- do you have an
idea on how to cope with events being handled out-of-order?
My python is a bit too limited to come up with a straight-up answer. :-)

Best regards,
Tim


Reply to this email directly or view it on GitHub
#1 (comment).

@backbord
Copy link
Contributor Author

Ok, my first pull request ever. Hope it's to your liking. :-)

Thanks and regards,
Tim

@dsoprea
Copy link
Owner

dsoprea commented Jun 22, 2015

I've performed a release of 0.2.4. This includes the fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants