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

Add FileOpenedEvent #941

Merged
merged 5 commits into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ Changelog

2023-xx-xx • `full history <https://github.com/gorakhargosh/watchdog/compare/v2.2.1...HEAD>`__

-
- Thanks to our beloved contributors: @BoboTiG
- [inotify] Add support for ``IN_OPEN`` events: a ``FileOpenedEvent`` event will be fired. (`#941 <https://github.com/gorakhargosh/watchdog/pull/941>`__)
- Thanks to our beloved contributors: @BoboTiG, @dstaple

2.2.1
~~~~~
Expand Down
21 changes: 21 additions & 0 deletions src/watchdog/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
:members:
:show-inheritance:

.. autoclass:: FileOpenedEvent
:members:
:show-inheritance:

.. autoclass:: DirCreatedEvent
:members:
:show-inheritance:
Expand Down Expand Up @@ -100,6 +104,7 @@
EVENT_TYPE_CREATED = 'created'
EVENT_TYPE_MODIFIED = 'modified'
EVENT_TYPE_CLOSED = 'closed'
EVENT_TYPE_OPENED = 'opened'


class FileSystemEvent:
Expand Down Expand Up @@ -223,6 +228,12 @@ class FileClosedEvent(FileSystemEvent):
event_type = EVENT_TYPE_CLOSED


class FileOpenedEvent(FileSystemEvent):
"""File system event representing file close on the file system."""

event_type = EVENT_TYPE_OPENED


# Directory events.


Expand Down Expand Up @@ -275,6 +286,7 @@ def dispatch(self, event):
EVENT_TYPE_MODIFIED: self.on_modified,
EVENT_TYPE_MOVED: self.on_moved,
EVENT_TYPE_CLOSED: self.on_closed,
EVENT_TYPE_OPENED: self.on_opened,
}[event.event_type](event)

def on_any_event(self, event):
Expand Down Expand Up @@ -331,6 +343,15 @@ def on_closed(self, event):
:class:`FileClosedEvent`
"""

def on_opened(self, event):
"""Called when a file is opened.

:param event:
Event representing file opening.
:type event:
:class:`FileOpenedEvent`
"""


class PatternMatchingEventHandler(FileSystemEventHandler):
"""
Expand Down
4 changes: 4 additions & 0 deletions src/watchdog/observers/inotify.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
FileMovedEvent,
FileCreatedEvent,
FileClosedEvent,
FileOpenedEvent,
generate_sub_moved_events,
generate_sub_created_events,
)
Expand Down Expand Up @@ -176,6 +177,9 @@ def queue_events(self, timeout, full_events=False):
cls = FileClosedEvent
self.queue_event(cls(src_path))
self.queue_event(DirModifiedEvent(os.path.dirname(src_path)))
elif event.is_open and not event.is_directory:
cls = FileOpenedEvent
self.queue_event(cls(src_path))
# elif event.is_close_nowrite and not event.is_directory:
# cls = FileClosedEvent
# self.queue_event(cls(src_path))
Expand Down
5 changes: 5 additions & 0 deletions src/watchdog/observers/inotify_c.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ class InotifyConstants:
InotifyConstants.IN_DELETE_SELF,
InotifyConstants.IN_DONT_FOLLOW,
InotifyConstants.IN_CLOSE_WRITE,
InotifyConstants.IN_OPEN,
])


Expand Down Expand Up @@ -486,6 +487,10 @@ def is_close_write(self):
def is_close_nowrite(self):
return self._mask & InotifyConstants.IN_CLOSE_NOWRITE > 0

@property
def is_open(self):
return self._mask & InotifyConstants.IN_OPEN > 0

@property
def is_access(self):
return self._mask & InotifyConstants.IN_ACCESS > 0
Expand Down
25 changes: 24 additions & 1 deletion tests/test_emitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
DirCreatedEvent,
DirMovedEvent,
FileClosedEvent,
FileOpenedEvent,
)
from watchdog.observers.api import ObservedWatch

Expand Down Expand Up @@ -122,6 +123,9 @@ def test_create():
expect_event(DirModifiedEvent(p()))

if platform.is_linux():
event = event_queue.get(timeout=5)[0]
assert event.src_path == p('a')
assert isinstance(event, FileOpenedEvent)
event = event_queue.get(timeout=5)[0]
assert event.src_path == p('a')
assert isinstance(event, FileClosedEvent)
Expand Down Expand Up @@ -188,6 +192,11 @@ def test_modify():

touch(p('a'))

if platform.is_linux():
event = event_queue.get(timeout=5)[0]
assert event.src_path == p('a')
assert isinstance(event, FileOpenedEvent)

