Skip to content

Commit 6f95a46

Browse files
k4cper-gclaude
andcommitted
Fix ruff lint and format errors failing CI
Remove unused Quartz imports and fix unsorted import block in macOS action handler, and apply ruff formatting to 8 drifted files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c080382 commit 6f95a46

File tree

8 files changed

+145
-111
lines changed

8 files changed

+145
-111
lines changed

cup/__init__.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -454,19 +454,19 @@ def _screenshot_macos(self, region: dict[str, int] | None) -> bytes:
454454
cmd = ["screencapture", "-x"] # -x = no sound
455455

456456
if region is not None:
457-
cmd.extend([
458-
"-R",
459-
f"{region['x']},{region['y']},{region['w']},{region['h']}",
460-
])
457+
cmd.extend(
458+
[
459+
"-R",
460+
f"{region['x']},{region['y']},{region['w']},{region['h']}",
461+
]
462+
)
461463

462464
cmd.append(tmp_path)
463465

464466
result = subprocess.run(cmd, capture_output=True, timeout=10)
465467
if result.returncode != 0:
466468
stderr = result.stderr.decode(errors="replace").strip()
467-
raise RuntimeError(
468-
f"screencapture failed (exit {result.returncode}): {stderr}"
469-
)
469+
raise RuntimeError(f"screencapture failed (exit {result.returncode}): {stderr}")
470470

471471
with open(tmp_path, "rb") as f:
472472
data = f.read()
@@ -500,7 +500,8 @@ def _check_macos_screen_recording_permission() -> None:
500500
)
501501

502502
windows = CGWindowListCopyWindowInfo(
503-
kCGWindowListOptionOnScreenOnly, kCGNullWindowID,
503+
kCGWindowListOptionOnScreenOnly,
504+
kCGNullWindowID,
504505
)
505506

506507
# If any window has a name, we have permission
@@ -510,6 +511,7 @@ def _check_macos_screen_recording_permission() -> None:
510511
# Trigger the macOS permission prompt
511512
try:
512513
from Quartz import CGRequestScreenCaptureAccess
514+
513515
CGRequestScreenCaptureAccess()
514516
except ImportError:
515517
pass

cup/__main__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ def main() -> None:
3030
parser.add_argument(
3131
"--full-json-out", type=str, default=None, help="Write full (unpruned) CUP JSON to file"
3232
)
33+
parser.add_argument("--compact-out", type=str, default=None, help="Write compact text to file")
3334
parser.add_argument(
34-
"--compact-out", type=str, default=None, help="Write compact text to file"
35-
)
36-
parser.add_argument(
37-
"--verbose", action="store_true", help="Print diagnostics (timing, role distribution, sizes)"
35+
"--verbose",
36+
action="store_true",
37+
help="Print diagnostics (timing, role distribution, sizes)",
3838
)
3939
parser.add_argument(
4040
"--platform",

cup/actions/_linux.py

Lines changed: 43 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,18 @@
5151
}
5252

5353
_XK_MODIFIERS: dict[str, int] = {
54-
"ctrl": 0xFFE3, # XK_Control_L
55-
"alt": 0xFFE9, # XK_Alt_L
54+
"ctrl": 0xFFE3, # XK_Control_L
55+
"alt": 0xFFE9, # XK_Alt_L
5656
"shift": 0xFFE1, # XK_Shift_L
57-
"meta": 0xFFEB, # XK_Super_L
57+
"meta": 0xFFEB, # XK_Super_L
5858
}
5959

6060

6161
# ---------------------------------------------------------------------------
6262
# XTest keyboard/mouse input via ctypes
6363
# ---------------------------------------------------------------------------
6464

65+
6566
class _XTest:
6667
"""Thin ctypes wrapper around Xlib + XTest for input simulation."""
6768

@@ -81,9 +82,7 @@ def _ensure_open(self):
8182

