From 9f38c4bfa6a875f1b5b08cddb1e83d2af8cebb40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Tue, 10 Dec 2019 07:40:37 +0100 Subject: [PATCH] Fix a TypeError when deleting the watched folder on Windows --- src/watchdog/observers/winapi.py | 2 +- tests/test_observers_winapi.py | 55 +++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/watchdog/observers/winapi.py b/src/watchdog/observers/winapi.py index e7a8a7b1b..65ab1c3f0 100644 --- a/src/watchdog/observers/winapi.py +++ b/src/watchdog/observers/winapi.py @@ -295,7 +295,7 @@ def _is_observed_path_deleted(handle, path): def _generate_observed_path_deleted_event(): # Create synthetic event for notify that observed directory is deleted path = ctypes.create_unicode_buffer('.') - event = FILE_NOTIFY_INFORMATION(0, FILE_ACTION_DELETED_SELF, len(path), path.value) + event = FILE_NOTIFY_INFORMATION(0, FILE_ACTION_DELETED_SELF, len(path), path.value.encode("utf-8")) event_size = ctypes.sizeof(event) buff = ctypes.create_string_buffer(BUFFER_SIZE) ctypes.memmove(buff, ctypes.addressof(event), event_size) diff --git a/tests/test_observers_winapi.py b/tests/test_observers_winapi.py index 5bc497783..e13f4e947 100644 --- a/tests/test_observers_winapi.py +++ b/tests/test_observers_winapi.py @@ -22,6 +22,7 @@ if not platform.is_windows(): # noqa pytest.skip("Windows only.", allow_module_level=True) +import os import os.path from time import sleep @@ -36,11 +37,16 @@ from .shell import ( mkdir, mkdtemp, - mv + mv, + rm ) -temp_dir = mkdtemp() +SLEEP_TIME = 2 + +# Path with non-ASCII +temp_dir = os.path.join(mkdtemp(), u"Strange \N{SNOWMAN}") +os.makedirs(temp_dir) def p(*args): @@ -65,8 +71,6 @@ def emitter(event_queue): def test___init__(event_queue, emitter): - SLEEP_TIME = 2 - emitter.start() sleep(SLEEP_TIME) mkdir(p('fromdir')) @@ -98,3 +102,46 @@ def test___init__(event_queue, emitter): got.add(event) assert expected == got + + +def test_root_deleted(event_queue, emitter): + r"""Test the event got when removing the watched folder. + The regression to prevent is: + + Exception in thread Thread-1: + Traceback (most recent call last): + File "watchdog\observers\winapi.py", line 333, in read_directory_changes + ctypes.byref(nbytes), None, None) + File "watchdog\observers\winapi.py", line 105, in _errcheck_bool + raise ctypes.WinError() + PermissionError: [WinError 5] Access refused. + + During handling of the above exception, another exception occurred: + + Traceback (most recent call last): + File "C:\Python37-32\lib\threading.py", line 926, in _bootstrap_inner + self.run() + File "watchdog\observers\api.py", line 145, in run + self.queue_events(self.timeout) + File "watchdog\observers\read_directory_changes.py", line 76, in queue_events + winapi_events = self._read_events() + File "watchdog\observers\read_directory_changes.py", line 73, in _read_events + return read_events(self._handle, self.watch.path, self.watch.is_recursive) + File "watchdog\observers\winapi.py", line 387, in read_events + buf, nbytes = read_directory_changes(handle, path, recursive) + File "watchdog\observers\winapi.py", line 340, in read_directory_changes + return _generate_observed_path_deleted_event() + File "watchdog\observers\winapi.py", line 298, in _generate_observed_path_deleted_event + event = FILE_NOTIFY_INFORMATION(0, FILE_ACTION_DELETED_SELF, len(path), path.value) + TypeError: expected bytes, str found + """ + + emitter.start() + sleep(SLEEP_TIME) + + # This should not fail + rm(p(), recursive=True) + sleep(SLEEP_TIME) + + # The emitter is automatically stopped, with no error + assert not emitter.should_keep_running()