Skip to content

Commit a5cfdbb

Browse files
anbeckhamclaude
andcommitted
fix(ci): Fix linting issues and format code
- Add noqa comments for intentional import patterns - Format code with ruff - Remove unused imports Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent e790693 commit a5cfdbb

12 files changed

Lines changed: 371 additions & 457 deletions

src/linux_desktop_mcp/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
def main():
1111
"""Entry point for the MCP server."""
1212
from .server import main as _main
13+
1314
_main()
1415

1516

src/linux_desktop_mcp/atspi_bridge.py

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
"""
66

77
import asyncio
8+
import logging
89
import threading
910
from concurrent.futures import ThreadPoolExecutor
10-
from typing import Optional, Callable, Any
11-
import logging
11+
from typing import Any, Callable, Optional
1212

1313
logger = logging.getLogger(__name__)
1414

@@ -17,7 +17,8 @@
1717

1818
try:
1919
import gi
20-
gi.require_version('Atspi', '2.0')
20+
21+
gi.require_version("Atspi", "2.0")
2122
from gi.repository import Atspi
2223

2324
ROLE_MAPPING = {
@@ -77,8 +78,12 @@
7778
Atspi = None
7879

7980

80-
from .references import (
81-
ElementReference, ElementRole, ElementBounds, ElementState, ReferenceManager
81+
from .references import ( # noqa: E402
82+
ElementBounds,
83+
ElementReference,
84+
ElementRole,
85+
ElementState,
86+
ReferenceManager,
8287
)
8388

8489

@@ -97,10 +102,10 @@ def _get_state_from_atspi(state_set) -> ElementState:
97102
return ElementState()
98103

99104
return ElementState(
100-
visible=state_set.contains(Atspi.StateType.VISIBLE) and
101-
state_set.contains(Atspi.StateType.SHOWING),
102-
enabled=state_set.contains(Atspi.StateType.ENABLED) and
103-
state_set.contains(Atspi.StateType.SENSITIVE),
105+
visible=state_set.contains(Atspi.StateType.VISIBLE)
106+
and state_set.contains(Atspi.StateType.SHOWING),
107+
enabled=state_set.contains(Atspi.StateType.ENABLED)
108+
and state_set.contains(Atspi.StateType.SENSITIVE),
104109
focused=state_set.contains(Atspi.StateType.FOCUSED),
105110
selected=state_set.contains(Atspi.StateType.SELECTED),
106111
checked=state_set.contains(Atspi.StateType.CHECKED),
@@ -123,12 +128,7 @@ def _get_bounds_from_atspi(accessible) -> ElementBounds:
123128
component = accessible.get_component_iface()
124129
if component:
125130
rect = component.get_extents(Atspi.CoordType.SCREEN)
126-
return ElementBounds(
127-
x=rect.x,
128-
y=rect.y,
129-
width=rect.width,
130-
height=rect.height
131-
)
131+
return ElementBounds(x=rect.x, y=rect.y, width=rect.width, height=rect.height)
132132
except Exception:
133133
pass
134134
return ElementBounds(x=0, y=0, width=0, height=0)
@@ -229,7 +229,7 @@ def _build_tree_sync(
229229
current_depth: int = 0,
230230
app_name: Optional[str] = None,
231231
window_title: Optional[str] = None,
232-
parent_ref: Optional[str] = None
232+
parent_ref: Optional[str] = None,
233233
) -> list[ElementReference]:
234234
"""Build element tree from accessible (sync)."""
235235
refs = []
@@ -261,7 +261,7 @@ def _build_tree_sync(
261261
available_actions=_get_available_actions(accessible),
262262
description=accessible.get_description() or "",
263263
value=_get_element_value(accessible),
264-
atspi_path=str(accessible.get_id()) if hasattr(accessible, 'get_id') else None,
264+
atspi_path=str(accessible.get_id()) if hasattr(accessible, "get_id") else None,
265265
atspi_accessible=accessible,
266266
parent_ref=parent_ref,
267267
app_name=app_name,
@@ -283,7 +283,7 @@ def _build_tree_sync(
283283
current_depth=current_depth + 1,
284284
app_name=app_name,
285285
window_title=window_title,
286-
parent_ref=ref_id
286+
parent_ref=ref_id,
287287
)
288288
for child_ref in child_refs:
289289
if child_ref.parent_ref == ref_id:
@@ -298,9 +298,7 @@ def _build_tree_sync(
298298
return refs
299299

300300
async def build_tree(
301-
self,
302-
app_name_filter: Optional[str] = None,
303-
max_depth: int = 15
301+
self, app_name_filter: Optional[str] = None, max_depth: int = 15
304302
) -> list[ElementReference]:
305303
"""Build the full accessibility tree.
306304
@@ -333,9 +331,7 @@ def _build():
333331
return await self._run_sync(_build)
334332

