From eeb667e16318854ae742d57148559e6877f4dbe7 Mon Sep 17 00:00:00 2001 From: JeffreyChen Date: Sat, 25 Apr 2026 01:53:20 +0800 Subject: [PATCH] Clear 8 remaining Sonar findings on main branch - win32 keyboard/mouse listeners + Windows clipboard: mark the four S5655 ctypes byref/sizeof/addressof calls with NOSONAR. Sonar's type checker doesn't know Array and Structure satisfy the _CData contract, so these keep firing as false positives. - test_rest_server: drop the ``:`` between NOSONAR and the rule code so Sonar's Python parser recognises the suppression syntax (S7632) - test_plugin_loader: add NOSONAR next to the existing noqa for the plugin-contract AC_value field and AC_run method (S116 / S100) - test_watcher: replace the ``(_ for _ in ()).throw(...)`` generator trick with a real helper function so S7500 stops flagging the "comprehension passed to collection constructor" pattern; behaviour unchanged (raises OSError when get_pixel is called) --- je_auto_control/utils/clipboard/clipboard.py | 4 ++-- .../windows/listener/win32_keyboard_listener.py | 2 +- je_auto_control/windows/listener/win32_mouse_listener.py | 2 +- test/unit_test/headless/test_plugin_loader.py | 4 ++-- test/unit_test/headless/test_rest_server.py | 2 +- test/unit_test/headless/test_watcher.py | 7 +++++-- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/je_auto_control/utils/clipboard/clipboard.py b/je_auto_control/utils/clipboard/clipboard.py index 0f81ebd..761f3cb 100644 --- a/je_auto_control/utils/clipboard/clipboard.py +++ b/je_auto_control/utils/clipboard/clipboard.py @@ -93,14 +93,14 @@ def _win_set(text: str) -> None: kernel32.GlobalUnlock.argtypes = [wintypes.HGLOBAL] data = ctypes.create_unicode_buffer(text) - size = ctypes.sizeof(data) + size = ctypes.sizeof(data) # NOSONAR S5655 false positive — Array is accepted by sizeof handle = kernel32.GlobalAlloc(gmem_moveable, size) if not handle: raise RuntimeError("GlobalAlloc failed") pointer = kernel32.GlobalLock(handle) if not pointer: raise RuntimeError("GlobalLock failed") - ctypes.memmove(pointer, ctypes.addressof(data), size) + ctypes.memmove(pointer, ctypes.addressof(data), size) # NOSONAR S5655 false positive — Array is accepted by addressof kernel32.GlobalUnlock(handle) if not user32.OpenClipboard(None): raise RuntimeError("OpenClipboard failed") diff --git a/je_auto_control/windows/listener/win32_keyboard_listener.py b/je_auto_control/windows/listener/win32_keyboard_listener.py index c0a65f2..8310716 100644 --- a/je_auto_control/windows/listener/win32_keyboard_listener.py +++ b/je_auto_control/windows/listener/win32_keyboard_listener.py @@ -90,7 +90,7 @@ def _start_listener(self) -> None: message = MSG() # 進入訊息迴圈 Enter message loop - _user32.GetMessageA(byref(message), 0, 0, 0) + _user32.GetMessageA(byref(message), 0, 0, 0) # NOSONAR S5655 false positive — MSG is a ctypes Structure def record(self, want_to_record_queue: Queue) -> None: """ diff --git a/je_auto_control/windows/listener/win32_mouse_listener.py b/je_auto_control/windows/listener/win32_mouse_listener.py index ddb137f..345bfa0 100644 --- a/je_auto_control/windows/listener/win32_mouse_listener.py +++ b/je_auto_control/windows/listener/win32_mouse_listener.py @@ -99,7 +99,7 @@ def _start_listener(self) -> None: raise AutoControlException("Failed to set mouse hook") message = MSG() - _user32.GetMessageA(byref(message), 0, 0, 0) + _user32.GetMessageA(byref(message), 0, 0, 0) # NOSONAR S5655 false positive — MSG is a ctypes Structure def record(self, want_to_record_queue: Queue) -> None: """ diff --git a/test/unit_test/headless/test_plugin_loader.py b/test/unit_test/headless/test_plugin_loader.py index 3f16b88..83a2f0c 100644 --- a/test/unit_test/headless/test_plugin_loader.py +++ b/test/unit_test/headless/test_plugin_loader.py @@ -73,10 +73,10 @@ def test_register_plugin_commands_adds_and_removes_cleanly(tmp_path): def test_discover_ignores_non_callable_ac_attribute(): class Module: - AC_value = 42 # noqa: N815 # reason: AC_* is the plugin contract + AC_value = 42 # noqa: N815 # NOSONAR AC_* is the plugin contract @staticmethod - def AC_run(): # noqa: N802 # reason: AC_* is the plugin contract + def AC_run(): # noqa: N802 # NOSONAR AC_* is the plugin contract return 1 found = discover_plugin_commands(Module) diff --git a/test/unit_test/headless/test_rest_server.py b/test/unit_test/headless/test_rest_server.py index db28d0d..6a938c7 100644 --- a/test/unit_test/headless/test_rest_server.py +++ b/test/unit_test/headless/test_rest_server.py @@ -16,7 +16,7 @@ def rest_server(): server.stop(timeout=1.0) -_TEST_SCHEME = "http" # NOSONAR: S5332 # reason: localhost-only ephemeral test server; TLS is out of scope here +_TEST_SCHEME = "http" # NOSONAR localhost-only ephemeral test server; TLS is out of scope here def _request(server, path, method="GET", body=None): diff --git a/test/unit_test/headless/test_watcher.py b/test/unit_test/headless/test_watcher.py index 8721436..932fa50 100644 --- a/test/unit_test/headless/test_watcher.py +++ b/test/unit_test/headless/test_watcher.py @@ -46,8 +46,11 @@ def broken(): def test_pixel_watcher_returns_none_on_error(monkeypatch): import je_auto_control.wrapper.auto_control_screen as screen_mod - monkeypatch.setattr(screen_mod, "get_pixel", - lambda x, y: (_ for _ in ()).throw(OSError("nope"))) + + def _raise_os_error(_x, _y): + raise OSError("nope") + + monkeypatch.setattr(screen_mod, "get_pixel", _raise_os_error) assert PixelWatcher().sample(0, 0) is None