8283
libxtst_name = ctypes.util.find_library("Xtst")
8384
if not libxtst_name:
84-
raise RuntimeError(
85-
"libXtst not found. Install libxtst-dev or xorg-x11-server-utils."
86-
)
85+
raise RuntimeError("libXtst not found. Install libxtst-dev or xorg-x11-server-utils.")
8786
self._xtst = ctypes.cdll.LoadLibrary(libxtst_name)
8887

8988
display_name = os.environ.get("DISPLAY", ":0").encode()
@@ -99,17 +98,27 @@ def _ensure_open(self):
9998
self._xlib.XKeysymToKeycode.restype = ctypes.c_ubyte
10099

101100
self._xtst.XTestFakeKeyEvent.argtypes = [
102-
ctypes.c_void_p, ctypes.c_uint, ctypes.c_int, ctypes.c_ulong,
101+
ctypes.c_void_p,
102+
ctypes.c_uint,
103+
ctypes.c_int,
104+
ctypes.c_ulong,
103105
]
104106
self._xtst.XTestFakeKeyEvent.restype = ctypes.c_int
105107

106108
self._xtst.XTestFakeButtonEvent.argtypes = [
107-
ctypes.c_void_p, ctypes.c_uint, ctypes.c_int, ctypes.c_ulong,
109+
ctypes.c_void_p,
110+
ctypes.c_uint,
111+
ctypes.c_int,
112+
ctypes.c_ulong,
108113
]
109114
self._xtst.XTestFakeButtonEvent.restype = ctypes.c_int
110115

111116
self._xtst.XTestFakeMotionEvent.argtypes = [
112-
ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_ulong,
117+
ctypes.c_void_p,
118+
ctypes.c_int,
119+
ctypes.c_int,
120+
ctypes.c_int,
121+
ctypes.c_ulong,
113122
]
114123
self._xtst.XTestFakeMotionEvent.restype = ctypes.c_int
115124

@@ -153,6 +162,7 @@ def _get_xtest() -> _XTest:
153162
# Input simulation helpers
154163
# ---------------------------------------------------------------------------
155164

165+
156166
def _send_key_combo(combo_str: str) -> None:
157167
"""Send a keyboard combination via XTest fake key events."""
158168
xt = _get_xtest()
@@ -308,6 +318,7 @@ def _send_scroll(x: int, y: int, direction: str, amount: int = 5) -> None:
308318
# AT-SPI2 action helpers
309319
# ---------------------------------------------------------------------------
310320

321+
311322
def _atspi_do_action(accessible, action_name: str) -> bool:
312323
"""Invoke a named action on an AT-SPI2 accessible object.
313324
@@ -399,6 +410,7 @@ def _atspi_get_selection_iface(accessible):
399410
# App launching helpers
400411
# ---------------------------------------------------------------------------
401412

413+
402414
def _discover_desktop_apps() -> dict[str, str]:
403415
"""Discover installed Linux apps from .desktop files.
404416
@@ -407,12 +419,8 @@ def _discover_desktop_apps() -> dict[str, str]:
407419
apps: dict[str, str] = {}
408420

409421
# Standard XDG data directories
410-
xdg_data_dirs = os.environ.get(
411-
"XDG_DATA_DIRS", "/usr/local/share:/usr/share"
412-
).split(":")
413-
xdg_data_home = os.environ.get(
414-
"XDG_DATA_HOME", os.path.expanduser("~/.local/share")
415-
)
422+
xdg_data_dirs = os.environ.get("XDG_DATA_DIRS", "/usr/local/share:/usr/share").split(":")
423+
xdg_data_home = os.environ.get("XDG_DATA_HOME", os.path.expanduser("~/.local/share"))
416424
search_dirs = [xdg_data_home] + xdg_data_dirs
417425

