From 87786e9c724c3c3777c67c4c0653bbedf633b092 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Mon, 29 Dec 2025 05:17:33 -0500 Subject: [PATCH] Try to make Cthulhu python 3.9 compatible. Fixed keybinding and notifications bugs. --- CLAUDE.md | 2 +- README.md | 2 +- build-local.sh | 57 +-- meson.build | 2 +- pyproject.toml | 2 +- src/cthulhu/action_presenter.py | 4 +- src/cthulhu/ax_collection.py | 13 +- src/cthulhu/ax_component.py | 9 +- src/cthulhu/ax_event_synthesizer.py | 47 +- src/cthulhu/ax_hypertext.py | 7 +- src/cthulhu/ax_object.py | 42 +- src/cthulhu/ax_selection.py | 3 +- src/cthulhu/ax_table.py | 54 +- src/cthulhu/ax_text.py | 64 +-- src/cthulhu/ax_utilities.py | 25 +- src/cthulhu/ax_utilities_application.py | 7 +- src/cthulhu/ax_utilities_collection.py | 398 +++++++-------- src/cthulhu/ax_utilities_relation.py | 3 +- src/cthulhu/ax_utilities_role.py | 483 +++++++++--------- src/cthulhu/ax_value.py | 3 +- src/cthulhu/cthulhu-setup.ui | 5 +- src/cthulhu/cthulhu_gui_prefs.py | 44 +- src/cthulhu/dbus_service.py | 10 +- src/cthulhu/focus_manager.py | 50 +- src/cthulhu/input_event_manager.py | 13 +- src/cthulhu/notification_presenter.py | 54 +- src/cthulhu/plugin.py | 6 +- src/cthulhu/plugin_system_manager.py | 48 +- .../plugins/AIAssistant/ai_providers.py | 170 +++++- src/cthulhu/plugins/AIAssistant/plugin.py | 36 ++ src/cthulhu/plugins/OCR/meson.build | 6 +- src/cthulhu/plugins/PluginManager/plugin.py | 14 +- src/cthulhu/settings.py | 1 + src/cthulhu/speech_history.py | 5 +- 34 files changed, 1003 insertions(+), 686 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 96f5ecf..a392a5a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -225,7 +225,7 @@ subdir('PluginName') - Community: IRC #stormux on irc.stormux.org ### Key Dependencies -- Python 3.10+, pygobject-3.0, pluggy, gtk+-3.0 +- Python 3.9+, pygobject-3.0, pluggy, gtk+-3.0 - AT-SPI2, ATK for accessibility - Optional: BrlTTY/BrlAPI (braille), Speech Dispatcher, liblouis, GStreamer diff --git a/README.md b/README.md index 70a19d9..95faf18 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ toolkit, OpenOffice/LibreOffice, Gecko, WebKitGtk, and KDE Qt toolkit. ### Core Requirements -* **Python 3.10+** - Python platform +* **Python 3.9+** - Python platform * **pygobject-3.0** - Python bindings for the GObject library * **gtk+-3.0** - GTK+ toolkit (minimal usage for AT-SPI integration) * **AT-SPI2** - Assistive Technology Service Provider Interface diff --git a/build-local.sh b/build-local.sh index 941de67..85f0af3 100755 --- a/build-local.sh +++ b/build-local.sh @@ -6,85 +6,72 @@ set -e # Exit on error -# Colors for output (only if stdout is a terminal) -if [[ -t 1 ]]; then - RED='\033[0;31m' - GREEN='\033[0;32m' - YELLOW='\033[1;33m' - BLUE='\033[0;34m' - NC='\033[0m' # No Color -else - RED='' - GREEN='' - YELLOW='' - BLUE='' - NC='' -fi - -echo -e "${BLUE}Cthulhu Meson Local Build Script${NC}" +echo "Cthulhu Meson Local Build Script" echo "Building and installing Cthulhu to ~/.local" echo "================================================" # Check if we're in the right directory if [[ ! -f "meson.build" ]] || [[ ! -f "src/cthulhu/cthulhu.py" ]]; then - echo -e "${RED}Error: This script must be run from the Cthulhu source directory${NC}" + echo "Error: This script must be run from the Cthulhu source directory" exit 1 fi # Check for required dependencies -echo -e "${YELLOW}Checking dependencies...${NC}" +echo "Checking dependencies..." for cmd in meson ninja python3; do if ! command -v "$cmd" &> /dev/null; then - echo -e "${RED}Error: $cmd is not installed${NC}" + echo "Error: $cmd is not installed" echo "Please install: meson ninja python" exit 1 fi done # Check for optional dependencies -missing_optional=() -for module in gi speech; do - if ! python3 -c "import $module" 2>/dev/null; then - missing_optional+=("python-$module") - fi -done +missingOptional=() +if ! python3 -c "import gi" 2>/dev/null; then + missingOptional+=("python-gi") +fi -if [[ ${#missing_optional[@]} -gt 0 ]]; then - echo -e "${YELLOW}Warning: Optional dependencies missing: ${missing_optional[*]}${NC}" +if [[ ${#missingOptional[@]} -gt 0 ]]; then + echo "Warning: Optional dependencies missing: ${missingOptional[*]}" echo "Cthulhu may not function properly without these." fi +# Clean any cached bytecode +echo "Removing __pycache__ directories..." +find . -type d -name "__pycache__" -prune -exec rm -rf {} + + # Clean any existing build directory if [[ -d "_build" ]]; then - echo -e "${YELLOW}Removing existing build directory...${NC}" + echo "Removing existing build directory..." rm -rf _build fi # Setup Meson build -echo -e "${YELLOW}Setting up Meson build...${NC}" +echo "Setting up Meson build..." meson setup _build --prefix="$HOME/.local" --buildtype=debugoptimized # Build -echo -e "${YELLOW}Building Cthulhu...${NC}" +echo "Building Cthulhu..." meson compile -C _build # Install -echo -e "${YELLOW}Installing Cthulhu to ~/.local...${NC}" +echo "Installing Cthulhu to ~/.local..." meson install -C _build # Update desktop database and icon cache if command -v update-desktop-database &> /dev/null; then - echo -e "${YELLOW}Updating desktop database...${NC}" + echo "Updating desktop database..." update-desktop-database "$HOME/.local/share/applications" 2>/dev/null || true fi if command -v gtk-update-icon-cache &> /dev/null; then - echo -e "${YELLOW}Updating icon cache...${NC}" + echo "Updating icon cache..." gtk-update-icon-cache -f -t "$HOME/.local/share/icons/hicolor" 2>/dev/null || true fi echo -echo -e "${GREEN}Build completed successfully!${NC}" +echo "Build completed successfully!" echo echo "To run Cthulhu:" echo " ~/.local/bin/cthulhu" @@ -93,4 +80,4 @@ echo "To run Cthulhu setup:" echo " ~/.local/bin/cthulhu -s" echo echo "Build artifacts are in: _build/" -echo -e "${BLUE}To clean build artifacts, run: ${YELLOW}rm -rf _build${NC}" \ No newline at end of file +echo "To clean build artifacts, run: rm -rf _build" diff --git a/meson.build b/meson.build index 49bc445..53233f1 100644 --- a/meson.build +++ b/meson.build @@ -6,7 +6,7 @@ project('cthulhu', python = import('python') i18n = import('i18n') -python_minimum_version = '3.10' +python_minimum_version = '3.9' python3 = python.find_installation('python3', required: true) if not python3.language_version().version_compare(f'>= @python_minimum_version@') error(f'Python @python_minimum_version@ or newer is required.') diff --git a/pyproject.toml b/pyproject.toml index c0911a2..01e59ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "cthulhu" dynamic = ["version"] description = "Fork of the Orca screen reader" readme = "README.md" -requires-python = ">=3.10" +requires-python = ">=3.9" license = { text = "LGPL-2.1-or-later" } dependencies = [ "pygobject>=3.18", diff --git a/src/cthulhu/action_presenter.py b/src/cthulhu/action_presenter.py index d3314df..092e385 100644 --- a/src/cthulhu/action_presenter.py +++ b/src/cthulhu/action_presenter.py @@ -34,7 +34,7 @@ __copyright__ = "Copyright (c) 2023 Igalia, S.L." __license__ = "LGPL" import time -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Union import gi @@ -103,7 +103,7 @@ class ActionList(Gtk.Window): GLib.idle_add(self._presenter._clear_gui_and_restore_focus) - def populate_actions(self, actions: dict[str, str] | list[str]) -> None: + def populate_actions(self, actions: Union[dict[str, str], list[str]]) -> None: """Populates the list with accessible actions.""" if isinstance(actions, dict): diff --git a/src/cthulhu/ax_collection.py b/src/cthulhu/ax_collection.py index e20aa50..f2957b1 100644 --- a/src/cthulhu/ax_collection.py +++ b/src/cthulhu/ax_collection.py @@ -29,6 +29,7 @@ __date__ = "$Date$" __copyright__ = "Copyright (c) 2023 Igalia, S.L." __license__ = "LGPL" +from typing import Optional import time import gi @@ -48,15 +49,15 @@ class AXCollection: # pylint: disable=R0913,R0914 @staticmethod def create_match_rule( - states: list[str] | None = None, + states: Optional[list[str]] = None, state_match_type: Atspi.CollectionMatchType = Atspi.CollectionMatchType.ALL, - attributes: list[str] | None = None, + attributes: Optional[list[str]] = None, attribute_match_type: Atspi.CollectionMatchType = Atspi.CollectionMatchType.ALL, - roles: list[str] | None = None, + roles: Optional[list[str]] = None, role_match_type: Atspi.CollectionMatchType = Atspi.CollectionMatchType.ALL, - interfaces: list[str] | None = None, + interfaces: Optional[list[str]] = None, interface_match_type: Atspi.CollectionMatchType = Atspi.CollectionMatchType.ALL, - invert: bool = False) -> Atspi.MatchRule | None: + invert: bool = False) -> Optional[Atspi.MatchRule]: """Creates a match rule based on the supplied criteria.""" if states is None: @@ -136,7 +137,7 @@ class AXCollection: obj: Atspi.Accessible, rule: Atspi.MatchRule, order: Atspi.CollectionSortOrder = Atspi.CollectionSortOrder.CANONICAL - ) -> Atspi.Accessible | None: + ) -> Optional[Atspi.Accessible]: """Returns the first object matching the specified rule.""" if not AXObject.supports_collection(obj): diff --git a/src/cthulhu/ax_component.py b/src/cthulhu/ax_component.py index ae08da1..c26bf10 100644 --- a/src/cthulhu/ax_component.py +++ b/src/cthulhu/ax_component.py @@ -34,6 +34,7 @@ __copyright__ = "Copyright (c) 2024 Igalia, S.L." \ "Copyright (c) 2024 GNOME Foundation Inc." __license__ = "LGPL" +from typing import Optional import functools import gi @@ -285,7 +286,7 @@ class AXComponent: @staticmethod def _find_descendant_at_point( obj: Atspi.Accessible, x: int, y: int - ) -> Atspi.Accessible | None: + ) -> Optional[Atspi.Accessible]: """Checks each child to see if it has a descendant at the specified point.""" for child in AXObject.iter_children(obj): @@ -297,7 +298,7 @@ class AXComponent: return None @staticmethod - def _get_object_at_point(obj: Atspi.Accessible, x: int, y: int) -> Atspi.Accessible | None: + def _get_object_at_point(obj: Atspi.Accessible, x: int, y: int) -> Optional[Atspi.Accessible]: """Returns the child (or descendant?) of obj at the specified point.""" if not AXObject.supports_component(obj): @@ -317,7 +318,7 @@ class AXComponent: @staticmethod def _get_descendant_at_point( obj: Atspi.Accessible, x: int, y: int - ) -> Atspi.Accessible | None: + ) -> Optional[Atspi.Accessible]: """Returns the deepest descendant of obj at the specified point.""" child = AXComponent._get_object_at_point(obj, x, y) @@ -338,7 +339,7 @@ class AXComponent: @staticmethod def get_descendant_at_point( obj: Atspi.Accessible, x: int, y: int - ) -> Atspi.Accessible | None: + ) -> Optional[Atspi.Accessible]: """Returns the deepest descendant of obj at the specified point.""" result = AXComponent._get_descendant_at_point(obj, x, y) diff --git a/src/cthulhu/ax_event_synthesizer.py b/src/cthulhu/ax_event_synthesizer.py index 9b55505..bc77c1b 100644 --- a/src/cthulhu/ax_event_synthesizer.py +++ b/src/cthulhu/ax_event_synthesizer.py @@ -34,6 +34,7 @@ __copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc." \ "Copyright (c) 2018-2023 Igalia, S.L." __license__ = "LGPL" +from typing import Optional import gi gi.require_version("Atspi", "2.0") gi.require_version("Gtk", "3.0") @@ -60,8 +61,8 @@ class AXEventSynthesizer: @staticmethod def _is_scrolled_off_screen( obj: Atspi.Accessible, - offset: int | None = None, - ancestor: Atspi.Accessible | None = None + offset: Optional[int] = None, + ancestor: Optional[Atspi.Accessible] = None ) -> bool: """Returns true if obj, or the caret offset therein, is scrolled off-screen.""" @@ -119,7 +120,7 @@ class AXEventSynthesizer: return True @staticmethod - def _mouse_event_on_character(obj: Atspi.Accessible, offset: int | None, event: str) -> bool: + def _mouse_event_on_character(obj: Atspi.Accessible, offset: Optional[int], event: str) -> bool: """Performs the specified mouse event on the current character in obj.""" if offset is None: @@ -164,7 +165,7 @@ class AXEventSynthesizer: return AXEventSynthesizer._generate_mouse_event(obj, relative_x, relative_y, event) @staticmethod - def route_to_character(obj: Atspi.Accessible, offset: int | None = None) -> bool: + def route_to_character(obj: Atspi.Accessible, offset: Optional[int] = None) -> bool: """Routes the pointer to the current character in obj.""" tokens = [f"AXEventSynthesizer: Attempting to route to offset {offset} in", obj] @@ -181,7 +182,7 @@ class AXEventSynthesizer: @staticmethod def click_character( - obj: Atspi.Accessible, offset: int | None = None, button: int = 1 + obj: Atspi.Accessible, offset: Optional[int] = None, button: int = 1 ) -> bool: """Single click on the current character in obj using the specified button.""" @@ -198,7 +199,7 @@ class AXEventSynthesizer: @staticmethod def _scroll_to_location( obj: Atspi.Accessible, location: Atspi.ScrollType, - start_offset: int | None = None, end_offset: int | None = None + start_offset: Optional[int] = None, end_offset: Optional[int] = None ) -> None: """Attempts to scroll to the specified location.""" @@ -220,7 +221,7 @@ class AXEventSynthesizer: @staticmethod def _scroll_to_point( obj: Atspi.Accessible, x_coord: int, y_coord: int, - start_offset: int | None = None, end_offset: int | None = None + start_offset: Optional[int] = None, end_offset: Optional[int] = None ) -> None: """Attempts to scroll obj to the specified point.""" @@ -242,8 +243,8 @@ class AXEventSynthesizer: @staticmethod def scroll_into_view( obj: Atspi.Accessible, - start_offset: int | None = None, - end_offset: int | None = None + start_offset: Optional[int] = None, + end_offset: Optional[int] = None ) -> None: """Attempts to scroll obj into view.""" @@ -253,8 +254,8 @@ class AXEventSynthesizer: @staticmethod def scroll_to_center( obj: Atspi.Accessible, - start_offset: int | None = None, - end_offset: int | None = None + start_offset: Optional[int] = None, + end_offset: Optional[int] = None ) -> None: """Attempts to scroll obj to the center of its window.""" @@ -272,8 +273,8 @@ class AXEventSynthesizer: @staticmethod def scroll_to_top_edge( obj: Atspi.Accessible, - start_offset: int | None = None, - end_offset: int | None = None + start_offset: Optional[int] = None, + end_offset: Optional[int] = None ) -> None: """Attempts to scroll obj to the top edge.""" @@ -283,8 +284,8 @@ class AXEventSynthesizer: @staticmethod def scroll_to_top_left( obj: Atspi.Accessible, - start_offset: int | None = None, - end_offset: int | None = None + start_offset: Optional[int] = None, + end_offset: Optional[int] = None ) -> None: """Attempts to scroll obj to the top left.""" @@ -294,8 +295,8 @@ class AXEventSynthesizer: @staticmethod def scroll_to_left_edge( obj: Atspi.Accessible, - start_offset: int | None = None, - end_offset: int | None = None + start_offset: Optional[int] = None, + end_offset: Optional[int] = None ) -> None: """Attempts to scroll obj to the left edge.""" @@ -305,8 +306,8 @@ class AXEventSynthesizer: @staticmethod def scroll_to_bottom_edge( obj: Atspi.Accessible, - start_offset: int | None = None, - end_offset: int | None = None + start_offset: Optional[int] = None, + end_offset: Optional[int] = None ) -> None: """Attempts to scroll obj to the bottom edge.""" @@ -316,8 +317,8 @@ class AXEventSynthesizer: @staticmethod def scroll_to_bottom_right( obj: Atspi.Accessible, - start_offset: int | None = None, - end_offset: int | None = None + start_offset: Optional[int] = None, + end_offset: Optional[int] = None ) -> None: """Attempts to scroll obj to the bottom right.""" @@ -327,8 +328,8 @@ class AXEventSynthesizer: @staticmethod def scroll_to_right_edge( obj: Atspi.Accessible, - start_offset: int | None = None, - end_offset: int | None = None + start_offset: Optional[int] = None, + end_offset: Optional[int] = None ) -> None: """Attempts to scroll obj to the right edge.""" diff --git a/src/cthulhu/ax_hypertext.py b/src/cthulhu/ax_hypertext.py index 4da6dc6..2222ac9 100644 --- a/src/cthulhu/ax_hypertext.py +++ b/src/cthulhu/ax_hypertext.py @@ -32,6 +32,7 @@ __date__ = "$Date$" __copyright__ = "Copyright (c) 2024 Igalia, S.L." __license__ = "LGPL" +from typing import Optional import os import re from urllib.parse import urlparse @@ -66,7 +67,7 @@ class AXHypertext: return count @staticmethod - def _get_link_at_index(obj: Atspi.Accessible, index: int) -> Atspi.Hyperlink | None: + def _get_link_at_index(obj: Atspi.Accessible, index: int) -> Optional[Atspi.Hyperlink]: """Returns the hyperlink object at the specified index.""" if not AXObject.supports_hypertext(obj): @@ -202,7 +203,7 @@ class AXHypertext: return basename @staticmethod - def find_child_at_offset(obj: Atspi.Accessible, offset: int) -> Atspi.Accessible | None: + def find_child_at_offset(obj: Atspi.Accessible, offset: int) -> Optional[Atspi.Accessible]: """Attempts to correct for off-by-one brokenness in implementations""" if child := AXHypertext.get_child_at_offset(obj, offset): @@ -227,7 +228,7 @@ class AXHypertext: return None @staticmethod - def get_child_at_offset(obj: Atspi.Accessible, offset: int) -> Atspi.Accessible | None: + def get_child_at_offset(obj: Atspi.Accessible, offset: int) -> Optional[Atspi.Accessible]: """Returns the embedded-object child of obj at the specified offset.""" if not AXObject.supports_hypertext(obj): diff --git a/src/cthulhu/ax_object.py b/src/cthulhu/ax_object.py index 362ffca..00137a6 100644 --- a/src/cthulhu/ax_object.py +++ b/src/cthulhu/ax_object.py @@ -34,7 +34,7 @@ __license__ = "LGPL" import re import threading import time -from typing import Callable, Generator +from typing import Callable, Generator, Optional import gi gi.require_version("Atspi", "2.0") @@ -105,7 +105,7 @@ class AXObject: return name.lower() @staticmethod - def get_application(obj: Atspi.Accessible) -> Atspi.Accessible | None: + def get_application(obj: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the accessible application associated with obj.""" if obj is None: @@ -313,7 +313,7 @@ class AXObject: return iface is not None @staticmethod - def find_real_app_and_window_for(obj: Atspi.Accessible, app: Atspi.Accessible | None = None): + def find_real_app_and_window_for(obj: Atspi.Accessible, app: Optional[Atspi.Accessible] = None): """Work around for window events coming from mutter-x11-frames.""" if app is None: @@ -545,7 +545,7 @@ class AXObject: return index @staticmethod - def get_parent(obj: Atspi.Accessible) -> Atspi.Accessible | None: + def get_parent(obj: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the accessible parent of obj. See also get_parent_checked.""" if not AXObject.is_valid(obj): @@ -571,7 +571,7 @@ class AXObject: return parent @staticmethod - def get_parent_checked(obj: Atspi.Accessible) -> Atspi.Accessible | None: + def get_parent_checked(obj: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the parent of obj, doing checks for tree validity""" if not AXObject.is_valid(obj): @@ -619,7 +619,7 @@ class AXObject: def get_common_ancestor( obj1: Atspi.Accessible, obj2: Atspi.Accessible - ) -> Atspi.Accessible | None: + ) -> Optional[Atspi.Accessible]: """Returns the common ancestor of obj1 and obj2.""" tokens = ["AXObject: Looking for common ancestor of", obj1, "and", obj2] @@ -647,7 +647,7 @@ class AXObject: def find_ancestor_inclusive( obj: Atspi.Accessible, pred: Callable[[Atspi.Accessible], bool] - ) -> Atspi.Accessible | None: + ) -> Optional[Atspi.Accessible]: """Returns obj, or the ancestor of obj, for which the function pred is true""" if pred(obj): @@ -659,7 +659,7 @@ class AXObject: def find_ancestor( obj: Atspi.Accessible, pred: Callable[[Atspi.Accessible], bool] - ) -> Atspi.Accessible | None: + ) -> Optional[Atspi.Accessible]: """Returns the ancestor of obj if the function pred is true""" if not AXObject.is_valid(obj): @@ -703,7 +703,7 @@ class AXObject: return AXObject.find_ancestor(obj, lambda x: x == ancestor) is not None @staticmethod - def get_child(obj: Atspi.Accessible, index: int) -> Atspi.Accessible | None: + def get_child(obj: Atspi.Accessible, index: int) -> Optional[Atspi.Accessible]: """Returns the nth child of obj. See also get_child_checked.""" if not AXObject.is_valid(obj): @@ -736,7 +736,7 @@ class AXObject: @staticmethod def get_child_checked( obj: Atspi.Accessible, index: int - ) -> Atspi.Accessible | None: + ) -> Optional[Atspi.Accessible]: """Returns the nth child of obj, doing checks for tree validity""" if not AXObject.is_valid(obj): @@ -757,7 +757,7 @@ class AXObject: def get_active_descendant_checked( container: Atspi.Accessible, reported_child: Atspi.Accessible - ) -> Atspi.Accessible | None: + ) -> Optional[Atspi.Accessible]: """Checks the reported active descendant and return the real/valid one.""" if not AXObject.has_state(container, Atspi.StateType.MANAGES_DESCENDANTS): @@ -784,7 +784,7 @@ class AXObject: def _find_descendant( obj: Atspi.Accessible, pred: Callable[[Atspi.Accessible], bool] - ) -> Atspi.Accessible | None: + ) -> Optional[Atspi.Accessible]: """Returns the descendant of obj if the function pred is true""" if not AXObject.is_valid(obj): @@ -806,7 +806,7 @@ class AXObject: def find_descendant( obj: Atspi.Accessible, pred: Callable[[Atspi.Accessible], bool] - ) -> Atspi.Accessible | None: + ) -> Optional[Atspi.Accessible]: """Returns the descendant of obj if the function pred is true""" start = time.time() @@ -816,7 +816,7 @@ class AXObject: return result @staticmethod - def find_deepest_descendant(obj: Atspi.Accessible) -> Atspi.Accessible | None: + def find_deepest_descendant(obj: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the deepest descendant of obj""" if not AXObject.is_valid(obj): @@ -831,8 +831,8 @@ class AXObject: @staticmethod def _find_all_descendants( obj: Atspi.Accessible, - include_if: Callable[[Atspi.Accessible], bool] | None, - exclude_if: Callable[[Atspi.Accessible], bool] | None, + include_if: Optional[Callable[[Atspi.Accessible], bool]], + exclude_if: Optional[Callable[[Atspi.Accessible], bool]], matches: list[Atspi.Accessible] ) -> None: """Returns all descendants which match the specified inclusion and exclusion""" @@ -852,8 +852,8 @@ class AXObject: @staticmethod def find_all_descendants( root: Atspi.Accessible, - include_if: Callable[[Atspi.Accessible], bool] | None = None, - exclude_if: Callable[[Atspi.Accessible], bool] | None = None + include_if: Optional[Callable[[Atspi.Accessible], bool]] = None, + exclude_if: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants which match the specified inclusion and exclusion""" @@ -1044,7 +1044,7 @@ class AXObject: @staticmethod def iter_children( obj: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> Generator[Atspi.Accessible, None, None]: """Generator to iterate through obj's children. If the function pred is specified, children for which pred is False will be skipped.""" @@ -1068,7 +1068,7 @@ class AXObject: yield child @staticmethod - def get_previous_sibling(obj: Atspi.Accessible) -> Atspi.Accessible | None: + def get_previous_sibling(obj: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the previous sibling of obj, based on child indices""" if not AXObject.is_valid(obj): @@ -1091,7 +1091,7 @@ class AXObject: return sibling @staticmethod - def get_next_sibling(obj: Atspi.Accessible) -> Atspi.Accessible | None: + def get_next_sibling(obj: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the next sibling of obj, based on child indices""" if not AXObject.is_valid(obj): diff --git a/src/cthulhu/ax_selection.py b/src/cthulhu/ax_selection.py index 903dd5e..ffbd47e 100644 --- a/src/cthulhu/ax_selection.py +++ b/src/cthulhu/ax_selection.py @@ -28,6 +28,7 @@ __date__ = "$Date$" __copyright__ = "Copyright (c) 2023 Igalia, S.L." __license__ = "LGPL" +from typing import Optional import gi gi.require_version("Atspi", "2.0") from gi.repository import Atspi @@ -60,7 +61,7 @@ class AXSelection: return count @staticmethod - def get_selected_child(obj: Atspi.Accessible, index: int) -> Atspi.Accessible | None: + def get_selected_child(obj: Atspi.Accessible, index: int) -> Optional[Atspi.Accessible]: """Returns the nth selected child of obj.""" n_children = AXSelection.get_selected_child_count(obj) diff --git a/src/cthulhu/ax_table.py b/src/cthulhu/ax_table.py index 7dc4414..97d2975 100644 --- a/src/cthulhu/ax_table.py +++ b/src/cthulhu/ax_table.py @@ -35,7 +35,7 @@ __license__ = "LGPL" import threading import time -from typing import Generator +from typing import Generator, Optional import gi gi.require_version("Atspi", "2.0") @@ -62,11 +62,11 @@ class AXTable: PHYSICAL_SPANS_FROM_TABLE: dict[int, tuple[int, int]] = {} PHYSICAL_COLUMN_COUNT: dict[int, int] = {} PHYSICAL_ROW_COUNT: dict[int, int] = {} - PRESENTABLE_COORDINATES: dict[int, tuple[str | None, str | None]] = {} + PRESENTABLE_COORDINATES: dict[int, tuple[Optional[str], Optional[str]]] = {} PRESENTABLE_COORDINATES_LABELS: dict[int, str] = {} - PRESENTABLE_SPANS: dict[int, tuple[str | None, str | None]] = {} - PRESENTABLE_COLUMN_COUNT: dict[int, int | None] = {} - PRESENTABLE_ROW_COUNT: dict[int, int | None] = {} + PRESENTABLE_SPANS: dict[int, tuple[Optional[str], Optional[str]]] = {} + PRESENTABLE_COLUMN_COUNT: dict[int, Optional[int]] = {} + PRESENTABLE_ROW_COUNT: dict[int, Optional[int]] = {} COLUMN_HEADERS_FOR_CELL: dict[int, list[Atspi.Accessible]] = {} ROW_HEADERS_FOR_CELL: dict[int, list[Atspi.Accessible]] = {} @@ -121,7 +121,7 @@ class AXTable: AXTable._clear_all_dictionaries(reason) @staticmethod - def get_caption(table: Atspi.Accessible) -> Atspi.Accessible | None: + def get_caption(table: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the accessible object containing the caption of table.""" if not AXObject.supports_table(table): @@ -171,7 +171,7 @@ class AXTable: return count @staticmethod - def _get_column_count_from_attribute(table: Atspi.Accessible) -> int | None: + def _get_column_count_from_attribute(table: Atspi.Accessible) -> Optional[int]: """Returns the value of the 'colcount' object attribute or None if not found.""" if hash(table) in AXTable.PRESENTABLE_COLUMN_COUNT: @@ -217,7 +217,7 @@ class AXTable: return count @staticmethod - def _get_row_count_from_attribute(table: Atspi.Accessible) -> int | None: + def _get_row_count_from_attribute(table: Atspi.Accessible) -> Optional[int]: """Returns the value of the 'rowcount' object attribute or None if not found.""" if hash(table) in AXTable.PRESENTABLE_ROW_COUNT: @@ -344,7 +344,7 @@ class AXTable: return AXTable.get_selected_column_count(table) == cols @staticmethod - def get_cell_at(table: Atspi.Accessible, row: int, column: int) -> Atspi.Accessible | None: + def get_cell_at(table: Atspi.Accessible, row: int, column: int) -> Optional[Atspi.Accessible]: """Returns the cell at the 0-indexed row and column.""" if not AXObject.supports_table(table): @@ -405,7 +405,7 @@ class AXTable: @staticmethod def _get_cell_spans_from_attribute( cell: Atspi.Accessible - ) -> tuple[str | None, str | None]: + ) -> tuple[Optional[str], Optional[str]]: """Returns the row and column spans exposed via object attribute, or None, None.""" if hash(cell) in AXTable.PRESENTABLE_SPANS: @@ -606,7 +606,7 @@ class AXTable: @staticmethod def get_new_row_headers( cell: Atspi.Accessible, - old_cell: Atspi.Accessible | None + old_cell: Optional[Atspi.Accessible] ) -> list[Atspi.Accessible]: """Returns row headers of cell that are not also headers of old_cell. """ @@ -623,7 +623,7 @@ class AXTable: @staticmethod def get_new_column_headers( cell: Atspi.Accessible, - old_cell: Atspi.Accessible | None + old_cell: Optional[Atspi.Accessible] ) -> list[Atspi.Accessible]: """Returns column headers of cell that are not also headers of old_cell. """ @@ -880,7 +880,7 @@ class AXTable: @staticmethod def _get_cell_coordinates_from_attribute( cell: Atspi.Accessible - ) -> tuple[str | None, str | None]: + ) -> tuple[Optional[str], Optional[str]]: """Returns the 1-based indices for cell exposed via object attribute, or None, None.""" if cell is None: @@ -941,7 +941,7 @@ class AXTable: return result @staticmethod - def get_table(obj: Atspi.Accessible) -> Atspi.Accessible | None: + def get_table(obj: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns obj if it is a table, otherwise returns the ancestor table of obj.""" if obj is None: @@ -981,21 +981,21 @@ class AXTable: return result @staticmethod - def get_first_cell(table: Atspi.Accessible) -> Atspi.Accessible | None: + def get_first_cell(table: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the first cell in table.""" row, col = 0, 0 return AXTable.get_cell_at(table, row, col) @staticmethod - def get_last_cell(table: Atspi.Accessible) -> Atspi.Accessible | None: + def get_last_cell(table: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the last cell in table.""" row, col = AXTable.get_row_count(table) - 1, AXTable.get_column_count(table) - 1 return AXTable.get_cell_at(table, row, col) @staticmethod - def get_cell_above(cell: Atspi.Accessible) -> Atspi.Accessible | None: + def get_cell_above(cell: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the cell above cell in table.""" row, col = AXTable.get_cell_coordinates(cell, prefer_attribute=False) @@ -1003,7 +1003,7 @@ class AXTable: return AXTable.get_cell_at(AXTable.get_table(cell), row, col) @staticmethod - def get_cell_below(cell: Atspi.Accessible) -> Atspi.Accessible | None: + def get_cell_below(cell: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the cell below cell in table.""" row, col = AXTable.get_cell_coordinates(cell, prefer_attribute=False) @@ -1011,7 +1011,7 @@ class AXTable: return AXTable.get_cell_at(AXTable.get_table(cell), row, col) @staticmethod - def get_cell_on_left(cell: Atspi.Accessible) -> Atspi.Accessible | None: + def get_cell_on_left(cell: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the cell to the left.""" row, col = AXTable.get_cell_coordinates(cell, prefer_attribute=False) @@ -1019,7 +1019,7 @@ class AXTable: return AXTable.get_cell_at(AXTable.get_table(cell), row, col) @staticmethod - def get_cell_on_right(cell: Atspi.Accessible) -> Atspi.Accessible | None: + def get_cell_on_right(cell: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the cell to the right.""" row, col = AXTable.get_cell_coordinates(cell, prefer_attribute=False) @@ -1027,14 +1027,14 @@ class AXTable: return AXTable.get_cell_at(AXTable.get_table(cell), row, col) @staticmethod - def get_start_of_row(cell: Atspi.Accessible) -> Atspi.Accessible | None: + def get_start_of_row(cell: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the cell at the start of cell's row.""" row = AXTable.get_cell_coordinates(cell, prefer_attribute=False)[0] return AXTable.get_cell_at(AXTable.get_table(cell), row, 0) @staticmethod - def get_end_of_row(cell: Atspi.Accessible) -> Atspi.Accessible | None: + def get_end_of_row(cell: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the cell at the end of cell's row.""" row = AXTable.get_cell_coordinates(cell, prefer_attribute=False)[0] @@ -1043,14 +1043,14 @@ class AXTable: return AXTable.get_cell_at(AXTable.get_table(cell), row, col) @staticmethod - def get_top_of_column(cell: Atspi.Accessible) -> Atspi.Accessible | None: + def get_top_of_column(cell: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the cell at the top of cell's column.""" col = AXTable.get_cell_coordinates(cell, prefer_attribute=False)[1] return AXTable.get_cell_at(AXTable.get_table(cell), 0, col) @staticmethod - def get_bottom_of_column(cell: Atspi.Accessible) -> Atspi.Accessible | None: + def get_bottom_of_column(cell: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the cell at the bottom of cell's column.""" col = AXTable.get_cell_coordinates(cell, prefer_attribute=False)[1] @@ -1059,7 +1059,7 @@ class AXTable: return AXTable.get_cell_at(AXTable.get_table(cell), row, col) @staticmethod - def get_cell_formula(cell: Atspi.Accessible) -> str | None: + def get_cell_formula(cell: Atspi.Accessible) -> Optional[str]: """Returns the formula associated with this cell.""" attrs = AXObject.get_attributes_dict(cell, use_cache=False) @@ -1192,7 +1192,7 @@ class AXTable: return result @staticmethod - def get_dynamic_row_header(cell: Atspi.Accessible) -> Atspi.Accessible | None: + def get_dynamic_row_header(cell: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the user-set row header for cell.""" table = AXTable.get_table(cell) @@ -1207,7 +1207,7 @@ class AXTable: return AXTable.get_cell_at(table, cell_row, headers_column) @staticmethod - def get_dynamic_column_header(cell: Atspi.Accessible) -> Atspi.Accessible | None: + def get_dynamic_column_header(cell: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the user-set column header for cell.""" table = AXTable.get_table(cell) diff --git a/src/cthulhu/ax_text.py b/src/cthulhu/ax_text.py index 610e39b..9cafdbf 100644 --- a/src/cthulhu/ax_text.py +++ b/src/cthulhu/ax_text.py @@ -44,7 +44,7 @@ __license__ = "LGPL" import enum import locale import re -from typing import Generator +from typing import Generator, Optional import gi gi.require_version("Atspi", "2.0") @@ -104,7 +104,7 @@ class AXTextAttribute(enum.Enum): WRITING_MODE = ("writing-mode", False) @classmethod - def from_string(cls, string: str) -> "AXTextAttribute" | None: + def from_string(cls, string: str) -> Optional['AXTextAttribute']: """Returns the AXTextAttribute for the specified string.""" for attribute in cls: @@ -114,7 +114,7 @@ class AXTextAttribute(enum.Enum): return None @classmethod - def from_localized_string(cls, string: str) -> "AXTextAttribute" | None: + def from_localized_string(cls, string: str) -> Optional['AXTextAttribute']: """Returns the AXTextAttribute for the specified localized string.""" for attribute in cls: @@ -192,7 +192,7 @@ class AXText: @staticmethod def get_character_at_offset( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> tuple[str, int, int]: """Returns the (character, start, end) for the current or specified offset.""" @@ -239,7 +239,7 @@ class AXText: @staticmethod def get_next_character( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> tuple[str, int, int]: """Returns the next (character, start, end) for the current or specified offset.""" @@ -264,7 +264,7 @@ class AXText: @staticmethod def get_previous_character( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> tuple[str, int, int]: """Returns the previous (character, start, end) for the current or specified offset.""" @@ -291,7 +291,7 @@ class AXText: @staticmethod def iter_character( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> Generator[tuple[str, int, int], None, None]: """Generator to iterate by character in obj starting with the character at offset.""" @@ -312,7 +312,7 @@ class AXText: @staticmethod def get_word_at_offset( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> tuple[str, int, int]: """Returns the (word, start, end) for the current or specified offset.""" @@ -354,7 +354,7 @@ class AXText: @staticmethod def get_next_word( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> tuple[str, int, int]: """Returns the next (word, start, end) for the current or specified offset.""" @@ -379,7 +379,7 @@ class AXText: @staticmethod def get_previous_word( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> tuple[str, int, int]: """Returns the previous (word, start, end) for the current or specified offset.""" @@ -406,7 +406,7 @@ class AXText: @staticmethod def iter_word( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> Generator[tuple[str, int, int], None, None]: """Generator to iterate by word in obj starting with the word at offset.""" @@ -427,7 +427,7 @@ class AXText: @staticmethod def get_line_at_offset( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> tuple[str, int, int]: """Returns the (line, start, end) for the current or specified offset.""" @@ -495,7 +495,7 @@ class AXText: @staticmethod def get_next_line( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> tuple[str, int, int]: """Returns the next (line, start, end) for the current or specified offset.""" @@ -520,7 +520,7 @@ class AXText: @staticmethod def get_previous_line( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> tuple[str, int, int]: """Returns the previous (line, start, end) for the current or specified offset.""" @@ -549,7 +549,7 @@ class AXText: @staticmethod def iter_line( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> Generator[tuple[str, int, int], None, None]: """Generator to iterate by line in obj starting with the line at offset.""" @@ -606,7 +606,7 @@ class AXText: @staticmethod def _get_sentence_at_offset_fallback( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> tuple[str, int, int]: """Fallback sentence detection for broken implementations.""" @@ -633,7 +633,7 @@ class AXText: @staticmethod def get_sentence_at_offset( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> tuple[str, int, int]: """Returns the (sentence, start, end) for the current or specified offset.""" @@ -682,7 +682,7 @@ class AXText: @staticmethod def get_next_sentence( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> tuple[str, int, int]: """Returns the next (sentence, start, end) for the current or specified offset.""" @@ -707,7 +707,7 @@ class AXText: @staticmethod def get_previous_sentence( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> tuple[str, int, int]: """Returns the previous (sentence, start, end) for the current or specified offset.""" @@ -734,7 +734,7 @@ class AXText: @staticmethod def iter_sentence( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> Generator[tuple[str, int, int], None, None]: """Generator to iterate by sentence in obj starting with the sentence at offset.""" @@ -759,7 +759,7 @@ class AXText: @staticmethod def get_paragraph_at_offset( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> tuple[str, int, int]: """Returns the (paragraph, start, end) for the current or specified offset.""" @@ -801,7 +801,7 @@ class AXText: @staticmethod def get_next_paragraph( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> tuple[str, int, int]: """Returns the next (paragraph, start, end) for the current or specified offset.""" @@ -826,7 +826,7 @@ class AXText: @staticmethod def get_previous_paragraph( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> tuple[str, int, int]: """Returns the previous (paragraph, start, end) for the current or specified offset.""" @@ -852,7 +852,7 @@ class AXText: @staticmethod def iter_paragraph( - obj: Atspi.Accessible, offset: int | None = None + obj: Atspi.Accessible, offset: Optional[int] = None ) -> Generator[tuple[str, int, int], None, None]: """Generator to iterate by paragraph in obj starting with the paragraph at offset.""" @@ -1206,7 +1206,7 @@ class AXText: @staticmethod def get_text_attributes_at_offset( obj: Atspi.Accessible, - offset: int | None = None + offset: Optional[int] = None ) -> tuple[dict[str, str], int, int]: """Returns a (dict, start, end) tuple for attributes at offset in obj.""" @@ -1303,7 +1303,7 @@ class AXText: return offset @staticmethod - def get_character_rect(obj: Atspi.Accessible, offset: int | None = None) -> Atspi.Rect: + def get_character_rect(obj: Atspi.Accessible, offset: Optional[int] = None) -> Atspi.Rect: """Returns the Atspi rect of the character at the specified offset in obj.""" if not AXObject.supports_text(obj): @@ -1487,7 +1487,7 @@ class AXText: return result @staticmethod - def string_has_spelling_error(obj: Atspi.Accessible, offset: int | None = None) -> bool: + def string_has_spelling_error(obj: Atspi.Accessible, offset: Optional[int] = None) -> bool: """Returns True if the text attributes indicate a spelling error.""" attributes = AXText.get_text_attributes_at_offset(obj, offset)[0] @@ -1502,7 +1502,7 @@ class AXText: return False @staticmethod - def string_has_grammar_error(obj: Atspi.Accessible, offset: int | None = None) -> bool: + def string_has_grammar_error(obj: Atspi.Accessible, offset: Optional[int] = None) -> bool: """Returns True if the text attributes indicate a grammar error.""" attributes = AXText.get_text_attributes_at_offset(obj, offset)[0] @@ -1552,8 +1552,8 @@ class AXText: obj: Atspi.Accessible, x: int, y: int, - start_offset: int | None = None, - end_offset: int | None = None + start_offset: Optional[int] = None, + end_offset: Optional[int] = None ) -> bool: """Attempts to scroll obj to the specified point.""" @@ -1583,8 +1583,8 @@ class AXText: def scroll_substring_to_location( obj: Atspi.Accessible, location: Atspi.ScrollType, - start_offset: int | None = None, - end_offset: int | None = None + start_offset: Optional[int] = None, + end_offset: Optional[int] = None ) -> bool: """Attempts to scroll the substring to the specified Atspi.ScrollType location.""" diff --git a/src/cthulhu/ax_utilities.py b/src/cthulhu/ax_utilities.py index 5963e4e..4de6e53 100644 --- a/src/cthulhu/ax_utilities.py +++ b/src/cthulhu/ax_utilities.py @@ -34,6 +34,7 @@ __date__ = "$Date$" __copyright__ = "Copyright (c) 2023-2025 Igalia, S.L." __license__ = "LGPL" +from typing import Optional import functools import inspect import queue @@ -97,7 +98,7 @@ class AXUtilities: AXUtilities.IS_LAYOUT_ONLY.clear() @staticmethod - def clear_all_cache_now(obj: Atspi.Accessible | None = None, reason: str = "") -> None: + def clear_all_cache_now(obj: Optional[Atspi.Accessible] = None, reason: str = "") -> None: """Clears all cached information immediately.""" AXUtilities._clear_all_dictionaries(reason) @@ -152,7 +153,7 @@ class AXUtilities: return True @staticmethod - def find_active_window() -> Atspi.Accessible | None: + def find_active_window() -> Optional[Atspi.Accessible]: """Tries to locate the active window; may or may not succeed.""" candidates = [] @@ -192,7 +193,7 @@ class AXUtilities: debug.print_tokens(debug.LEVEL_INFO, tokens, True) return filtered[0] - guess: Atspi.Accessible | None = None + guess: Optional[Atspi.Accessible] = None if filtered: tokens = ["AXUtilities: Still have multiple active windows:", filtered] debug.print_tokens(debug.LEVEL_INFO, tokens, True) @@ -266,7 +267,7 @@ class AXUtilities: return AXObject.find_all_descendants(obj, is_match) @staticmethod - def get_default_button(obj: Atspi.Accessible) -> Atspi.Accessible | None: + def get_default_button(obj: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the default button descendant of obj""" result = None @@ -278,7 +279,7 @@ class AXUtilities: return AXObject.find_descendant(obj, AXUtilitiesRole.is_default_button) @staticmethod - def get_focused_object(obj: Atspi.Accessible) -> Atspi.Accessible | None: + def get_focused_object(obj: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the focused descendant of obj""" result = None @@ -290,7 +291,7 @@ class AXUtilities: return AXObject.find_descendant(obj, AXUtilitiesState.is_focused) @staticmethod - def get_info_bar(obj: Atspi.Accessible) -> Atspi.Accessible | None: + def get_info_bar(obj: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the info bar descendant of obj""" result = None @@ -302,7 +303,7 @@ class AXUtilities: return AXObject.find_descendant(obj, AXUtilitiesRole.is_info_bar) @staticmethod - def get_status_bar(obj: Atspi.Accessible) -> Atspi.Accessible | None: + def get_status_bar(obj: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the status bar descendant of obj""" result = None @@ -748,7 +749,7 @@ class AXUtilities: return len(ancestors) @staticmethod - def get_next_object(obj: Atspi.Accessible) -> Atspi.Accessible | None: + def get_next_object(obj: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the next object (depth first, unless there's a flows-to relation)""" if not AXObject.is_valid(obj): @@ -778,7 +779,7 @@ class AXUtilities: return next_object @staticmethod - def get_previous_object(obj: Atspi.Accessible) -> Atspi.Accessible | None: + def get_previous_object(obj: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the previous object (depth first, unless there's a flows-from relation)""" if not AXObject.is_valid(obj): @@ -810,7 +811,7 @@ class AXUtilities: @staticmethod def is_on_screen( obj: Atspi.Accessible, - bounding_box: Atspi.Rect | None = None + bounding_box: Optional[Atspi.Rect] = None ) -> bool: """Returns true if obj should be treated as being on screen.""" @@ -887,7 +888,7 @@ class AXUtilities: def _get_on_screen_objects( root: Atspi.Accessible, cancellation_event: threading.Event, - bounding_box: Atspi.Rect | None = None + bounding_box: Optional[Atspi.Rect] = None ) -> list: tokens = ["AXUtilities: Getting on-screen objects in", root, f"({hex(id(root))})"] @@ -939,7 +940,7 @@ class AXUtilities: @staticmethod def get_on_screen_objects( root: Atspi.Accessible, - bounding_box: Atspi.Rect | None = None, + bounding_box: Optional[Atspi.Rect] = None, timeout: float = 5.0 ) -> list: """Returns a list of onscreen objects in the given root.""" diff --git a/src/cthulhu/ax_utilities_application.py b/src/cthulhu/ax_utilities_application.py index dbaca2e..df82c48 100644 --- a/src/cthulhu/ax_utilities_application.py +++ b/src/cthulhu/ax_utilities_application.py @@ -30,6 +30,7 @@ __copyright__ = "Copyright (c) 2023-2024 Igalia, S.L." \ "Copyright (c) 2024 GNOME Foundation Inc." __license__ = "LGPL" +from typing import Optional import subprocess import gi @@ -82,7 +83,7 @@ class AXUtilitiesApplication: return list(AXObject.iter_children(desktop, pred)) @staticmethod - def get_application(obj: Atspi.Accessible) -> Atspi.Accessible | None: + def get_application(obj: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the accessible application associated with obj""" if obj is None: @@ -131,7 +132,7 @@ class AXUtilitiesApplication: return version @staticmethod - def get_application_with_pid(pid: int) -> Atspi.Accessible | None: + def get_application_with_pid(pid: int) -> Optional[Atspi.Accessible]: """Returns the accessible application with the specified pid""" applications = AXUtilitiesApplication.get_all_applications() @@ -144,7 +145,7 @@ class AXUtilitiesApplication: return None @staticmethod - def get_desktop() -> Atspi.Accessible | None: + def get_desktop() -> Optional[Atspi.Accessible]: """Returns the accessible desktop""" try: diff --git a/src/cthulhu/ax_utilities_collection.py b/src/cthulhu/ax_utilities_collection.py index f4e2c8d..98f4dda 100644 --- a/src/cthulhu/ax_utilities_collection.py +++ b/src/cthulhu/ax_utilities_collection.py @@ -32,7 +32,7 @@ __license__ = "LGPL" import inspect import time -from typing import Callable +from typing import Callable, Optional import gi gi.require_version("Atspi", "2.0") @@ -72,7 +72,7 @@ class AXUtilitiesCollection: root: Atspi.Accessible, state_list: list[Atspi.StateType], state_match_type: Atspi.CollectionMatchType, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: if not (root and state_list): return [] @@ -94,7 +94,7 @@ class AXUtilitiesCollection: root: Atspi.Accessible, role_list: list[Atspi.Role], role_match_type: Atspi.CollectionMatchType, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: if not (root and role_list): return [] @@ -120,7 +120,7 @@ class AXUtilitiesCollection: root: Atspi.Accessible, role_list: list[Atspi.Role], role_match_type: Atspi.CollectionMatchType, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: def matchesLandmarkRole(acc, role): if role == Atspi.Role.LANDMARK: @@ -200,7 +200,7 @@ class AXUtilitiesCollection: def find_all_with_interfaces( root: Atspi.Accessible, interface_list: list[str], - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which implement all the specified interfaces""" @@ -223,7 +223,7 @@ class AXUtilitiesCollection: def find_all_with_role( root: Atspi.Accessible, role_list: list[Atspi.Role], - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with any of the specified roles""" @@ -234,7 +234,7 @@ class AXUtilitiesCollection: def find_all_without_roles( root: Atspi.Accessible, role_list: list[Atspi.Role], - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which have none of the specified roles""" @@ -246,7 +246,7 @@ class AXUtilitiesCollection: root: Atspi.Accessible, role_list: list[Atspi.Role], state_list: list[Atspi.StateType], - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with any of the roles, and all the states""" @@ -273,7 +273,7 @@ class AXUtilitiesCollection: root: Atspi.Accessible, role_list: list[Atspi.Role], state_list: list[Atspi.StateType], - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with any of the roles, and any of the states""" @@ -300,7 +300,7 @@ class AXUtilitiesCollection: root: Atspi.Accessible, role_list: list[Atspi.Role], state_list: list[Atspi.StateType], - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with any of the roles, and none of the states""" @@ -326,7 +326,7 @@ class AXUtilitiesCollection: def find_all_with_states( root: Atspi.Accessible, state_list: list[Atspi.StateType], - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which have all of the specified states""" @@ -337,7 +337,7 @@ class AXUtilitiesCollection: def find_all_with_any_state( root: Atspi.Accessible, state_list: list[Atspi.StateType], - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which have any of the specified states""" @@ -348,7 +348,7 @@ class AXUtilitiesCollection: def find_all_without_states( root: Atspi.Accessible, state_list: list[Atspi.StateType], - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which have none of the specified states""" @@ -358,7 +358,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_accelerator_labels( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the accelerator label role""" @@ -368,7 +368,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_alerts( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the alert role""" @@ -378,7 +378,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_animations( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the animation role""" @@ -388,7 +388,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_arrows( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the arrow role""" @@ -398,7 +398,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_articles( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the article role""" @@ -408,7 +408,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_audios( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the audio role""" @@ -418,7 +418,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_autocompletes( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the autocomplete role""" @@ -428,7 +428,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_block_quotes( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the block quote role""" @@ -438,7 +438,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_buttons( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the push- or toggle-button role""" @@ -448,7 +448,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_calendars( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the calendar role""" @@ -458,7 +458,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_canvases( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the canvas role""" @@ -468,7 +468,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_captions( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the caption role""" @@ -478,7 +478,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_charts( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the chart role""" @@ -488,7 +488,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_check_boxes( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the checkbox role""" @@ -498,7 +498,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_check_menu_items( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the check menuitem role""" @@ -508,7 +508,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_clickables( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all non-focusable descendants of root which support the click action""" @@ -551,7 +551,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_color_choosers( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the color_chooser role""" @@ -561,7 +561,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_column_headers( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the column header role""" @@ -571,7 +571,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_combo_boxes( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the combobox role""" @@ -581,7 +581,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_comments( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the comment role""" @@ -591,7 +591,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_content_deletions( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the content deletion role""" @@ -601,7 +601,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_content_insertions( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the content insertion role""" @@ -611,7 +611,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_date_editors( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the date editor role""" @@ -621,7 +621,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_definitions( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the definition role""" @@ -631,7 +631,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_description_lists( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the description list role""" @@ -641,7 +641,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_description_terms( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the description term role""" @@ -651,7 +651,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_description_values( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the description value role""" @@ -661,7 +661,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_desktop_frames( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the desktop frame role""" @@ -671,7 +671,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_desktop_icons( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the desktop icon role""" @@ -681,7 +681,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_dials( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the dial role""" @@ -691,7 +691,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_dialogs( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the dialog role""" @@ -701,7 +701,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_dialogs_and_alerts( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root that has any dialog or alert role""" @@ -711,7 +711,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_directory_panes( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the directory pane role""" @@ -721,7 +721,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_documents( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root that has any document-related role""" @@ -731,7 +731,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_document_emails( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the document email role""" @@ -741,7 +741,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_document_frames( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the document frame role""" @@ -751,7 +751,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_document_presentations( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the document presentation role""" @@ -761,7 +761,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_document_spreadsheets( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the document spreadsheet role""" @@ -771,7 +771,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_document_texts( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the document text role""" @@ -781,7 +781,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_document_webs( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the document web role""" @@ -791,7 +791,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_drawing_areas( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the drawing area role""" @@ -802,7 +802,7 @@ class AXUtilitiesCollection: def find_all_editable_objects( root: Atspi.Accessible, must_be_focusable: bool = True, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which are editable""" @@ -814,7 +814,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_editbars( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the editbar role""" @@ -824,7 +824,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_embeddeds( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the embedded role""" @@ -834,7 +834,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_entries( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the entry role""" @@ -844,7 +844,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_extendeds( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the extended role""" @@ -854,7 +854,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_file_choosers( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the file chooser role""" @@ -864,7 +864,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_fillers( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the filler role""" @@ -874,7 +874,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_focusable_objects( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which are focusable""" @@ -884,7 +884,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_focusable_objects_with_click_ancestor( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all focusable descendants of root which support the click-ancestor action""" @@ -923,7 +923,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_focused_objects( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which are focused""" @@ -933,7 +933,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_focus_traversables( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the focus traversable role""" @@ -943,7 +943,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_font_choosers( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the font chooser role""" @@ -953,7 +953,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_footers( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the footer role""" @@ -963,7 +963,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_footnotes( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the footnote role""" @@ -973,7 +973,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_forms( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the form role""" @@ -984,7 +984,7 @@ class AXUtilitiesCollection: def find_all_form_fields( root: Atspi.Accessible, must_be_focusable: bool = True, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with a form-field-related role""" @@ -998,7 +998,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_frames( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the frame role""" @@ -1008,7 +1008,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_glass_panes( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the glass pane role""" @@ -1018,7 +1018,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_grids( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root that are grids""" @@ -1041,7 +1041,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_grid_cells( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root that are grid cells""" @@ -1068,7 +1068,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_groupings( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the grouping role""" @@ -1078,7 +1078,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_headers( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the header role""" @@ -1088,7 +1088,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_headings( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the heading role""" @@ -1099,7 +1099,7 @@ class AXUtilitiesCollection: def find_all_headings_at_level( root: Atspi.Accessible, level: int, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the heading role""" @@ -1121,7 +1121,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_html_containers( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the html container role""" @@ -1131,7 +1131,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_horizontal_scrollbars( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root that is a horizontal scrollbar""" @@ -1142,7 +1142,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_horizontal_separators( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root that is a horizontal separator""" @@ -1153,7 +1153,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_horizontal_sliders( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root that is a horizontal slider""" @@ -1164,7 +1164,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_icons( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the icon role""" @@ -1174,7 +1174,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_icons_and_canvases( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the icon or canvas role""" @@ -1184,7 +1184,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_images( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the image role""" @@ -1194,7 +1194,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_images_and_canvases( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the image or canvas role""" @@ -1204,7 +1204,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_images_and_image_maps( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the image or image map role""" @@ -1214,7 +1214,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_image_maps( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the image map role""" @@ -1224,7 +1224,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_info_bars( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the info bar role""" @@ -1234,7 +1234,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_input_method_windows( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the input method window role""" @@ -1244,7 +1244,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_internal_frames( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the internal frame role""" @@ -1254,7 +1254,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_labels( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the label role""" @@ -1264,7 +1264,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_labels_and_captions( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the label or caption role""" @@ -1274,7 +1274,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_landmarks( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the landmark role""" @@ -1284,7 +1284,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_large_containers( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root that we consider a large container""" @@ -1294,7 +1294,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_layered_panes( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the layered pane role""" @@ -1304,7 +1304,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_level_bars( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the level bar role""" @@ -1315,7 +1315,7 @@ class AXUtilitiesCollection: def find_all_links( root: Atspi.Accessible, must_be_focusable: bool = True, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the link role""" @@ -1329,7 +1329,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_live_regions( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root that are live regions""" @@ -1356,7 +1356,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_lists( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None, + pred: Optional[Callable[[Atspi.Accessible], bool]] = None, include_description_lists: bool = False, include_tab_lists: bool = False ) -> list[Atspi.Accessible]: @@ -1372,7 +1372,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_list_boxes( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the list box role""" @@ -1382,7 +1382,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_list_items( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None, + pred: Optional[Callable[[Atspi.Accessible], bool]] = None, include_description_terms: bool = False, include_tabs: bool = False ) -> list[Atspi.Accessible]: @@ -1398,7 +1398,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_logs( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the log role""" @@ -1408,7 +1408,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_marks( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the mark role""" @@ -1418,7 +1418,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_marquees( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the marquee role""" @@ -1428,7 +1428,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_maths( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the math role""" @@ -1438,7 +1438,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_math_fractions( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the math fraction role""" @@ -1448,7 +1448,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_math_roots( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the math root role""" @@ -1458,7 +1458,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_menus( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the menu role""" @@ -1468,7 +1468,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_menu_bars( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the menubar role""" @@ -1478,7 +1478,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_menu_items( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the menu item role""" @@ -1488,7 +1488,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_menu_items_of_any_kind( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root that has any menu item role""" @@ -1498,7 +1498,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_menu_related_objects( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root that has any menu-related role""" @@ -1508,7 +1508,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_modal_dialogs( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the alert or dialog role and modal state""" @@ -1519,7 +1519,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_multi_line_entries( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the entry role and multiline state""" @@ -1530,7 +1530,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_notifications( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the notification role""" @@ -1540,7 +1540,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_option_panes( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the option pane role""" @@ -1550,7 +1550,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_pages( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the page role""" @@ -1560,7 +1560,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_page_tabs( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the page tab role""" @@ -1570,7 +1570,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_page_tab_lists( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the page tab list role""" @@ -1580,7 +1580,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_page_tab_list_related_objects( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the page tab or page tab list role""" @@ -1590,7 +1590,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_panels( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the panel role""" @@ -1601,7 +1601,7 @@ class AXUtilitiesCollection: def find_all_paragraphs( root: Atspi.Accessible, treat_headings_as_paragraphs: bool = False, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the paragraph role""" @@ -1613,7 +1613,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_password_texts( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the password text role""" @@ -1623,7 +1623,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_popup_menus( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the popup menu role""" @@ -1633,7 +1633,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_progress_bars( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the progress bar role""" @@ -1643,7 +1643,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_push_buttons( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the push button role""" @@ -1653,7 +1653,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_push_button_menus( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the push button menu role""" @@ -1663,7 +1663,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_radio_buttons( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the radio button role""" @@ -1673,7 +1673,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_radio_menu_items( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the radio menu item role""" @@ -1683,7 +1683,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_ratings( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the rating role""" @@ -1693,7 +1693,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_root_panes( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the root pane role""" @@ -1703,7 +1703,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_row_headers( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the row header role""" @@ -1713,7 +1713,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_rulers( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the ruler role""" @@ -1723,7 +1723,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_scroll_bars( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the scrollbar role""" @@ -1733,7 +1733,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_scroll_panes( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the scroll pane role""" @@ -1743,7 +1743,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_sections( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the section role""" @@ -1753,7 +1753,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_selectable_objects( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which are selectable""" @@ -1763,7 +1763,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_selected_objects( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which are selected""" @@ -1773,7 +1773,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_separators( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the separator role""" @@ -1783,7 +1783,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_set_containers( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with a set container role""" @@ -1793,7 +1793,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_showing_objects( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which are showing""" @@ -1803,7 +1803,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_showing_and_visible_objects( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which are showing and visible""" @@ -1813,7 +1813,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_showing_or_visible_objects( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which are showing or visible""" @@ -1823,7 +1823,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_single_line_entries( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the entry role and multiline state""" @@ -1834,7 +1834,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_sliders( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the slider role""" @@ -1844,7 +1844,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_spin_buttons( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the spin button role""" @@ -1854,7 +1854,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_split_panes( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the split pane role""" @@ -1864,7 +1864,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_statics( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the static role""" @@ -1874,7 +1874,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_status_bars( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the statusbar role""" @@ -1884,7 +1884,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_subscripts( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the subscript role""" @@ -1894,7 +1894,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_subscripts_and_superscripts( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the subscript or superscript role""" @@ -1904,7 +1904,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_suggestions( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the suggestion role""" @@ -1914,7 +1914,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_superscripts( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the superscript role""" @@ -1924,7 +1924,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_supports_action( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which support the action interface""" @@ -1934,7 +1934,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_supports_document( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which support the document interface""" @@ -1944,7 +1944,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_supports_editable_text( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which support the editable text interface""" @@ -1954,7 +1954,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_supports_hypertext( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which support the hypertext interface""" @@ -1964,7 +1964,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_supports_hyperlink( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which support the hyperlink interface""" @@ -1974,7 +1974,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_supports_selection( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which support the selection interface""" @@ -1984,7 +1984,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_supports_table( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which support the table interface""" @@ -1994,7 +1994,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_supports_table_cell( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which support the table cell interface""" @@ -2004,7 +2004,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_supports_text( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which support the text interface""" @@ -2014,7 +2014,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_supports_value( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which support the value interface""" @@ -2024,7 +2024,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_tables( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the table role""" @@ -2051,7 +2051,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_table_cells( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the table cell role""" @@ -2061,7 +2061,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_table_cells_and_headers( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the table cell or a header-related role""" @@ -2071,7 +2071,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_table_column_headers( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the table column header role""" @@ -2081,7 +2081,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_table_headers( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root that has a table header related role""" @@ -2091,7 +2091,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_table_related_objects( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None, + pred: Optional[Callable[[Atspi.Accessible], bool]] = None, include_caption: bool = False ) -> list[Atspi.Accessible]: """Returns all descendants of root that has a table related role""" @@ -2102,7 +2102,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_table_rows( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the table row role""" @@ -2112,7 +2112,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_table_row_headers( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the table row header role""" @@ -2122,7 +2122,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_tearoff_menu_items( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the tearoff menu item role""" @@ -2132,7 +2132,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_terminals( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the terminal role""" @@ -2142,7 +2142,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_texts( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the text role""" @@ -2152,7 +2152,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_text_inputs( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root that has any role associated with textual input""" @@ -2162,7 +2162,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_timers( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the timer role""" @@ -2172,7 +2172,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_title_bars( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the titlebar role""" @@ -2182,7 +2182,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_toggle_buttons( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the toggle button role""" @@ -2192,7 +2192,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_tool_bars( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the toolbar role""" @@ -2202,7 +2202,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_tool_tips( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the tooltip role""" @@ -2212,7 +2212,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_trees( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the tree role""" @@ -2222,7 +2222,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_trees_and_tree_tables( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the tree or tree table role""" @@ -2232,7 +2232,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_tree_related_objects( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root that has a tree related role""" @@ -2242,7 +2242,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_tree_items( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the tree item role""" @@ -2252,7 +2252,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_tree_tables( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the tree table role""" @@ -2263,7 +2263,7 @@ class AXUtilitiesCollection: def find_all_unrelated_labels( root: Atspi.Accessible, must_be_showing: bool = True, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all the descendants of root that have a label role, but no relations""" @@ -2288,7 +2288,7 @@ class AXUtilitiesCollection: def find_all_unvisited_links( root: Atspi.Accessible, must_be_focusable: bool = True, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the link role and without the visited state""" @@ -2302,7 +2302,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_vertical_scrollbars( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root that is a vertical scrollbar""" @@ -2313,7 +2313,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_vertical_separators( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root that is a vertical separator""" @@ -2324,7 +2324,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_vertical_sliders( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root that is a vertical slider""" @@ -2335,7 +2335,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_visible_objects( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root which are visible""" @@ -2345,7 +2345,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_videos( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the video role""" @@ -2355,7 +2355,7 @@ class AXUtilitiesCollection: @staticmethod def find_all_viewports( root: Atspi.Accessible, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the viewport role""" @@ -2366,7 +2366,7 @@ class AXUtilitiesCollection: def find_all_visited_links( root: Atspi.Accessible, must_be_focusable: bool = True, - pred: Callable[[Atspi.Accessible], bool] | None = None + pred: Optional[Callable[[Atspi.Accessible], bool]] = None ) -> list[Atspi.Accessible]: """Returns all descendants of root with the link role and focused and visited states""" @@ -2377,7 +2377,7 @@ class AXUtilitiesCollection: return AXUtilitiesCollection.find_all_with_role_and_all_states(root, roles, states, pred) @staticmethod - def find_default_button(root: Atspi.Accessible) -> Atspi.Accessible | None: + def find_default_button(root: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the default button inside root""" roles = [Atspi.Role.BUTTON] @@ -2386,7 +2386,7 @@ class AXUtilitiesCollection: return AXCollection.get_first_match(root, rule) @staticmethod - def find_focused_object(root: Atspi.Accessible) -> Atspi.Accessible | None: + def find_focused_object(root: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the focused object inside root""" states = [Atspi.StateType.FOCUSED] @@ -2394,7 +2394,7 @@ class AXUtilitiesCollection: return AXCollection.get_first_match(root, rule) @staticmethod - def find_info_bar(root: Atspi.Accessible) -> Atspi.Accessible | None: + def find_info_bar(root: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the info bar inside root""" roles = [Atspi.Role.INFO_BAR] @@ -2403,7 +2403,7 @@ class AXUtilitiesCollection: return AXCollection.get_first_match(root, rule) @staticmethod - def find_status_bar(root: Atspi.Accessible) -> Atspi.Accessible | None: + def find_status_bar(root: Atspi.Accessible) -> Optional[Atspi.Accessible]: """Returns the status bar inside root""" roles = [Atspi.Role.STATUS_BAR] diff --git a/src/cthulhu/ax_utilities_relation.py b/src/cthulhu/ax_utilities_relation.py index 0947f74..c9cfee4 100644 --- a/src/cthulhu/ax_utilities_relation.py +++ b/src/cthulhu/ax_utilities_relation.py @@ -35,6 +35,7 @@ __copyright__ = "Copyright (c) 2024 Igalia, S.L." \ "Copyright (c) 2024 GNOME Foundation Inc." __license__ = "LGPL" +from typing import Optional import threading import time @@ -118,7 +119,7 @@ class AXUtilitiesRelation: def _get_relation( obj: Atspi.Accessible, relation_type: Atspi.RelationType - ) -> Atspi.Relation | None: + ) -> Optional[Atspi.Relation]: """Returns the specified Atspi.Relation for obj""" for relation in AXUtilitiesRelation.get_relations(obj): diff --git a/src/cthulhu/ax_utilities_role.py b/src/cthulhu/ax_utilities_role.py index e4901d3..269d39d 100644 --- a/src/cthulhu/ax_utilities_role.py +++ b/src/cthulhu/ax_utilities_role.py @@ -33,6 +33,7 @@ __date__ = "$Date$" __copyright__ = "Copyright (c) 2023 Igalia, S.L." __license__ = "LGPL" +from typing import Optional import gi gi.require_version("Atspi", "2.0") from gi.repository import Atspi @@ -51,7 +52,7 @@ class AXUtilitiesRole: return attrs.get("display", "") @staticmethod - def _get_tag(obj: Atspi.Accessible) -> str | None: + def _get_tag(obj: Atspi.Accessible) -> Optional[str]: attrs = AXObject.get_attributes_dict(obj) return attrs.get("tag") @@ -63,7 +64,7 @@ class AXUtilitiesRole: @staticmethod def children_are_presentational( obj: Atspi.Accessible, - role: Atspi.Role | None = None + role: Optional[Atspi.Role] = None ) -> bool: """Returns True if the descendants of obj should be ignored. See ARIA spec.""" @@ -307,7 +308,7 @@ class AXUtilitiesRole: return roles @staticmethod - def get_localized_role_name(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> str: + def get_localized_role_name(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> str: """Returns a string representing the localized role name of obj.""" if role is None: @@ -455,7 +456,7 @@ class AXUtilitiesRole: return AXObject.get_role(obj1) == AXObject.get_role(obj2) @staticmethod - def is_accelerator_label(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_accelerator_label(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the accelerator label role""" if role is None: @@ -463,7 +464,7 @@ class AXUtilitiesRole: return role == Atspi.Role.ACCELERATOR_LABEL @staticmethod - def is_alert(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_alert(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the alert (a type of dialog) role""" if role is None: @@ -471,7 +472,7 @@ class AXUtilitiesRole: return role == Atspi.Role.ALERT @staticmethod - def is_aria_alert(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_aria_alert(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is an ARIA alert (should have notification role)""" if "alert" not in AXUtilitiesRole._get_xml_roles(obj): @@ -487,7 +488,7 @@ class AXUtilitiesRole: return True @staticmethod - def is_animation(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_animation(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the animation role""" if role is None: @@ -495,7 +496,7 @@ class AXUtilitiesRole: return role == Atspi.Role.ANIMATION @staticmethod - def is_application(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_application(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the application role""" if role is None: @@ -503,7 +504,7 @@ class AXUtilitiesRole: return role == Atspi.Role.APPLICATION @staticmethod - def is_arrow(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_arrow(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the arrow role""" if role is None: @@ -511,7 +512,7 @@ class AXUtilitiesRole: return role == Atspi.Role.ARROW @staticmethod - def is_article(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_article(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the article role""" if role is None: @@ -519,7 +520,7 @@ class AXUtilitiesRole: return role == Atspi.Role.ARTICLE @staticmethod - def is_audio(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_audio(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the audio role""" if role is None: @@ -527,7 +528,7 @@ class AXUtilitiesRole: return role == Atspi.Role.AUDIO @staticmethod - def is_autocomplete(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_autocomplete(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the autocomplete role""" if role is None: @@ -535,7 +536,7 @@ class AXUtilitiesRole: return role == Atspi.Role.AUTOCOMPLETE @staticmethod - def is_block_quote(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_block_quote(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the block quote role""" if role is None: @@ -543,7 +544,7 @@ class AXUtilitiesRole: return role == Atspi.Role.BLOCK_QUOTE or AXUtilitiesRole._get_tag(obj) == "blockquote" @staticmethod - def is_button(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_button(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the push- or toggle-button role""" if role is None: @@ -551,7 +552,7 @@ class AXUtilitiesRole: return role in [Atspi.Role.BUTTON, Atspi.Role.TOGGLE_BUTTON] @staticmethod - def is_button_with_popup(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_button_with_popup(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the push- or toggle-button role and a popup""" if not AXUtilitiesRole.is_button(obj, role): @@ -559,7 +560,7 @@ class AXUtilitiesRole: return AXUtilitiesState.has_popup(obj) @staticmethod - def is_calendar(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_calendar(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the calendar role""" if role is None: @@ -567,7 +568,7 @@ class AXUtilitiesRole: return role == Atspi.Role.CALENDAR @staticmethod - def is_canvas(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_canvas(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the canvas role""" if role is None: @@ -575,7 +576,7 @@ class AXUtilitiesRole: return role == Atspi.Role.CANVAS @staticmethod - def is_caption(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_caption(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the caption role""" if role is None: @@ -583,7 +584,7 @@ class AXUtilitiesRole: return role == Atspi.Role.CAPTION @staticmethod - def is_chart(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_chart(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the chart role""" if role is None: @@ -591,7 +592,7 @@ class AXUtilitiesRole: return role == Atspi.Role.CHART @staticmethod - def is_check_box(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_check_box(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the checkbox role""" if role is None: @@ -599,7 +600,7 @@ class AXUtilitiesRole: return role == Atspi.Role.CHECK_BOX @staticmethod - def is_check_menu_item(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_check_menu_item(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the check menuitem role""" if role is None: @@ -607,14 +608,14 @@ class AXUtilitiesRole: return role == Atspi.Role.CHECK_MENU_ITEM @staticmethod - def is_code(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_code(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the code or code-like role""" return "code" in AXUtilitiesRole._get_xml_roles(obj) \ or AXUtilitiesRole._get_tag(obj) in ["code", "pre"] @staticmethod - def is_color_chooser(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_color_chooser(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the color_chooser role""" if role is None: @@ -622,7 +623,7 @@ class AXUtilitiesRole: return role == Atspi.Role.COLOR_CHOOSER @staticmethod - def is_column_header(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_column_header(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the column header role""" if role is None: @@ -630,7 +631,7 @@ class AXUtilitiesRole: return role == Atspi.Role.COLUMN_HEADER @staticmethod - def is_combo_box(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_combo_box(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the combobox role""" if role is None: @@ -638,7 +639,7 @@ class AXUtilitiesRole: return role == Atspi.Role.COMBO_BOX @staticmethod - def is_comment(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_comment(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the comment role""" if role is None: @@ -646,7 +647,7 @@ class AXUtilitiesRole: return role == Atspi.Role.COMMENT or "comment" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_content_deletion(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_content_deletion(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the content deletion role""" if role is None: @@ -656,7 +657,7 @@ class AXUtilitiesRole: or "del" == AXUtilitiesRole._get_tag(obj) @staticmethod - def is_content_insertion(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_content_insertion(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the content insertion role""" if role is None: @@ -666,7 +667,7 @@ class AXUtilitiesRole: or "ins" == AXUtilitiesRole._get_tag(obj) @staticmethod - def is_date_editor(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_date_editor(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the date editor role""" if role is None: @@ -674,14 +675,14 @@ class AXUtilitiesRole: return role == Atspi.Role.DATE_EDITOR @staticmethod - def is_default_button(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_default_button(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the push button role the is-default state""" return AXUtilitiesRole.is_push_button(obj, role) \ and AXObject.has_state(obj, Atspi.StateType.IS_DEFAULT) @staticmethod - def is_definition(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_definition(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the definition role""" if role is None: @@ -689,7 +690,7 @@ class AXUtilitiesRole: return role == Atspi.Role.DEFINITION @staticmethod - def is_description_list(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_description_list(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the description list role""" if role is None: @@ -698,7 +699,7 @@ class AXUtilitiesRole: or "dl" == AXUtilitiesRole._get_tag(obj) @staticmethod - def is_description_term(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_description_term(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the description term role""" if role is None: @@ -707,7 +708,7 @@ class AXUtilitiesRole: or "dt" == AXUtilitiesRole._get_tag(obj) @staticmethod - def is_description_value(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_description_value(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the description value role""" if role is None: @@ -716,7 +717,7 @@ class AXUtilitiesRole: or "dd" == AXUtilitiesRole._get_tag(obj) @staticmethod - def is_desktop_frame(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_desktop_frame(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the desktop frame role""" if role is None: @@ -729,7 +730,7 @@ class AXUtilitiesRole: return False @staticmethod - def is_desktop_icon(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_desktop_icon(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the desktop icon role""" if role is None: @@ -737,7 +738,7 @@ class AXUtilitiesRole: return role == Atspi.Role.DESKTOP_ICON @staticmethod - def is_dial(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_dial(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the dial role""" if role is None: @@ -745,7 +746,7 @@ class AXUtilitiesRole: return role == Atspi.Role.DIAL @staticmethod - def is_dialog(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_dialog(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the dialog role""" if role is None: @@ -753,7 +754,7 @@ class AXUtilitiesRole: return role == Atspi.Role.DIALOG @staticmethod - def is_dialog_or_alert(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_dialog_or_alert(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has any dialog or alert role""" roles = AXUtilitiesRole.get_dialog_roles(True) @@ -762,7 +763,7 @@ class AXUtilitiesRole: return role in roles @staticmethod - def is_dialog_or_window(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_dialog_or_window(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has any dialog or window-related role""" roles = AXUtilitiesRole.get_dialog_roles(False) @@ -772,7 +773,7 @@ class AXUtilitiesRole: return role in roles @staticmethod - def is_directory_pane(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_directory_pane(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the directory pane role""" if role is None: @@ -780,7 +781,7 @@ class AXUtilitiesRole: return role == Atspi.Role.DIRECTORY_PANE @staticmethod - def is_docked_frame(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_docked_frame(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the frame role and is docked.""" if not AXUtilitiesRole.is_frame(obj, role): @@ -790,7 +791,7 @@ class AXUtilitiesRole: return attrs.get("window-type") == "dock" @staticmethod - def is_document(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_document(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has any document-related role""" roles = AXUtilitiesRole.get_document_roles() @@ -799,7 +800,7 @@ class AXUtilitiesRole: return role in roles @staticmethod - def is_document_email(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_document_email(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the document email role""" if role is None: @@ -807,7 +808,7 @@ class AXUtilitiesRole: return role == Atspi.Role.DOCUMENT_EMAIL @staticmethod - def is_document_frame(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_document_frame(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the document frame role""" if role is None: @@ -815,7 +816,7 @@ class AXUtilitiesRole: return role == Atspi.Role.DOCUMENT_FRAME @staticmethod - def is_document_presentation(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_document_presentation(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the document presentation role""" if role is None: @@ -823,7 +824,7 @@ class AXUtilitiesRole: return role == Atspi.Role.DOCUMENT_PRESENTATION @staticmethod - def is_document_spreadsheet(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_document_spreadsheet(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the document spreadsheet role""" if role is None: @@ -831,7 +832,7 @@ class AXUtilitiesRole: return role == Atspi.Role.DOCUMENT_SPREADSHEET @staticmethod - def is_document_text(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_document_text(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the document text role""" if role is None: @@ -839,7 +840,7 @@ class AXUtilitiesRole: return role == Atspi.Role.DOCUMENT_TEXT @staticmethod - def is_document_web(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_document_web(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the document web role""" if role is None: @@ -847,7 +848,7 @@ class AXUtilitiesRole: return role == Atspi.Role.DOCUMENT_WEB @staticmethod - def is_dpub(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has a DPub role.""" roles = AXUtilitiesRole._get_xml_roles(obj) @@ -855,223 +856,223 @@ class AXUtilitiesRole: return rv @staticmethod - def is_dpub_abstract(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_abstract(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub abstract role.""" return "doc-abstract" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_acknowledgments(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_acknowledgments(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub acknowledgments role.""" return "doc-acknowledgments" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_afterword(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_afterword(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub afterword role.""" return "doc-afterword" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_appendix(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_appendix(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub appendix role.""" return "doc-appendix" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_backlink(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_backlink(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub backlink role.""" return "doc-backlink" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_biblioref(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_biblioref(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub biblioref role.""" return "doc-biblioref" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_bibliography(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_bibliography(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub bibliography role.""" return "doc-bibliography" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_chapter(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_chapter(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub chapter role.""" return "doc-chapter" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_colophon(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_colophon(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub colophon role.""" return "doc-colophon" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_conclusion(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_conclusion(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub conclusion role.""" return "doc-conclusion" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_cover(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_cover(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub cover role.""" return "doc-cover" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_credit(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_credit(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub credit role.""" return "doc-credit" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_credits(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_credits(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub credits role.""" return "doc-credits" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_dedication(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_dedication(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub dedication role.""" return "doc-dedication" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_endnote(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_endnote(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub endnote role.""" return "doc-endnote" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_endnotes(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_endnotes(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub endnotes role.""" return "doc-endnotes" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_epigraph(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_epigraph(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub epigraph role.""" return "doc-epigraph" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_epilogue(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_epilogue(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub epilogue role.""" return "doc-epilogue" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_errata(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_errata(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub errata role.""" return "doc-errata" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_example(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_example(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub example role.""" return "doc-example" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_footnote(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_footnote(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub footnote role.""" return "doc-footnote" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_foreword(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_foreword(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub foreword role.""" return "doc-foreword" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_glossary(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_glossary(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub glossary role.""" return "doc-glossary" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_glossref(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_glossref(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub glossref role.""" return "doc-glossref" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_index(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_index(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub index role.""" return "doc-index" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_introduction(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_introduction(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub introduction role.""" return "doc-introduction" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_noteref(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_noteref(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub noteref role.""" return "doc-noteref" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_pagelist(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_pagelist(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub pagelist role.""" return "doc-pagelist" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_pagebreak(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_pagebreak(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub pagebreak role.""" return "doc-pagebreak" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_part(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_part(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub part role.""" return "doc-part" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_preface(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_preface(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub preface role.""" return "doc-preface" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_prologue(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_prologue(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub prologue role.""" return "doc-prologue" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_pullquote(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_pullquote(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub pullquote role.""" return "doc-pullquote" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_qna(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_qna(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub qna role.""" return "doc-qna" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_subtitle(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_subtitle(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub subtitle role.""" return "doc-subtitle" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_dpub_toc(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_dpub_toc(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the DPub toc role.""" return "doc-toc" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_drawing_area(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_drawing_area(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the drawing area role""" if role is None: @@ -1079,7 +1080,7 @@ class AXUtilitiesRole: return role == Atspi.Role.DRAWING_AREA @staticmethod - def is_editable_combo_box(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_editable_combo_box(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is an editable combobox""" if role is None: @@ -1091,7 +1092,7 @@ class AXUtilitiesRole: return bool(AXObject.find_descendant(obj, AXUtilitiesRole.is_text_input)) @staticmethod - def is_editbar(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_editbar(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the editbar role""" if role is None: @@ -1099,7 +1100,7 @@ class AXUtilitiesRole: return role == Atspi.Role.EDITBAR @staticmethod - def is_embedded(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_embedded(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the embedded role""" if role is None: @@ -1107,7 +1108,7 @@ class AXUtilitiesRole: return role == Atspi.Role.EMBEDDED @staticmethod - def is_entry(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_entry(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the entry role""" if role is None: @@ -1115,7 +1116,7 @@ class AXUtilitiesRole: return role == Atspi.Role.ENTRY @staticmethod - def is_extended(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_extended(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the extended role""" if role is None: @@ -1123,13 +1124,13 @@ class AXUtilitiesRole: return role == Atspi.Role.EXTENDED @staticmethod - def is_feed(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_feed(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the feed role""" return "feed" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_feed_article(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_feed_article(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the article role and descends from a feed.""" if not AXUtilitiesRole.is_article(obj, role): @@ -1138,14 +1139,14 @@ class AXUtilitiesRole: return AXObject.find_ancestor(obj, AXUtilitiesRole.is_feed) is not None @staticmethod - def is_figure(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_figure(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the figure role or tag.""" return "figure" in AXUtilitiesRole._get_xml_roles(obj) \ or AXUtilitiesRole._get_tag(obj) == "figure" @staticmethod - def is_file_chooser(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_file_chooser(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the file chooser role""" if role is None: @@ -1153,7 +1154,7 @@ class AXUtilitiesRole: return role == Atspi.Role.FILE_CHOOSER @staticmethod - def is_filler(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_filler(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the filler role""" if role is None: @@ -1161,7 +1162,7 @@ class AXUtilitiesRole: return role == Atspi.Role.FILLER @staticmethod - def is_focus_traversable(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_focus_traversable(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the focus traversable role""" if role is None: @@ -1169,7 +1170,7 @@ class AXUtilitiesRole: return role == Atspi.Role.FOCUS_TRAVERSABLE @staticmethod - def is_font_chooser(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_font_chooser(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the font chooser role""" if role is None: @@ -1177,7 +1178,7 @@ class AXUtilitiesRole: return role == Atspi.Role.FONT_CHOOSER @staticmethod - def is_footer(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_footer(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the footer role""" if role is None: @@ -1185,7 +1186,7 @@ class AXUtilitiesRole: return role == Atspi.Role.FOOTER @staticmethod - def is_footnote(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_footnote(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the footnote role""" if role is None: @@ -1193,7 +1194,7 @@ class AXUtilitiesRole: return role == Atspi.Role.FOOTNOTE @staticmethod - def is_form(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_form(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the form role""" if role is None: @@ -1201,7 +1202,7 @@ class AXUtilitiesRole: return role == Atspi.Role.FORM @staticmethod - def is_frame(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_frame(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the frame role""" if role is None: @@ -1209,7 +1210,7 @@ class AXUtilitiesRole: return role == Atspi.Role.FRAME @staticmethod - def is_glass_pane(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_glass_pane(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the glass pane role""" if role is None: @@ -1217,7 +1218,7 @@ class AXUtilitiesRole: return role == Atspi.Role.GLASS_PANE @staticmethod - def is_gui_list(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_gui_list(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the list role but contains UI rather than static text.""" if not AXUtilitiesRole.is_list(obj, role): @@ -1226,7 +1227,7 @@ class AXUtilitiesRole: return AXObject.get_toolkit_name(obj) == "gtk" @staticmethod - def is_grid(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_grid(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the grid role.""" if not AXUtilitiesRole.is_table(obj, role): @@ -1235,7 +1236,7 @@ class AXUtilitiesRole: return "grid" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_grid_cell(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_grid_cell(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the gridcell role or the cell role and is in a grid.""" if not AXUtilitiesRole.is_table_cell(obj, role): @@ -1249,13 +1250,13 @@ class AXUtilitiesRole: return False @staticmethod - def is_group(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_group(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is an ARIA group.""" return "group" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_grouping(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_grouping(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the grouping role""" if role is None: @@ -1263,7 +1264,7 @@ class AXUtilitiesRole: return role == Atspi.Role.GROUPING @staticmethod - def is_header(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_header(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the header role""" if role is None: @@ -1271,7 +1272,7 @@ class AXUtilitiesRole: return role == Atspi.Role.HEADER @staticmethod - def is_heading(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_heading(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the heading role""" if role is None: @@ -1279,7 +1280,7 @@ class AXUtilitiesRole: return role == Atspi.Role.HEADING @staticmethod - def is_html_container(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_html_container(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the html container role""" if role is None: @@ -1287,28 +1288,28 @@ class AXUtilitiesRole: return role == Atspi.Role.HTML_CONTAINER @staticmethod - def is_horizontal_scrollbar(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_horizontal_scrollbar(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is a horizontal scrollbar""" return AXUtilitiesRole.is_scroll_bar(obj, role) \ and AXObject.has_state(obj, Atspi.StateType.HORIZONTAL) @staticmethod - def is_horizontal_separator(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_horizontal_separator(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is a horizontal separator""" return AXUtilitiesRole.is_separator(obj, role) \ and AXObject.has_state(obj, Atspi.StateType.HORIZONTAL) @staticmethod - def is_horizontal_slider(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_horizontal_slider(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is a horizontal slider""" return AXUtilitiesRole.is_slider(obj, role) \ and AXObject.has_state(obj, Atspi.StateType.HORIZONTAL) @staticmethod - def is_icon(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_icon(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the icon role""" if role is None: @@ -1316,7 +1317,7 @@ class AXUtilitiesRole: return role == Atspi.Role.ICON @staticmethod - def is_icon_or_canvas(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_icon_or_canvas(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the icon or canvas role""" if role is None: @@ -1324,7 +1325,7 @@ class AXUtilitiesRole: return role in [Atspi.Role.ICON, Atspi.Role.CANVAS] @staticmethod - def is_image(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_image(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the image role""" if role is None: @@ -1332,7 +1333,7 @@ class AXUtilitiesRole: return role == Atspi.Role.IMAGE @staticmethod - def is_image_or_canvas(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_image_or_canvas(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the image or canvas role""" if role is None: @@ -1340,7 +1341,7 @@ class AXUtilitiesRole: return role in [Atspi.Role.IMAGE, Atspi.Role.CANVAS] @staticmethod - def is_image_map(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_image_map(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the image map role""" if role is None: @@ -1348,7 +1349,7 @@ class AXUtilitiesRole: return role == Atspi.Role.IMAGE_MAP @staticmethod - def is_info_bar(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_info_bar(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the info bar role""" if role is None: @@ -1356,7 +1357,7 @@ class AXUtilitiesRole: return role == Atspi.Role.INFO_BAR @staticmethod - def is_inline_internal_frame(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_inline_internal_frame(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the internal frame role and is inline.""" if not AXUtilitiesRole.is_internal_frame(obj, role): @@ -1365,7 +1366,7 @@ class AXUtilitiesRole: return "inline" in AXUtilitiesRole._get_display_style(obj) @staticmethod - def is_inline_list_item(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_inline_list_item(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the list item role and is inline.""" if not AXUtilitiesRole.is_list_item(obj, role): @@ -1374,7 +1375,7 @@ class AXUtilitiesRole: return "inline" in AXUtilitiesRole._get_display_style(obj) @staticmethod - def is_inline_suggestion(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_inline_suggestion(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the suggestion role and is inline.""" if not AXUtilitiesRole.is_suggestion(obj, role): @@ -1383,7 +1384,7 @@ class AXUtilitiesRole: return "inline" in AXUtilitiesRole._get_display_style(obj) @staticmethod - def is_input_method_window(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_input_method_window(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the input method window role""" if role is None: @@ -1391,7 +1392,7 @@ class AXUtilitiesRole: return role == Atspi.Role.INPUT_METHOD_WINDOW @staticmethod - def is_internal_frame(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_internal_frame(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the internal frame role""" if role is None: @@ -1399,7 +1400,7 @@ class AXUtilitiesRole: return role == Atspi.Role.INTERNAL_FRAME or AXUtilitiesRole._get_tag(obj) == "iframe" @staticmethod - def is_invalid_role(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_invalid_role(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the invalid role""" if role is None: @@ -1407,7 +1408,7 @@ class AXUtilitiesRole: return role == Atspi.Role.INVALID @staticmethod - def is_label(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_label(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the label role""" if role is None: @@ -1415,7 +1416,7 @@ class AXUtilitiesRole: return role == Atspi.Role.LABEL @staticmethod - def is_label_or_caption(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_label_or_caption(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the label or caption role""" if role is None: @@ -1423,7 +1424,7 @@ class AXUtilitiesRole: return role in [Atspi.Role.LABEL, Atspi.Role.CAPTION] @staticmethod - def is_landmark(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_landmark(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the landmark role""" if role is None: @@ -1431,57 +1432,57 @@ class AXUtilitiesRole: return role == Atspi.Role.LANDMARK @staticmethod - def is_landmark_banner(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_landmark_banner(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the banner landmark role""" return "banner" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod def is_landmark_complementary( - obj: Atspi.Accessible, _role: Atspi.Role | None = None + obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None ) -> bool: """Returns True if obj has the complementary landmark role""" return "complementary" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_landmark_contentinfo(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_landmark_contentinfo(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the contentinfo landmark role""" return "contentinfo" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_landmark_form(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_landmark_form(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the form landmark role""" return "form" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_landmark_main(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_landmark_main(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the main landmark role""" return "main" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_landmark_navigation(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_landmark_navigation(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the navigation landmark role""" return "navigation" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_landmark_region(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_landmark_region(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the region landmark role""" return "region" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_landmark_search(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_landmark_search(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the search landmark role""" return "search" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_landmark_without_type(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_landmark_without_type(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the landmark role but no type""" if not AXUtilitiesRole.is_landmark(obj, role): @@ -1491,7 +1492,7 @@ class AXUtilitiesRole: return not roles @staticmethod - def is_large_container(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_large_container(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has a large container role""" if role is None: @@ -1500,7 +1501,7 @@ class AXUtilitiesRole: return role in AXUtilitiesRole.get_large_container_roles() @staticmethod - def is_layered_pane(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_layered_pane(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the layered pane role""" if role is None: @@ -1508,7 +1509,7 @@ class AXUtilitiesRole: return role == Atspi.Role.LAYERED_PANE @staticmethod - def is_level_bar(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_level_bar(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the level bar role""" if role is None: @@ -1516,7 +1517,7 @@ class AXUtilitiesRole: return role == Atspi.Role.LEVEL_BAR @staticmethod - def is_link(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_link(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the link role""" if role is None: @@ -1524,7 +1525,7 @@ class AXUtilitiesRole: return role == Atspi.Role.LINK @staticmethod - def is_list(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_list(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the list role""" if role is None: @@ -1532,7 +1533,7 @@ class AXUtilitiesRole: return role == Atspi.Role.LIST @staticmethod - def is_list_box(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_list_box(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the list box role""" if role is None: @@ -1540,7 +1541,7 @@ class AXUtilitiesRole: return role == Atspi.Role.LIST_BOX @staticmethod - def is_list_box_item(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_list_box_item(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is an item in a list box""" if not AXUtilitiesRole.is_list_item(obj, role): @@ -1548,7 +1549,7 @@ class AXUtilitiesRole: return AXObject.find_ancestor(obj, AXUtilitiesRole.is_list_box) is not None @staticmethod - def is_list_item(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_list_item(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the list item role""" if role is None: @@ -1556,7 +1557,7 @@ class AXUtilitiesRole: return role == Atspi.Role.LIST_ITEM @staticmethod - def is_log(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_log(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the log role""" if role is None: @@ -1564,14 +1565,14 @@ class AXUtilitiesRole: return role == Atspi.Role.LOG @staticmethod - def is_live_region(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_live_region(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is a live region.""" attrs = AXObject.get_attributes_dict(obj) return "container-live" in attrs and attrs.get("container-live") in ["polite", "assertive"] @staticmethod - def is_mark(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_mark(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the mark role""" if role is None: @@ -1581,7 +1582,7 @@ class AXUtilitiesRole: or "mark" == AXUtilitiesRole._get_tag(obj) @staticmethod - def is_marquee(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_marquee(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the marquee role""" if role is None: @@ -1589,7 +1590,7 @@ class AXUtilitiesRole: return role == Atspi.Role.MARQUEE @staticmethod - def is_math(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_math(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the math role""" if role is None: @@ -1609,7 +1610,7 @@ class AXUtilitiesRole: return AXUtilitiesRole._get_tag(obj) == "mfenced" @staticmethod - def is_math_fraction(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_math_fraction(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the math fraction role""" if role is None: @@ -1618,7 +1619,7 @@ class AXUtilitiesRole: @staticmethod def is_math_fraction_without_bar( - obj: Atspi.Accessible, role: Atspi.Role | None = None + obj: Atspi.Accessible, role: Optional[Atspi.Role] = None ) -> bool: """Returns True if obj has the math fraction role and lacks the fraction bar""" @@ -1649,7 +1650,7 @@ class AXUtilitiesRole: return AXUtilitiesRole._get_tag(obj) == "mmultiscripts" @staticmethod - def is_math_related(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_math_related(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has a math-related role""" if role is None: @@ -1698,7 +1699,7 @@ class AXUtilitiesRole: "munderover"] @staticmethod - def is_math_root(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_math_root(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the math root role""" if role is None: @@ -1742,7 +1743,7 @@ class AXUtilitiesRole: return AXUtilitiesRole._get_tag(obj) in ["mover", "munder", "munderover"] @staticmethod - def is_menu(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_menu(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the menu role""" if role is None: @@ -1750,7 +1751,7 @@ class AXUtilitiesRole: return role == Atspi.Role.MENU @staticmethod - def is_menu_bar(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_menu_bar(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the menubar role""" if role is None: @@ -1758,7 +1759,7 @@ class AXUtilitiesRole: return role == Atspi.Role.MENU_BAR @staticmethod - def is_menu_item(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_menu_item(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the menu item role""" if role is None: @@ -1766,7 +1767,7 @@ class AXUtilitiesRole: return role == Atspi.Role.MENU_ITEM @staticmethod - def is_menu_item_of_any_kind(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_menu_item_of_any_kind(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has any menu item role""" roles = AXUtilitiesRole.get_menu_item_roles() @@ -1775,7 +1776,7 @@ class AXUtilitiesRole: return role in roles @staticmethod - def is_menu_related(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_menu_related(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has any menu-related role""" roles = AXUtilitiesRole.get_menu_related_roles() @@ -1784,21 +1785,21 @@ class AXUtilitiesRole: return role in roles @staticmethod - def is_modal_dialog(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_modal_dialog(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the alert or dialog role and modal state""" return AXUtilitiesRole.is_dialog_or_alert(obj, role) \ and AXObject.has_state(obj, Atspi.StateType.MODAL) @staticmethod - def is_multi_line_entry(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_multi_line_entry(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the entry role and multiline state""" return AXUtilitiesRole.is_entry(obj, role) \ and AXObject.has_state(obj, Atspi.StateType.MULTI_LINE) @staticmethod - def is_notification(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_notification(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the notification role""" if role is None: @@ -1806,7 +1807,7 @@ class AXUtilitiesRole: return role == Atspi.Role.NOTIFICATION @staticmethod - def is_option_pane(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_option_pane(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the option pane role""" if role is None: @@ -1814,7 +1815,7 @@ class AXUtilitiesRole: return role == Atspi.Role.OPTION_PANE @staticmethod - def is_page(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_page(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the page role""" if role is None: @@ -1822,7 +1823,7 @@ class AXUtilitiesRole: return role == Atspi.Role.PAGE @staticmethod - def is_page_tab(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_page_tab(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the page tab role""" if role is None: @@ -1830,7 +1831,7 @@ class AXUtilitiesRole: return role == Atspi.Role.PAGE_TAB @staticmethod - def is_page_tab_list(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_page_tab_list(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the page tab list role""" if role is None: @@ -1838,7 +1839,7 @@ class AXUtilitiesRole: return role == Atspi.Role.PAGE_TAB_LIST @staticmethod - def is_page_tab_list_related(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_page_tab_list_related(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the page tab or page tab list role""" roles = [Atspi.Role.PAGE_TAB_LIST, Atspi.Role.PAGE_TAB] @@ -1847,7 +1848,7 @@ class AXUtilitiesRole: return role in roles @staticmethod - def is_panel(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_panel(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the panel role""" if role is None: @@ -1855,7 +1856,7 @@ class AXUtilitiesRole: return role == Atspi.Role.PANEL @staticmethod - def is_paragraph(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_paragraph(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the paragraph role""" if role is None: @@ -1863,7 +1864,7 @@ class AXUtilitiesRole: return role == Atspi.Role.PARAGRAPH @staticmethod - def is_password_text(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_password_text(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the password text role""" if role is None: @@ -1871,7 +1872,7 @@ class AXUtilitiesRole: return role == Atspi.Role.PASSWORD_TEXT @staticmethod - def is_popup_menu(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_popup_menu(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the popup menu role""" if role is None: @@ -1879,7 +1880,7 @@ class AXUtilitiesRole: return role == Atspi.Role.POPUP_MENU @staticmethod - def is_progress_bar(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_progress_bar(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the progress bar role""" if role is None: @@ -1887,7 +1888,7 @@ class AXUtilitiesRole: return role == Atspi.Role.PROGRESS_BAR @staticmethod - def is_push_button(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_push_button(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the push button role""" if role is None: @@ -1895,7 +1896,7 @@ class AXUtilitiesRole: return role == Atspi.Role.BUTTON @staticmethod - def is_push_button_menu(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_push_button_menu(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the push button menu role""" if role is None: @@ -1903,7 +1904,7 @@ class AXUtilitiesRole: return role == Atspi.Role.PUSH_BUTTON_MENU @staticmethod - def is_radio_button(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_radio_button(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the radio button role""" if role is None: @@ -1911,7 +1912,7 @@ class AXUtilitiesRole: return role == Atspi.Role.RADIO_BUTTON @staticmethod - def is_radio_menu_item(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_radio_menu_item(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the radio menu item role""" if role is None: @@ -1919,7 +1920,7 @@ class AXUtilitiesRole: return role == Atspi.Role.RADIO_MENU_ITEM @staticmethod - def is_rating(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_rating(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the rating role""" if role is None: @@ -1927,7 +1928,7 @@ class AXUtilitiesRole: return role == Atspi.Role.RATING @staticmethod - def is_redundant_object_role(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_redundant_object_role(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the redundant object role""" if role is None: @@ -1935,7 +1936,7 @@ class AXUtilitiesRole: return role == Atspi.Role.REDUNDANT_OBJECT @staticmethod - def is_root_pane(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_root_pane(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the root pane role""" if role is None: @@ -1943,7 +1944,7 @@ class AXUtilitiesRole: return role == Atspi.Role.ROOT_PANE @staticmethod - def is_row_header(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_row_header(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the row header role""" if role is None: @@ -1951,7 +1952,7 @@ class AXUtilitiesRole: return role == Atspi.Role.ROW_HEADER @staticmethod - def is_ruler(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_ruler(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the ruler role""" if role is None: @@ -1959,7 +1960,7 @@ class AXUtilitiesRole: return role == Atspi.Role.RULER @staticmethod - def is_scroll_bar(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_scroll_bar(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the scrollbar role""" if role is None: @@ -1967,7 +1968,7 @@ class AXUtilitiesRole: return role == Atspi.Role.SCROLL_BAR @staticmethod - def is_scroll_pane(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_scroll_pane(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the scroll pane role""" if role is None: @@ -1975,7 +1976,7 @@ class AXUtilitiesRole: return role == Atspi.Role.SCROLL_PANE @staticmethod - def is_section(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_section(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the section role""" if role is None: @@ -1983,7 +1984,7 @@ class AXUtilitiesRole: return role == Atspi.Role.SECTION @staticmethod - def is_separator(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_separator(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the separator role""" if role is None: @@ -1992,7 +1993,7 @@ class AXUtilitiesRole: @staticmethod def is_single_line_autocomplete_entry( - obj: Atspi.Accessible, role: Atspi.Role | None = None + obj: Atspi.Accessible, role: Optional[Atspi.Role] = None ) -> bool: """Returns True if obj has the entry role and single-line state""" @@ -2002,7 +2003,7 @@ class AXUtilitiesRole: return AXUtilitiesState.supports_autocompletion(obj) @staticmethod - def is_single_line_entry(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_single_line_entry(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the entry role and the single-line state""" if not AXUtilitiesState.is_single_line(obj): @@ -2014,7 +2015,7 @@ class AXUtilitiesRole: return False @staticmethod - def is_slider(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_slider(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the slider role""" if role is None: @@ -2022,7 +2023,7 @@ class AXUtilitiesRole: return role == Atspi.Role.SLIDER @staticmethod - def is_spin_button(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_spin_button(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the spin button role""" if role is None: @@ -2030,7 +2031,7 @@ class AXUtilitiesRole: return role == Atspi.Role.SPIN_BUTTON @staticmethod - def is_split_pane(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_split_pane(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the split pane role""" if role is None: @@ -2038,7 +2039,7 @@ class AXUtilitiesRole: return role == Atspi.Role.SPLIT_PANE @staticmethod - def is_static(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_static(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the static role""" if role is None: @@ -2046,7 +2047,7 @@ class AXUtilitiesRole: return role == Atspi.Role.STATIC @staticmethod - def is_status_bar(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_status_bar(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the statusbar role""" if role is None: @@ -2054,7 +2055,7 @@ class AXUtilitiesRole: return role == Atspi.Role.STATUS_BAR @staticmethod - def is_subscript(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_subscript(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the subscript role""" if role is None: @@ -2063,7 +2064,7 @@ class AXUtilitiesRole: @staticmethod def is_subscript_or_superscript( - obj: Atspi.Accessible, role: Atspi.Role | None = None + obj: Atspi.Accessible, role: Optional[Atspi.Role] = None ) -> bool: """Returns True if obj has the subscript or superscript role""" @@ -2073,7 +2074,7 @@ class AXUtilitiesRole: @staticmethod def is_subscript_or_superscript_text( - obj: Atspi.Accessible, role: Atspi.Role | None = None + obj: Atspi.Accessible, role: Optional[Atspi.Role] = None ) -> bool: """Returns True if obj has the subscript or superscript role and is not math-related""" @@ -2082,7 +2083,7 @@ class AXUtilitiesRole: return AXUtilitiesRole.is_subscript_or_superscript(obj, role) @staticmethod - def is_suggestion(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_suggestion(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the suggestion role""" if role is None: @@ -2091,7 +2092,7 @@ class AXUtilitiesRole: or "suggestion" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_superscript(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_superscript(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the superscript role""" if role is None: @@ -2105,7 +2106,7 @@ class AXUtilitiesRole: return AXUtilitiesRole._get_tag(obj) == "svg" @staticmethod - def is_switch(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_switch(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the switch role.""" if role is None: @@ -2117,7 +2118,7 @@ class AXUtilitiesRole: return "switch" in AXUtilitiesRole._get_xml_roles(obj) @staticmethod - def is_table(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_table(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the table role""" if role is None: @@ -2125,7 +2126,7 @@ class AXUtilitiesRole: return role == Atspi.Role.TABLE @staticmethod - def is_table_cell(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_table_cell(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the table cell role""" if role is None: @@ -2133,7 +2134,7 @@ class AXUtilitiesRole: return role == Atspi.Role.TABLE_CELL @staticmethod - def is_table_cell_or_header(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_table_cell_or_header(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the table cell or a header-related role""" roles = AXUtilitiesRole.get_table_cell_roles() @@ -2142,7 +2143,7 @@ class AXUtilitiesRole: return role in roles @staticmethod - def is_table_column_header(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_table_column_header(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the table column header role""" if role is None: @@ -2150,7 +2151,7 @@ class AXUtilitiesRole: return role == Atspi.Role.TABLE_COLUMN_HEADER @staticmethod - def is_table_header(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_table_header(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has a table header related role""" roles = AXUtilitiesRole.get_table_header_roles() @@ -2161,7 +2162,7 @@ class AXUtilitiesRole: @staticmethod def is_table_related( obj: Atspi.Accessible, - role: Atspi.Role | None = None, + role: Optional[Atspi.Role] = None, include_caption: bool = False ) -> bool: """Returns True if obj has a table-related role""" @@ -2172,7 +2173,7 @@ class AXUtilitiesRole: return role in roles @staticmethod - def is_table_row(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_table_row(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the table row role""" if role is None: @@ -2180,7 +2181,7 @@ class AXUtilitiesRole: return role == Atspi.Role.TABLE_ROW @staticmethod - def is_table_row_header(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_table_row_header(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the table row header role""" if role is None: @@ -2188,7 +2189,7 @@ class AXUtilitiesRole: return role == Atspi.Role.TABLE_ROW_HEADER @staticmethod - def is_tearoff_menu_item(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_tearoff_menu_item(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the tearoff menu item role""" if role is None: @@ -2196,14 +2197,14 @@ class AXUtilitiesRole: return role == Atspi.Role.TEAROFF_MENU_ITEM @staticmethod - def is_terminal(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_terminal(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the terminal role""" if role is None: role = AXObject.get_role(obj) return role == Atspi.Role.TERMINAL @staticmethod - def is_text(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_text(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the text role""" if role is None: @@ -2211,7 +2212,7 @@ class AXUtilitiesRole: return role == Atspi.Role.TEXT @staticmethod - def is_text_input(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_text_input(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has any role associated with textual input""" roles = [Atspi.Role.ENTRY, Atspi.Role.PASSWORD_TEXT, Atspi.Role.SPIN_BUTTON] @@ -2226,7 +2227,7 @@ class AXUtilitiesRole: return False @staticmethod - def is_text_input_date(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_text_input_date(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is a date text input""" if not AXUtilitiesRole.is_text_input(obj, role): @@ -2236,7 +2237,7 @@ class AXUtilitiesRole: return attrs.get("text-input-type") == "date" @staticmethod - def is_text_input_email(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_text_input_email(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is an email text input""" if not AXUtilitiesRole.is_text_input(obj, role): @@ -2246,7 +2247,7 @@ class AXUtilitiesRole: return attrs.get("text-input-type") == "email" @staticmethod - def is_text_input_number(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_text_input_number(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is a numeric text input""" if not AXUtilitiesRole.is_text_input(obj, role): @@ -2256,7 +2257,7 @@ class AXUtilitiesRole: return attrs.get("text-input-type") == "number" @staticmethod - def is_text_input_search(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_text_input_search(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is a telephone text input""" if not AXUtilitiesRole.is_text_input(obj, role): @@ -2284,7 +2285,7 @@ class AXUtilitiesRole: return False @staticmethod - def is_text_input_telephone(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_text_input_telephone(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is a telephone text input""" if not AXUtilitiesRole.is_text_input(obj, role): @@ -2294,7 +2295,7 @@ class AXUtilitiesRole: return attrs.get("text-input-type") == "telephone" @staticmethod - def is_text_input_time(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_text_input_time(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is a time text input""" if not AXUtilitiesRole.is_text_input(obj, role): @@ -2304,7 +2305,7 @@ class AXUtilitiesRole: return attrs.get("text-input-type") == "time" @staticmethod - def is_text_input_url(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_text_input_url(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is a url text input""" if not AXUtilitiesRole.is_text_input(obj, role): @@ -2314,7 +2315,7 @@ class AXUtilitiesRole: return attrs.get("text-input-type") == "url" @staticmethod - def is_text_input_week(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_text_input_week(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is a week text input""" if not AXUtilitiesRole.is_text_input(obj, role): @@ -2324,14 +2325,14 @@ class AXUtilitiesRole: return attrs.get("text-input-type") == "week" @staticmethod - def is_time(obj: Atspi.Accessible, _role: Atspi.Role | None = None) -> bool: + def is_time(obj: Atspi.Accessible, _role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the time role""" return "time" in AXUtilitiesRole._get_xml_roles(obj) \ or "time" == AXUtilitiesRole._get_tag(obj) @staticmethod - def is_timer(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_timer(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the timer role""" if role is None: @@ -2339,7 +2340,7 @@ class AXUtilitiesRole: return role == Atspi.Role.TIMER @staticmethod - def is_title_bar(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_title_bar(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the titlebar role""" if role is None: @@ -2347,7 +2348,7 @@ class AXUtilitiesRole: return role == Atspi.Role.TITLE_BAR @staticmethod - def is_toggle_button(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_toggle_button(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the toggle button role""" if role is None: @@ -2355,7 +2356,7 @@ class AXUtilitiesRole: return role == Atspi.Role.TOGGLE_BUTTON @staticmethod - def is_tool_bar(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_tool_bar(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the toolbar role""" if role is None: @@ -2363,7 +2364,7 @@ class AXUtilitiesRole: return role == Atspi.Role.TOOL_BAR @staticmethod - def is_tool_tip(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_tool_tip(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the tooltip role""" if role is None: @@ -2371,7 +2372,7 @@ class AXUtilitiesRole: return role == Atspi.Role.TOOL_TIP @staticmethod - def is_tree(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_tree(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the tree role""" if role is None: @@ -2379,7 +2380,7 @@ class AXUtilitiesRole: return role == Atspi.Role.TREE @staticmethod - def is_tree_or_tree_table(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_tree_or_tree_table(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the tree or tree table role""" if role is None: @@ -2387,7 +2388,7 @@ class AXUtilitiesRole: return role in [Atspi.Role.TREE, Atspi.Role.TREE_TABLE] @staticmethod - def is_tree_related(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_tree_related(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has a tree-related role""" roles = [Atspi.Role.TREE, @@ -2398,7 +2399,7 @@ class AXUtilitiesRole: return role in roles @staticmethod - def is_tree_item(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_tree_item(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the tree item role""" if role is None: @@ -2406,7 +2407,7 @@ class AXUtilitiesRole: return role == Atspi.Role.TREE_ITEM @staticmethod - def is_tree_table(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_tree_table(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the tree table role""" if role is None: @@ -2414,7 +2415,7 @@ class AXUtilitiesRole: return role == Atspi.Role.TREE_TABLE @staticmethod - def is_unknown(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_unknown(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the unknown role""" if role is None: @@ -2422,7 +2423,7 @@ class AXUtilitiesRole: return role == Atspi.Role.UNKNOWN @staticmethod - def is_unknown_or_redundant(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_unknown_or_redundant(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the unknown or redundant object role""" if role is None: @@ -2430,28 +2431,28 @@ class AXUtilitiesRole: return role in [Atspi.Role.UNKNOWN, Atspi.Role.REDUNDANT_OBJECT] @staticmethod - def is_vertical_scrollbar(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_vertical_scrollbar(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is a vertical scrollbar""" return AXUtilitiesRole.is_scroll_bar(obj, role) \ and AXObject.has_state(obj, Atspi.StateType.VERTICAL) @staticmethod - def is_vertical_separator(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_vertical_separator(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is a vertical separator""" return AXUtilitiesRole.is_separator(obj, role) \ and AXObject.has_state(obj, Atspi.StateType.VERTICAL) @staticmethod - def is_vertical_slider(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_vertical_slider(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj is a vertical slider""" return AXUtilitiesRole.is_slider(obj, role) \ and AXObject.has_state(obj, Atspi.StateType.VERTICAL) @staticmethod - def is_video(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_video(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the video role""" if role is None: @@ -2459,7 +2460,7 @@ class AXUtilitiesRole: return role == Atspi.Role.VIDEO @staticmethod - def is_viewport(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_viewport(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the viewport role""" if role is None: @@ -2486,7 +2487,7 @@ class AXUtilitiesRole: return tag is not None and "-" in tag @staticmethod - def is_widget(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_widget(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has a widget role""" if role is None: @@ -2495,7 +2496,7 @@ class AXUtilitiesRole: @staticmethod def is_widget_controlled_by_line_navigation( - obj: Atspi.Accessible, role: Atspi.Role | None = None + obj: Atspi.Accessible, role: Optional[Atspi.Role] = None ) -> bool: """Returns True if obj is a widget controlled by line navigation""" @@ -2523,7 +2524,7 @@ class AXUtilitiesRole: Atspi.Role.SPLIT_PANE] @staticmethod - def is_window(obj: Atspi.Accessible, role: Atspi.Role | None = None) -> bool: + def is_window(obj: Atspi.Accessible, role: Optional[Atspi.Role] = None) -> bool: """Returns True if obj has the window role""" if role is None: diff --git a/src/cthulhu/ax_value.py b/src/cthulhu/ax_value.py index fdce81d..0308f73 100644 --- a/src/cthulhu/ax_value.py +++ b/src/cthulhu/ax_value.py @@ -34,6 +34,7 @@ __copyright__ = "Copyright (c) 2024 Igalia, S.L." \ "Copyright (c) 2024 GNOME Foundation Inc." __license__ = "LGPL" +from typing import Optional import threading import time @@ -149,7 +150,7 @@ class AXValue: return f"{current:.{decimal_places}f}" @staticmethod - def get_value_as_percent(obj: Atspi.Accessible) -> int | None: + def get_value_as_percent(obj: Atspi.Accessible) -> Optional[int]: """Returns the current value as a percent, or None if that is not applicable.""" if not AXObject.supports_value(obj): diff --git a/src/cthulhu/cthulhu-setup.ui b/src/cthulhu/cthulhu-setup.ui index 337935f..04dc7fa 100644 --- a/src/cthulhu/cthulhu-setup.ui +++ b/src/cthulhu/cthulhu-setup.ui @@ -3571,9 +3571,8 @@ False True - Claude Code (Enhanced) - Claude (Anthropic) - ChatGPT (OpenAI) + Claude Code (CLI) + Codex (CLI) Gemini (Google) Ollama (Local - Free) diff --git a/src/cthulhu/cthulhu_gui_prefs.py b/src/cthulhu/cthulhu_gui_prefs.py index be79be9..9d86efb 100644 --- a/src/cthulhu/cthulhu_gui_prefs.py +++ b/src/cthulhu/cthulhu_gui_prefs.py @@ -57,6 +57,7 @@ from . import cthulhu_state from . import settings from . import settings_manager from . import input_event +from . import input_event_manager from . import keybindings from . import pronunciation_dict from . import braille @@ -64,6 +65,7 @@ from . import speech from . import speechserver from . import text_attribute_names from . import sound_theme_manager +from . import script_manager from .ax_object import AXObject _settingsManager = settings_manager.getManager() @@ -1874,10 +1876,12 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): # Set provider combo provider = prefs.get("aiProvider", settings.aiProvider) providerIndex = 0 # Default to Claude Code - if provider == settings.AI_PROVIDER_GEMINI: + if provider == settings.AI_PROVIDER_CODEX: providerIndex = 1 - elif provider == settings.AI_PROVIDER_OLLAMA: + elif provider == settings.AI_PROVIDER_GEMINI: providerIndex = 2 + elif provider == settings.AI_PROVIDER_OLLAMA: + providerIndex = 3 self.aiProviderCombo.set_active(providerIndex) # Set API key file @@ -1947,6 +1951,8 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): # Update labels based on provider if provider == settings.AI_PROVIDER_CLAUDE_CODE: self.aiApiKeyEntry.set_placeholder_text("No API key needed - uses Claude Code CLI") + elif provider == settings.AI_PROVIDER_CODEX: + self.aiApiKeyEntry.set_placeholder_text("No API key needed - uses Codex CLI") elif provider == settings.AI_PROVIDER_OLLAMA: self.aiApiKeyEntry.set_placeholder_text("No API key needed - uses local Ollama") else: @@ -3065,6 +3071,14 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): self._presentMessage(messages.KB_ENTER_NEW_KEY) cthulhu_state.capturingKeys = True + try: + script_manager.get_manager().get_active_script().removeKeyGrabs() + except Exception: + pass + try: + input_event_manager.get_manager().unmap_all_modifiers() + except Exception: + pass editable.connect('key-press-event', self.kbKeyPressed) return @@ -3073,6 +3087,10 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): cthulhu_state.capturingKeys = False self._capturedKey = [] + try: + script_manager.get_manager().get_active_script().refreshKeyGrabs() + except Exception: + pass return def _processKeyCaptured(self, keyPressedEvent): @@ -3090,9 +3108,17 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): entries = entries_for_keycode[-1] eventString = Gdk.keyval_name(entries[0]) eventState = keyPressedEvent.state + eventKeyvalName = Gdk.keyval_name(keyPressedEvent.keyval) cthulhuMods = settings.cthulhuModifierKeys - if eventString in cthulhuMods: + if eventKeyvalName in cthulhuMods: + eventString = eventKeyvalName + self._capturedKey = ['', keybindings.CTHULHU_MODIFIER_MASK, 0] + return False + if eventKeyvalName == "KP_0" \ + and "KP_Insert" in cthulhuMods \ + and eventState & Gdk.ModifierType.SHIFT_MASK: + eventString = "KP_Insert" self._capturedKey = ['', keybindings.CTHULHU_MODIFIER_MASK, 0] return False @@ -3180,6 +3206,10 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): cthulhu_state.capturingKeys = False self._capturedKey = [] + try: + script_manager.get_manager().get_active_script().refreshKeyGrabs() + except Exception: + pass myiter = treeModel.get_iter_from_string(path) try: originalBinding = treeModel.get_value(myiter, text) @@ -3793,7 +3823,12 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): def aiProviderChanged(self, widget): """AI Provider combo box changed handler""" - providers = [settings.AI_PROVIDER_CLAUDE_CODE, settings.AI_PROVIDER_GEMINI, settings.AI_PROVIDER_OLLAMA] + providers = [ + settings.AI_PROVIDER_CLAUDE_CODE, + settings.AI_PROVIDER_CODEX, + settings.AI_PROVIDER_GEMINI, + settings.AI_PROVIDER_OLLAMA + ] activeIndex = widget.get_active() if 0 <= activeIndex < len(providers): provider = providers[activeIndex] @@ -3970,4 +4005,3 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): """OCR copy to clipboard checkbox toggled handler""" self.prefsDict["ocrCopyToClipboard"] = widget.get_active() - diff --git a/src/cthulhu/dbus_service.py b/src/cthulhu/dbus_service.py index daf51b3..f77aad5 100644 --- a/src/cthulhu/dbus_service.py +++ b/src/cthulhu/dbus_service.py @@ -29,7 +29,7 @@ __license__ = "LGPL" import enum import inspect -from typing import Callable +from typing import Callable, Optional try: from dasbus.connection import SessionMessageBus @@ -156,7 +156,7 @@ class _HandlerInfo: description: str, action: Callable[..., bool], handler_type: 'HandlerType' = HandlerType.COMMAND, - parameters: list[tuple[str, str]] | None = None + parameters: Optional[list[tuple[str, str]]] = None ): self.python_function_name: str = python_function_name self.description: str = description @@ -548,10 +548,10 @@ class CthulhuRemoteController: OBJECT_PATH = "/org/stormux/Cthulhu/Service" def __init__(self) -> None: - self._dbus_service_interface: CthulhuDBusServiceInterface | None = None + self._dbus_service_interface: Optional[CthulhuDBusServiceInterface] = None self._is_running: bool = False - self._bus: SessionMessageBus | None = None - self._event_loop: EventLoop | None = None + self._bus: Optional[SessionMessageBus] = None + self._event_loop: Optional[EventLoop] = None self._pending_registrations: dict[str, object] = {} self._total_commands: int = 0 self._total_getters: int = 0 diff --git a/src/cthulhu/focus_manager.py b/src/cthulhu/focus_manager.py index 600eaa1..35a0d60 100644 --- a/src/cthulhu/focus_manager.py +++ b/src/cthulhu/focus_manager.py @@ -40,7 +40,7 @@ __copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc." \ "Copyright (c) 2016-2023 Igalia, S.L." __license__ = "LGPL" -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional import gi gi.require_version("Atspi", "2.0") @@ -75,13 +75,13 @@ class FocusManager: """Manages the focused object, window, etc.""" def __init__(self) -> None: - self._window: Atspi.Accessible | None = cthulhu_state.activeWindow - self._focus: Atspi.Accessible | None = cthulhu_state.locusOfFocus - self._object_of_interest: Atspi.Accessible | None = cthulhu_state.objOfInterest - self._active_mode: str | None = cthulhu_state.activeMode + self._window: Optional[Atspi.Accessible] = cthulhu_state.activeWindow + self._focus: Optional[Atspi.Accessible] = cthulhu_state.locusOfFocus + self._object_of_interest: Optional[Atspi.Accessible] = cthulhu_state.objOfInterest + self._active_mode: Optional[str] = cthulhu_state.activeMode self._last_cell_coordinates: tuple[int, int] = (-1, -1) - self._last_cursor_position: tuple[Atspi.Accessible | None, int] = (None, -1) - self._penultimate_cursor_position: tuple[Atspi.Accessible | None, int] = (None, -1) + self._last_cursor_position: tuple[Optional[Atspi.Accessible], int] = (None, -1) + self._penultimate_cursor_position: tuple[Optional[Atspi.Accessible], int] = (None, -1) msg = "FOCUS MANAGER: Registering D-Bus commands." debug.print_message(debug.LEVEL_INFO, msg, True) @@ -104,7 +104,7 @@ class FocusManager: cthulhu_state.objOfInterest = None cthulhu_state.activeMode = None - def find_focused_object(self) -> Atspi.Accessible | None: + def find_focused_object(self) -> Optional[Atspi.Accessible]: """Returns the focused object in the active window.""" result = _get_ax_utilities().get_focused_object(self._window) @@ -147,9 +147,9 @@ class FocusManager: def emit_region_changed( self, obj: Atspi.Accessible, - start_offset: int | None = None, - end_offset: int | None = None, - mode: str | None = None + start_offset: Optional[int] = None, + end_offset: Optional[int] = None, + mode: Optional[str] = None ) -> None: """Notifies interested clients that the current region of interest has changed.""" @@ -192,7 +192,7 @@ class FocusManager: def get_active_mode_and_object_of_interest( self - ) -> tuple[str | None, Atspi.Accessible | None]: + ) -> tuple[Optional[str], Optional[Atspi.Accessible]]: """Returns the current mode and associated object of interest""" tokens = ["FOCUS MANAGER: Active mode:", self._active_mode, @@ -200,7 +200,7 @@ class FocusManager: debug.print_tokens(debug.LEVEL_INFO, tokens, True) return self._active_mode, self._object_of_interest - def get_penultimate_cursor_position(self) -> tuple[Atspi.Accessible | None, int]: + def get_penultimate_cursor_position(self) -> tuple[Optional[Atspi.Accessible], int]: """Returns the penultimate cursor position as a tuple of (object, offset).""" obj, offset = self._penultimate_cursor_position @@ -208,7 +208,7 @@ class FocusManager: debug.print_tokens(debug.LEVEL_INFO, tokens, True) return obj, offset - def get_last_cursor_position(self) -> tuple[Atspi.Accessible | None, int]: + def get_last_cursor_position(self) -> tuple[Optional[Atspi.Accessible], int]: """Returns the last cursor position as a tuple of (object, offset).""" obj, offset = self._last_cursor_position @@ -216,7 +216,7 @@ class FocusManager: debug.print_tokens(debug.LEVEL_INFO, tokens, True) return obj, offset - def set_last_cursor_position(self, obj: Atspi.Accessible | None, offset: int) -> None: + def set_last_cursor_position(self, obj: Optional[Atspi.Accessible], offset: int) -> None: """Sets the last cursor position as a tuple of (object, offset).""" tokens = ["FOCUS MANAGER: Setting last cursor position to", obj, offset] @@ -239,7 +239,7 @@ class FocusManager: debug.print_message(debug.LEVEL_INFO, msg, True) self._last_cell_coordinates = row, column - def get_locus_of_focus(self) -> Atspi.Accessible | None: + def get_locus_of_focus(self) -> Optional[Atspi.Accessible]: """Returns the current locus of focus (i.e. the object with visual focus).""" tokens = ["FOCUS MANAGER: Locus of focus is", self._focus] @@ -248,8 +248,8 @@ class FocusManager: def set_locus_of_focus( self, - event: Atspi.Event | None, - obj: Atspi.Accessible | None, + event: Optional[Atspi.Event], + obj: Optional[Atspi.Accessible], notify_script: bool = True, force: bool = False ) -> None: @@ -340,7 +340,7 @@ class FocusManager: debug.print_tokens(debug.LEVEL_INFO, tokens, True) return is_active - def get_active_window(self) -> Atspi.Accessible | None: + def get_active_window(self) -> Optional[Atspi.Accessible]: """Returns the currently-active window (i.e. without searching or verifying).""" tokens = ["FOCUS MANAGER: Active window is", self._window] @@ -349,8 +349,8 @@ class FocusManager: def set_active_window( self, - frame: Atspi.Accessible | None, - app: Atspi.Accessible | None = None, + frame: Optional[Atspi.Accessible], + app: Optional[Atspi.Accessible] = None, set_window_as_focus: bool = False, notify_script: bool = False ) -> None: @@ -390,7 +390,7 @@ class FocusManager: def toggle_presentation_mode( self, script: default.Script, - event: InputEvent | None = None, + event: Optional[InputEvent] = None, notify_user: bool = True ) -> bool: """Switches between browse mode and focus mode (web content only).""" @@ -401,7 +401,7 @@ class FocusManager: def toggle_layout_mode( self, script: default.Script, - event: InputEvent | None = None, + event: Optional[InputEvent] = None, notify_user: bool = True ) -> bool: """Switches between object mode and layout mode for line presentation (web content only).""" @@ -412,7 +412,7 @@ class FocusManager: def enable_sticky_browse_mode( self, script: default.Script, - event: InputEvent | None = None, + event: Optional[InputEvent] = None, notify_user: bool = True ) -> bool: """Enables sticky browse mode (web content only).""" @@ -423,7 +423,7 @@ class FocusManager: def enable_sticky_focus_mode( self, script: default.Script, - event: InputEvent | None = None, + event: Optional[InputEvent] = None, notify_user: bool = True ) -> bool: """Enables sticky focus mode (web content only).""" diff --git a/src/cthulhu/input_event_manager.py b/src/cthulhu/input_event_manager.py index 6d02cce..0b21d2c 100644 --- a/src/cthulhu/input_event_manager.py +++ b/src/cthulhu/input_event_manager.py @@ -39,7 +39,7 @@ __copyright__ = "Copyright (c) 2024 Igalia, S.L." \ "Copyright (c) 2024 GNOME Foundation Inc." __license__ = "LGPL" -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional import gi gi.require_version("Atspi", "2.0") @@ -50,6 +50,7 @@ from . import focus_manager from . import input_event from . import script_manager from . import settings +from . import cthulhu_state from .ax_object import AXObject from .ax_utilities import AXUtilities @@ -60,9 +61,9 @@ class InputEventManager: """Provides utilities for managing input events.""" def __init__(self) -> None: - self._last_input_event: input_event.InputEvent | None = None - self._last_non_modifier_key_event: input_event.KeyboardEvent | None = None - self._device: Atspi.Device | None = None + self._last_input_event: Optional[input_event.InputEvent] = None + self._last_non_modifier_key_event: Optional[input_event.KeyboardEvent] = None + self._device: Optional[Atspi.Device] = None self._mapped_keycodes: list[int] = [] self._mapped_keysyms: list[int] = [] self._grabbed_bindings: dict[int, keybindings.KeyBinding] = {} @@ -267,6 +268,10 @@ class InputEventManager: msg = "INPUT EVENT MANAGER: Keyboard event processing is paused." debug.print_message(debug.LEVEL_INFO, msg, True) return False + if cthulhu_state.capturingKeys: + msg = "INPUT EVENT MANAGER: Capturing keys; ignoring keyboard event." + debug.print_message(debug.LEVEL_INFO, msg, True) + return False event = input_event.KeyboardEvent(pressed, keycode, keysym, modifiers, text) if event in [self._last_input_event, self._last_non_modifier_key_event]: diff --git a/src/cthulhu/notification_presenter.py b/src/cthulhu/notification_presenter.py index b7ff978..af6e614 100644 --- a/src/cthulhu/notification_presenter.py +++ b/src/cthulhu/notification_presenter.py @@ -38,6 +38,8 @@ import gi gi.require_version('Gtk', '3.0') from gi.repository import GObject from gi.repository import Gtk +from gi.repository import Gdk +from gi.repository import Gdk from . import cmdnames from . import debug @@ -275,16 +277,20 @@ class NotificationPresenter: class NotificationListGUI: """The dialog containing the notifications list.""" + RESPONSE_COPY = 1 + def __init__(self, script, title, column_headers, rows): self._script = script self._model = None + self._tree = None self._gui = self._create_dialog(title, column_headers, rows) def _create_dialog(self, title, column_headers, rows): dialog = Gtk.Dialog(title, None, Gtk.DialogFlags.MODAL, - (Gtk.STOCK_CLEAR, Gtk.ResponseType.APPLY, + (Gtk.STOCK_COPY, self.RESPONSE_COPY, + Gtk.STOCK_CLEAR, Gtk.ResponseType.APPLY, Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE)) dialog.set_default_size(600, 400) @@ -315,6 +321,11 @@ class NotificationListGUI: self._model.set_value(row_iter, i, cell) tree.set_model(self._model) + selection = tree.get_selection() + selection.set_mode(Gtk.SelectionMode.SINGLE) + if self._model.iter_n_children(None) > 0: + selection.select_path(0) + self._tree = tree dialog.connect("response", self.on_response) return dialog @@ -325,6 +336,10 @@ class NotificationListGUI: self._gui.destroy() return + if response == self.RESPONSE_COPY: + self._copy_selected_notification() + return + if response == Gtk.ResponseType.APPLY and self._model is not None: self._model.clear() getPresenter().clear_list() @@ -336,10 +351,39 @@ class NotificationListGUI: """Shows the notifications list dialog.""" self._gui.show_all() - time_stamp = cthulhu_state.lastInputEvent.timestamp - if time_stamp == 0: - time_stamp = Gtk.get_current_event_time() - self._gui.present_with_time(time_stamp) + time_stamp = Gtk.get_current_event_time() + if not time_stamp or time_stamp > 0xFFFFFFFF: + time_stamp = Gdk.CURRENT_TIME + self._gui.present_with_time(int(time_stamp)) + + def _copy_selected_notification(self): + if self._model is None or self._tree is None: + return + + selection = self._tree.get_selection() + model, paths = selection.get_selected_rows() + if not paths and self._model.iter_n_children(None) > 0: + selection.select_path(0) + model, paths = selection.get_selected_rows() + + if not paths: + return + + iter_for_path = model.get_iter(paths[0]) + if iter_for_path is None: + return + + message = model.get_value(iter_for_path, 0) + timestamp = model.get_value(iter_for_path, 1) + if timestamp: + text = f"{message}\t{timestamp}" + else: + text = f"{message}" + + clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) + clipboard.set_text(text, -1) + clipboard.store() + self._script.presentMessage(messages.CLIPBOARD_COPIED_FULL) _presenter = None def getPresenter(): diff --git a/src/cthulhu/plugin.py b/src/cthulhu/plugin.py index 6f7f7fb..257d362 100644 --- a/src/cthulhu/plugin.py +++ b/src/cthulhu/plugin.py @@ -59,9 +59,9 @@ class Plugin: self.plugin_info = plugin_info if plugin_info: self.module_name = getattr(plugin_info, 'module_name', '') - self.name = getattr(plugin_info, 'name', '') - self.version = getattr(plugin_info, 'version', '') - self.description = getattr(plugin_info, 'description', '') + self.name = plugin_info.get_name() + self.version = plugin_info.get_version() + self.description = plugin_info.get_description() @cthulhu_hookimpl def activate(self, plugin=None): diff --git a/src/cthulhu/plugin_system_manager.py b/src/cthulhu/plugin_system_manager.py index e0feb65..c749054 100644 --- a/src/cthulhu/plugin_system_manager.py +++ b/src/cthulhu/plugin_system_manager.py @@ -12,6 +12,7 @@ import os import inspect import importlib.util import logging +import configparser from enum import IntEnum # Import pluggy if available @@ -29,6 +30,12 @@ logger = logging.getLogger(__name__) if PLUGIN_DEBUG: logger.setLevel(logging.DEBUG) +_manager = None + +def getManager(): + """Return the shared PluginSystemManager instance.""" + return _manager + class PluginType(IntEnum): """Types of plugins we support.""" SYSTEM = 1 @@ -78,8 +85,10 @@ class PluginSystemManager: """Cthulhu Plugin Manager using pluggy.""" def __init__(self, app): + global _manager self.app = app logger.info("Initializing PluginSystemManager") + _manager = self # Initialize plugin manager if PLUGGY_AVAILABLE: @@ -378,15 +387,38 @@ class PluginSystemManager: if os.path.isfile(metadata_file): try: - with open(metadata_file, 'r') as f: - for line in f: - line = line.strip() - if not line or line.startswith('#'): - continue + with open(metadata_file, 'r', encoding='utf-8') as f: + contents = f.read() - if '=' in line: - key, value = line.split('=', 1) - metadata[key.strip()] = value.strip() + has_section_header = False + for line in contents.splitlines(): + stripped = line.strip() + if not stripped or stripped.startswith('#') or stripped.startswith(';'): + continue + if stripped.startswith('[') and stripped.endswith(']'): + has_section_header = True + break + + if has_section_header: + parser = configparser.ConfigParser() + try: + parser.read_string(contents) + if parser.sections(): + for section in parser.sections(): + for key, value in parser[section].items(): + metadata[key.strip().lower()] = value.strip() + return metadata + except configparser.Error as e: + logger.warning(f"Plugin metadata INI parse failed for {metadata_file}: {e}") + + for line in contents.splitlines(): + line = line.strip() + if not line or line.startswith('#') or line.startswith(';') or line.startswith('['): + continue + + if '=' in line: + key, value = line.split('=', 1) + metadata[key.strip().lower()] = value.strip() except Exception as e: logger.error(f"Error loading plugin metadata: {e}") diff --git a/src/cthulhu/plugins/AIAssistant/ai_providers.py b/src/cthulhu/plugins/AIAssistant/ai_providers.py index a6af653..9f0d67a 100644 --- a/src/cthulhu/plugins/AIAssistant/ai_providers.py +++ b/src/cthulhu/plugins/AIAssistant/ai_providers.py @@ -334,6 +334,172 @@ Keep descriptions informative and well-structured.""" return base_prompt +class CodexProvider(AIProvider): + """Codex CLI provider - uses installed Codex CLI.""" + + def __init__(self, codex_path=None, **kwargs): + super().__init__(**kwargs) + self.codex_path = codex_path or self._resolve_codex_path() + + def describe_screen(self, screenshot_data, accessibility_data): + """Generate a description using Codex CLI.""" + try: + prompt = self._build_prompt("describe", None, accessibility_data) + return self._call_codex(prompt, screenshot_data) + except Exception as e: + logger.error(f"Codex describe error: {e}") + return f"Error getting screen description: {e}" + + def answer_question(self, question, screenshot_data, accessibility_data): + """Answer a question using Codex CLI.""" + try: + prompt = self._build_prompt("question", question, accessibility_data) + return self._call_codex(prompt, screenshot_data) + except Exception as e: + logger.error(f"Codex question error: {e}") + return f"Error answering question: {e}" + + def suggest_actions(self, request, screenshot_data, accessibility_data): + """Suggest actions using Codex CLI.""" + try: + prompt = self._build_prompt("action", request, accessibility_data) + return self._call_codex(prompt, screenshot_data) + except Exception as e: + logger.error(f"Codex action error: {e}") + return f"Error suggesting actions: {e}" + + def analyze_images(self, user_question, screenshot_data, accessibility_data): + """Analyze images visible on screen using Codex CLI.""" + try: + prompt = self._build_prompt("image", user_question, accessibility_data) + return self._call_codex(prompt, screenshot_data) + except Exception as e: + logger.error(f"Codex image analysis error: {e}") + return f"Error analyzing images: {e}" + + def _build_prompt(self, task_type, user_input, accessibility_data): + """Build the complete prompt for Codex CLI.""" + system_prompt = self._prepare_system_prompt(task_type) + + if task_type == "image": + if user_input == "ANALYZE_SINGLE_IMAGE_FILE": + prompt = ( + f"{system_prompt}\n\nAnalyze and describe the single image file provided. " + "Focus on visual content only - describe what you see in the image: objects, " + "people, scenery, colors, text, composition, and any other visual details." + ) + else: + prompt = f"{system_prompt}\n\nCurrent screen context (focus on images):\n" + if user_input: + prompt += f"User question about images: {user_input}\n\n" + prompt += "Analyze and describe any images visible on this screen. Focus on visual content, not UI elements." + else: + prompt = ( + f"{system_prompt}\n\nCurrent accessibility information:\n" + f"```json\n{json.dumps(accessibility_data, indent=2)}\n```\n\n" + ) + + if task_type == "describe": + prompt += "Please describe what's on this screen." + elif task_type == "question": + prompt += f"User question: {user_input}" + elif task_type == "action": + prompt += f"User wants to: {user_input}\n\nProvide the action analysis in the required format." + + return prompt + + def _resolve_codex_path(self): + import shutil + import os + + codex_path = shutil.which('codex') + if not codex_path and os.path.isfile('/usr/bin/codex'): + codex_path = '/usr/bin/codex' + return codex_path + + def _call_codex(self, prompt, screenshot_data): + """Call Codex CLI with the prompt and optional image.""" + import subprocess + import tempfile + import os + import base64 + + if not self.codex_path: + return "Codex CLI not found" + + output_path = None + image_path = None + try: + # Write screenshot to a temp file if available + if screenshot_data: + image_format = screenshot_data.get('format', 'png') + suffix = f".{image_format}" + with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as temp_file: + image_data = base64.b64decode(screenshot_data['data']) + temp_file.write(image_data) + image_path = temp_file.name + + with tempfile.NamedTemporaryFile(delete=False) as output_file: + output_path = output_file.name + + cmd = [ + self.codex_path, + 'exec', + '--skip-git-repo-check', + '--color', + 'never', + '--output-last-message', + output_path, + '-' + ] + + if image_path: + cmd.extend(['-i', image_path]) + + result = subprocess.run( + cmd, + input=prompt, + capture_output=True, + text=True, + timeout=120 + ) + + if result.returncode != 0: + error_msg = result.stderr.strip() or result.stdout.strip() or "Codex CLI error" + logger.error(error_msg) + return f"Codex CLI error: {error_msg}" + + response_text = "" + if output_path and os.path.exists(output_path): + with open(output_path, 'r') as f: + response_text = f.read().strip() + + if not response_text: + response_text = result.stdout.strip() + + return response_text or "No response from Codex" + + except subprocess.TimeoutExpired: + error_msg = "Codex CLI timed out" + logger.error(error_msg) + return error_msg + except Exception as e: + error_msg = f"Error calling Codex CLI: {e}" + logger.error(error_msg) + return error_msg + finally: + if image_path and os.path.exists(image_path): + try: + os.unlink(image_path) + except Exception: + pass + if output_path and os.path.exists(output_path): + try: + os.unlink(output_path) + except Exception: + pass + + class OllamaProvider(AIProvider): """Ollama local AI provider.""" @@ -563,9 +729,11 @@ def create_provider(provider_type, **kwargs): """Factory function to create AI providers.""" if provider_type == "claude_code": return ClaudeCodeProvider(**kwargs) + elif provider_type == "codex": + return CodexProvider(**kwargs) elif provider_type == "ollama": return OllamaProvider(**kwargs) elif provider_type == "gemini": return GeminiProvider(**kwargs) else: - raise ValueError(f"Unknown provider type: {provider_type}") \ No newline at end of file + raise ValueError(f"Unknown provider type: {provider_type}") diff --git a/src/cthulhu/plugins/AIAssistant/plugin.py b/src/cthulhu/plugins/AIAssistant/plugin.py index c2ea69a..6bc3a39 100644 --- a/src/cthulhu/plugins/AIAssistant/plugin.py +++ b/src/cthulhu/plugins/AIAssistant/plugin.py @@ -204,6 +204,11 @@ class AIAssistant(Plugin): result = self._check_claude_code_availability() logger.info(f"Claude Code availability check result: {result}") return result + elif self._provider_type == settings.AI_PROVIDER_CODEX: + logger.info("Checking Codex CLI availability") + result = self._check_codex_availability() + logger.info(f"Codex CLI availability check result: {result}") + return result elif self._provider_type == settings.AI_PROVIDER_GEMINI: logger.info("Checking Gemini API key") if not self._api_key: @@ -258,12 +263,43 @@ class AIAssistant(Plugin): except Exception as e: logger.warning(f"Claude Code CLI not available: {e}") return False + + def _check_codex_availability(self): + """Check if Codex CLI is available.""" + try: + import subprocess + import shutil + + codex_path = shutil.which('codex') + if not codex_path and os.path.isfile('/usr/bin/codex'): + codex_path = '/usr/bin/codex' + + if not codex_path: + logger.warning("Codex CLI not found in PATH") + return False + + result = subprocess.run([codex_path, '--version'], + capture_output=True, text=True, timeout=5) + if result.returncode == 0: + logger.info("Codex CLI is available") + return True + else: + logger.warning(f"Codex CLI not responding: {result.stderr}") + return False + except subprocess.TimeoutExpired: + logger.warning("Codex CLI timeout") + return False + except Exception as e: + logger.warning(f"Codex CLI not available: {e}") + return False def _initialize_ai_provider(self): """Initialize the AI provider based on settings.""" try: if self._provider_type == settings.AI_PROVIDER_CLAUDE_CODE: self._ai_provider = create_provider("claude_code") + elif self._provider_type == settings.AI_PROVIDER_CODEX: + self._ai_provider = create_provider("codex") elif self._provider_type == settings.AI_PROVIDER_OLLAMA: self._ai_provider = create_provider("ollama", model=self._ollama_model, base_url=self._ollama_endpoint) elif self._provider_type == settings.AI_PROVIDER_GEMINI: diff --git a/src/cthulhu/plugins/OCR/meson.build b/src/cthulhu/plugins/OCR/meson.build index 09fe8ec..4dffac5 100644 --- a/src/cthulhu/plugins/OCR/meson.build +++ b/src/cthulhu/plugins/OCR/meson.build @@ -5,10 +5,10 @@ ocrdesktop_python_sources = files([ python3.install_sources( ocrdesktop_python_sources, - subdir: 'cthulhu/plugins/OCRDesktop' + subdir: 'cthulhu/plugins/OCR' ) install_data( 'plugin.info', - install_dir: python3.get_install_dir() / 'cthulhu' / 'plugins' / 'OCRDesktop' -) \ No newline at end of file + install_dir: python3.get_install_dir() / 'cthulhu' / 'plugins' / 'OCR' +) diff --git a/src/cthulhu/plugins/PluginManager/plugin.py b/src/cthulhu/plugins/PluginManager/plugin.py index 78e4b24..493e172 100644 --- a/src/cthulhu/plugins/PluginManager/plugin.py +++ b/src/cthulhu/plugins/PluginManager/plugin.py @@ -223,7 +223,7 @@ class PluginManager(Plugin): checkbox.connect("toggled", self._on_plugin_toggled, plugin_name) # Create plugin info label - info_text = f"{plugin_name}" + info_text = f"{plugin_info.get('name', plugin_name)}" if plugin_info.get('description'): info_text += f"\n{plugin_info['description']}" if plugin_info.get('version'): @@ -257,17 +257,17 @@ class PluginManager(Plugin): from cthulhu import plugin_system_manager # Use existing plugin manager to get plugins - if hasattr(plugin_system_manager, '_manager') and plugin_system_manager._manager: - manager = plugin_system_manager._manager + manager = plugin_system_manager.getManager() + if manager: manager.rescanPlugins() for plugin_info in manager.plugins: plugin_name = plugin_info.get_module_name() plugins[plugin_name] = { - 'name': plugin_name, - 'description': getattr(plugin_info, 'description', ''), - 'version': getattr(plugin_info, 'version', ''), - 'path': getattr(plugin_info, 'module_dir', '') + 'name': plugin_info.get_name(), + 'description': plugin_info.get_description(), + 'version': plugin_info.get_version(), + 'path': plugin_info.get_module_dir() } else: # Fallback: manually scan plugin directories diff --git a/src/cthulhu/settings.py b/src/cthulhu/settings.py index 5c7d32b..ab18cb4 100644 --- a/src/cthulhu/settings.py +++ b/src/cthulhu/settings.py @@ -210,6 +210,7 @@ CHAT_SPEAK_FOCUSED_CHANNEL = 2 # AI Assistant constants - simplified to providers that don't need complex API key management AI_PROVIDER_CLAUDE_CODE = "claude_code" +AI_PROVIDER_CODEX = "codex" AI_PROVIDER_GEMINI = "gemini" AI_PROVIDER_OLLAMA = "ollama" diff --git a/src/cthulhu/speech_history.py b/src/cthulhu/speech_history.py index 37bf756..cf54dcd 100644 --- a/src/cthulhu/speech_history.py +++ b/src/cthulhu/speech_history.py @@ -17,6 +17,7 @@ falls off the end, it may be added again later. from __future__ import annotations +from typing import Optional import threading from collections import deque @@ -76,7 +77,7 @@ def is_capture_paused() -> bool: return _pauseCount > 0 -def add(text: str | None, source: str = "") -> bool: +def add(text: Optional[str], source: str = "") -> bool: """Add text to speech history if it's not already present. Returns True if the item was added; False otherwise. @@ -141,7 +142,7 @@ def get_items() -> list[str]: return list(_historyItems) -def remove(text: str | None) -> bool: +def remove(text: Optional[str]) -> bool: """Remove an item from the history (if present).""" if text is None: return False