335333
async def build_tree_for_window(
336-
self,
337-
window_accessible,
338-
max_depth: int = 15
334+
self, window_accessible, max_depth: int = 15
339335
) -> list[ElementReference]:
340336
"""Build accessibility tree for a specific window only.
341337
@@ -368,7 +364,7 @@ def _build():
368364
window_accessible,
369365
max_depth=max_depth,
370366
app_name=app_name,
371-
window_title=window_title
367+
window_title=window_title,
372368
)
373369
except Exception as e:
374370
logger.error(f"Error building tree for window: {e}")
@@ -377,9 +373,7 @@ def _build():
377373
return await self._run_sync(_build)
378374

379375
async def build_tree_for_windows(
380-
self,
381-
window_accessibles: list,
382-
max_depth: int = 15
376+
self, window_accessibles: list, max_depth: int = 15
383377
) -> list[ElementReference]:
384378
"""Build accessibility tree for multiple windows.
385379
@@ -411,7 +405,7 @@ def _build():
411405
window_accessible,
412406
max_depth=max_depth,
413407
app_name=app_name,
414-
window_title=window_title
408+
window_title=window_title,
415409
)
416410
all_refs.extend(refs)
417411
except Exception as e:
@@ -517,9 +511,7 @@ def _get_element_at_point_sync(self, x: int, y: int):
517511
if app:
518512
component = app.get_component_iface()
519513
if component:
520-
accessible = component.get_accessible_at_point(
521-
x, y, Atspi.CoordType.SCREEN
522-
)
514+
accessible = component.get_accessible_at_point(x, y, Atspi.CoordType.SCREEN)
523515
if accessible:
524516
return accessible
525517
except Exception:

src/linux_desktop_mcp/detection.py

Lines changed: 33 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
class DisplayServer(Enum):
1212
"""Linux display server types."""
13+
1314
X11 = "x11"
1415
WAYLAND = "wayland"
1516
XWAYLAND = "xwayland"
@@ -19,6 +20,7 @@ class DisplayServer(Enum):
1920
@dataclass
2021
class PlatformCapabilities:
2122
"""Available platform capabilities for desktop automation."""
23+
2224
display_server: DisplayServer
2325
has_atspi: bool = False
2426
has_ydotool: bool = False
@@ -88,8 +90,10 @@ def _check_atspi_available() -> tuple[bool, bool]:
8890

8991
try:
9092
import gi
91-
gi.require_version('Atspi', '2.0')
93+
94+
gi.require_version("Atspi", "2.0")
9295
from gi.repository import Atspi
96+
9397
pyatspi_available = True
9498