418426
for data_dir in search_dirs:
@@ -488,8 +496,9 @@ def _fuzzy_match(
488496
substring_matches = [c for c in candidates if query_lower in c]
489497
if substring_matches:
490498
word_boundary = [
491-
c for c in substring_matches
492-
if re.search(r'(?:^|[\s\-_])' + re.escape(query_lower) + r'(?:$|[\s\-_])', c)
499+
c
500+
for c in substring_matches
501+
if re.search(r"(?:^|[\s\-_])" + re.escape(query_lower) + r"(?:$|[\s\-_])", c)
493502
]
494503
if word_boundary:
495504
return min(word_boundary, key=len)
@@ -615,9 +624,7 @@ def _click(self, element) -> ActionResult:
615624
_send_mouse_click(center[0], center[1])
616625
return ActionResult(success=True, message="Clicked (mouse fallback)")
617626
except Exception as exc:
618-
return ActionResult(
619-
success=False, message="", error=f"Mouse click failed: {exc}"
620-
)
627+
return ActionResult(success=False, message="", error=f"Mouse click failed: {exc}")
621628

622629
return ActionResult(
623630
success=False,
@@ -634,9 +641,7 @@ def _toggle(self, element) -> ActionResult:
634641
if _atspi_do_action(element, "click"):
635642
return ActionResult(success=True, message="Toggled")
636643

637-
return ActionResult(
638-
success=False, message="", error="Element does not support toggle"
639-
)
644+
return ActionResult(success=False, message="", error="Element does not support toggle")
640645

641646
def _type(self, element, text: str) -> ActionResult:
642647
"""Type text into an element.
@@ -678,9 +683,7 @@ def _type(self, element, text: str) -> ActionResult:
678683

679684
return ActionResult(success=True, message=f"Typed: {text}")
680685
except Exception as exc:
681-
return ActionResult(
682-
success=False, message="", error=f"Failed to type: {exc}"
683-
)
686+
return ActionResult(success=False, message="", error=f"Failed to type: {exc}")
684687

685688
def _setvalue(self, element, text: str) -> ActionResult:
686689
"""Set value programmatically via AT-SPI2 Value or EditableText interface."""
@@ -715,6 +718,7 @@ def _expand(self, element) -> ActionResult:
715718
try:
716719
state_set = element.get_state_set()
717720
from gi.repository import Atspi
721+
718722
if state_set.contains(Atspi.StateType.EXPANDED):
719723
return ActionResult(success=True, message="Already expanded")
720724
except Exception:
@@ -728,15 +732,14 @@ def _expand(self, element) -> ActionResult:
728732
if _atspi_do_action(element, "click") or _atspi_do_action(element, "activate"):
729733
return ActionResult(success=True, message="Expanded")
730734

731-
return ActionResult(
732-
success=False, message="", error="Element does not support expand"
733-
)
735+
return ActionResult(success=False, message="", error="Element does not support expand")
734736

735737
def _collapse(self, element) -> ActionResult:
736738
# Check if already collapsed
737739
try:
738740
state_set = element.get_state_set()
739741
from gi.repository import Atspi
742+
740743
if not state_set.contains(Atspi.StateType.EXPANDED):
741744
return ActionResult(success=True, message="Already collapsed")
742745
except Exception:
@@ -748,9 +751,7 @@ def _collapse(self, element) -> ActionResult:
748751
if _atspi_do_action(element, "click") or _atspi_do_action(element, "activate"):
749752
return ActionResult(success=True, message="Collapsed")
750753

751-
return ActionResult(
752-
success=False, message="", error="Element does not support collapse"
753-
)
754+
return ActionResult(success=False, message="", error="Element does not support collapse")
754755

755756
def _select(self, element) -> ActionResult:
756757
# Try Selection interface on the parent (e.g., list selects child)
@@ -780,9 +781,7 @@ def _scroll(self, element, direction: str) -> ActionResult:
780781
_send_scroll(center[0], center[1], direction)
781782
return ActionResult(success=True, message=f"Scrolled {direction}")
782783
except Exception as exc:
783-
return ActionResult(
784-
success=False, message="", error=f"Scroll failed: {exc}"
785-
)
784+
return ActionResult(success=False, message="", error=f"Scroll failed: {exc}")
786785

787786
return ActionResult(
788787
success=False,
@@ -810,9 +809,7 @@ def _increment(self, element) -> ActionResult:
810809
except Exception:
811810
pass
812811

813-
return ActionResult(
814-
success=False, message="", error="Element does not support increment"
815-
)
812+
return ActionResult(success=False, message="", error="Element does not support increment")
816813

817814
def _decrement(self, element) -> ActionResult:
818815
if _atspi_do_action(element, "decrement"):
@@ -832,9 +829,7 @@ def _decrement(self, element) -> ActionResult:
832829
except Exception:
833830
pass
834831

835-
return ActionResult(
836-
success=False, message="", error="Element does not support decrement"
837-
)
832+
return ActionResult(success=False, message="", error="Element does not support decrement")
838833

839834
def _rightclick(self, element) -> ActionResult:
840835
center = _get_element_center(element)
@@ -843,9 +838,7 @@ def _rightclick(self, element) -> ActionResult:
843838
_send_mouse_click(center[0], center[1], button="right")
844839
return ActionResult(success=True, message="Right-clicked")
845840
except Exception as exc:
846-
return ActionResult(
847-
success=False, message="", error=f"Right-click failed: {exc}"
848-
)
841+
return ActionResult(success=False, message="", error=f"Right-click failed: {exc}")
849842

850843
return ActionResult(
851844
success=False,
@@ -860,9 +853,7 @@ def _doubleclick(self, element) -> ActionResult:
860853
_send_mouse_click(center[0], center[1], count=2)
861854
return ActionResult(success=True, message="Double-clicked")
862855
except Exception as exc:
863-
return ActionResult(
864-
success=False, message="", error=f"Double-click failed: {exc}"
865-
)
856+
return ActionResult(success=False, message="", error=f"Double-click failed: {exc}")
866857

867858
return ActionResult(
868859
success=False,
@@ -873,9 +864,7 @@ def _doubleclick(self, element) -> ActionResult:
873864
def _focus(self, element) -> ActionResult:
874865
if _atspi_grab_focus(element):
875866
return ActionResult(success=True, message="Focused")
876-
return ActionResult(
877-
success=False, message="", error="Failed to focus element"
878-
)
867+
return ActionResult(success=False, message="", error="Failed to focus element")
879868

880869
def _dismiss(self, element) -> ActionResult:
881870
# Try AT-SPI close/dismiss action
@@ -890,9 +879,7 @@ def _dismiss(self, element) -> ActionResult:
890879
_send_key_combo("escape")
891880
return ActionResult(success=True, message="Dismissed (Escape)")
892881
except Exception as exc:
893-
return ActionResult(
894-
success=False, message="", error=f"Failed to dismiss: {exc}"
895-
)
882+
return ActionResult(success=False, message="", error=f"Failed to dismiss: {exc}")
896883

897884
def _longpress(self, element) -> ActionResult:
898885
center = _get_element_center(element)
@@ -901,9 +888,7 @@ def _longpress(self, element) -> ActionResult:
901888
_send_mouse_long_press(center[0], center[1])
902889
return ActionResult(success=True, message="Long-pressed")
903890
except Exception as exc:
904-
return ActionResult(
905-
success=False, message="", error=f"Long-press failed: {exc}"
906-
)
891+
return ActionResult(success=False, message="", error=f"Long-press failed: {exc}")
907892

908893
return ActionResult(
909894
success=False,
@@ -920,9 +905,7 @@ def open_app(self, name: str) -> ActionResult:
920905
fuzzy-matches the name, and launches the best match.
921906
"""
922907
if not name or not name.strip():
923-
return ActionResult(
924-
success=False, message="", error="App name must not be empty"
925-
)
908+
return ActionResult(success=False, message="", error="App name must not be empty")
926909

927910
try:
928911
apps = _discover_desktop_apps()
@@ -986,6 +969,7 @@ def _wait_for_window(
986969
"""Poll AT-SPI2 desktop for a new window matching the launched app."""
987970
try:
988971
import gi
972+
989973
gi.require_version("Atspi", "2.0")
990974
from gi.repository import Atspi
991975
except Exception:

0 commit comments

Comments
 (0)