expect_event(FileModifiedEvent(p('a')))

if platform.is_linux():
Expand Down Expand Up @@ -429,6 +438,11 @@ def test_recursive_on():
assert event.src_path == p('dir1', 'dir2', 'dir3')
assert isinstance(event, DirModifiedEvent)

if platform.is_linux():
event = event_queue.get(timeout=5)[0]
assert event.src_path == p('dir1', 'dir2', 'dir3', 'a')
assert isinstance(event, FileOpenedEvent)

if not platform.is_bsd():
event = event_queue.get(timeout=5)[0]
assert event.src_path == p('dir1', 'dir2', 'dir3', 'a')
Expand All @@ -450,6 +464,7 @@ def test_recursive_off():
expect_event(DirModifiedEvent(p()))

if platform.is_linux():
expect_event(FileOpenedEvent(p('b')))
expect_event(FileClosedEvent(p('b')))

# currently limiting these additional events to macOS only, see https://github.com/gorakhargosh/watchdog/pull/779
Expand Down Expand Up @@ -505,7 +520,8 @@ def test_renaming_top_level_directory():
if event_queue.empty():
break

assert all([isinstance(e, (FileCreatedEvent, FileMovedEvent, DirModifiedEvent, FileClosedEvent)) for e in events])
assert all([isinstance(e, (FileCreatedEvent, FileMovedEvent, FileOpenedEvent, DirModifiedEvent, FileClosedEvent))
for e in events])

for event in events:
if isinstance(event, FileCreatedEvent):
Expand Down Expand Up @@ -595,6 +611,11 @@ def test_move_nested_subdirectories():

touch(p('dir2/dir3', 'a'))

if platform.is_linux():
event = event_queue.get(timeout=5)[0]
assert event.src_path == p('dir2/dir3', 'a')
assert isinstance(event, FileOpenedEvent)

event = event_queue.get(timeout=5)[0]
assert event.src_path == p('dir2/dir3', 'a')
assert isinstance(event, FileModifiedEvent)
Expand Down Expand Up @@ -658,8 +679,10 @@ def test_file_lifecyle():
expect_event(DirModifiedEvent(p()))

if platform.is_linux():
expect_event(FileOpenedEvent(p('a')))
expect_event(FileClosedEvent(p('a')))
expect_event(DirModifiedEvent(p()))
expect_event(FileOpenedEvent(p('a')))

expect_event(FileModifiedEvent(p('a')))

Expand Down
15 changes: 15 additions & 0 deletions tests/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
FileModifiedEvent,
FileCreatedEvent,
FileClosedEvent,
FileOpenedEvent,
DirDeletedEvent,
DirModifiedEvent,
DirCreatedEvent,
Expand All @@ -31,6 +32,7 @@
EVENT_TYPE_DELETED,
EVENT_TYPE_MOVED,
EVENT_TYPE_CLOSED,
EVENT_TYPE_OPENED,
)

path_1 = '/path/xyz'
Expand Down Expand Up @@ -92,6 +94,14 @@ def test_file_closed_event():
assert not event.is_synthetic


def test_file_opened_event():
event = FileOpenedEvent(path_1)
assert path_1 == event.src_path
assert EVENT_TYPE_OPENED == event.event_type
assert not event.is_directory
assert not event.is_synthetic


def test_dir_deleted_event():
event = DirDeletedEvent(path_1)
assert path_1 == event.src_path
Expand Down Expand Up @@ -122,6 +132,7 @@ def test_file_system_event_handler_dispatch():
dir_cre_event = DirCreatedEvent('/path/blah.py')
file_cre_event = FileCreatedEvent('/path/blah.txt')
file_cls_event = FileClosedEvent('/path/blah.txt')
file_opened_event = FileOpenedEvent('/path/blah.txt')
dir_mod_event = DirModifiedEvent('/path/blah.py')
file_mod_event = FileModifiedEvent('/path/blah.txt')
dir_mov_event = DirMovedEvent('/path/blah.py', '/path/blah')
Expand All @@ -137,6 +148,7 @@ def test_file_system_event_handler_dispatch():
file_cre_event,
file_mov_event,
file_cls_event,
file_opened_event,
]

class TestableEventHandler(FileSystemEventHandler):
Expand All @@ -159,6 +171,9 @@ def on_created(self, event):
def on_closed(self, event):
assert event.event_type == EVENT_TYPE_CLOSED

def on_opened(self, event):
assert event.event_type == EVENT_TYPE_OPENED

handler = TestableEventHandler()

for event in all_events:
Expand Down