9599
desktop = Atspi.get_desktop(0)
@@ -106,11 +110,7 @@ def _check_atspi_available() -> tuple[bool, bool]:
106110
def _check_ydotool_daemon() -> bool:
107111
"""Check if ydotool daemon is running."""
108112
try:
109-
result = subprocess.run(
110-
["pgrep", "-x", "ydotoold"],
111-
capture_output=True,
112-
timeout=2
113-
)
113+
result = subprocess.run(["pgrep", "-x", "ydotoold"], capture_output=True, timeout=2)
114114
return result.returncode == 0
115115
except (subprocess.SubprocessError, FileNotFoundError):
116116
return False
@@ -123,27 +123,27 @@ def _detect_compositor() -> Optional[str]:
123123
Compositor name (sway, hyprland, gnome, kde, etc.) or None.
124124
"""
125125
# Check common compositor indicators via environment variables
126-
if os.environ.get('SWAYSOCK'):
127-
return 'sway'
128-
if os.environ.get('HYPRLAND_INSTANCE_SIGNATURE'):
129-
return 'hyprland'
130-
if os.environ.get('GNOME_SETUP_DISPLAY') or os.environ.get('GNOME_SHELL_SESSION_MODE'):
131-
return 'gnome'
132-
if os.environ.get('KDE_FULL_SESSION') or os.environ.get('KDE_SESSION_VERSION'):
133-
return 'kde'
134-
if os.environ.get('WESTON_CONFIG_FILE'):
135-
return 'weston'
126+
if os.environ.get("SWAYSOCK"):
127+
return "sway"
128+
if os.environ.get("HYPRLAND_INSTANCE_SIGNATURE"):
129+
return "hyprland"
130+
if os.environ.get("GNOME_SETUP_DISPLAY") or os.environ.get("GNOME_SHELL_SESSION_MODE"):
131+
return "gnome"
132+
if os.environ.get("KDE_FULL_SESSION") or os.environ.get("KDE_SESSION_VERSION"):
133+
return "kde"
134+
if os.environ.get("WESTON_CONFIG_FILE"):
135+
return "weston"
136136

137137
# Try to detect via XDG_CURRENT_DESKTOP
138-
desktop = os.environ.get('XDG_CURRENT_DESKTOP', '').lower()
139-
if 'gnome' in desktop:
140-
return 'gnome'
141-
if 'kde' in desktop or 'plasma' in desktop:
142-
return 'kde'
143-
if 'sway' in desktop:
144-
return 'sway'
145-
if 'hyprland' in desktop:
146-
return 'hyprland'
138+
desktop = os.environ.get("XDG_CURRENT_DESKTOP", "").lower()
139+
if "gnome" in desktop:
140+
return "gnome"
141+
if "kde" in desktop or "plasma" in desktop:
142+
return "kde"
143+
if "sway" in desktop:
144+
return "sway"
145+
if "hyprland" in desktop:
146+
return "hyprland"
147147

148148
return None
149149

@@ -156,10 +156,12 @@ def _check_layer_shell_available() -> bool:
156156
"""
157157
try:
158158
import gi
159-
gi.require_version('Gtk4LayerShell', '1.0')
160-
from gi.repository import Gtk4LayerShell
159+
160+
gi.require_version("Gtk4LayerShell", "1.0")
161+
from gi.repository import Gtk4LayerShell # noqa: F401
162+
161163
# Note: Gtk4LayerShell.is_supported() requires GTK to be initialized
162-
# So we just check if the import works
164+
# So we just check if the import works (import is intentionally unused)
163165
return True
164166
except (ImportError, ValueError):
165167
return False
@@ -201,18 +203,12 @@ def detect_capabilities() -> PlatformCapabilities:
201203

202204
if display_server == DisplayServer.WAYLAND:
203205
if not caps.has_ydotool and not caps.has_wtype:
204-
caps.errors.append(
205-
"No Wayland input tool available. Install ydotool or wtype."
206-
)
206+
caps.errors.append("No Wayland input tool available. Install ydotool or wtype.")
207207
elif caps.has_ydotool and not _check_ydotool_daemon():
208-
caps.errors.append(
209-
"ydotool daemon not running. Start with: sudo ydotoold"
210-
)
208+
caps.errors.append("ydotool daemon not running. Start with: sudo ydotoold")
211209
elif display_server == DisplayServer.X11:
212210
if not caps.has_xdotool and not caps.has_ydotool:
213-
caps.errors.append(
214-
"No X11 input tool available. Install: sudo apt install xdotool"
215-
)
211+
caps.errors.append("No X11 input tool available. Install: sudo apt install xdotool")
216212

217213
return caps
218214

0 commit comments

Comments
 (0)