xa11y

xa11y — Cross-Platform Accessibility Client Library for Python.

Quick start:
>>> import xa11y
>>> app = xa11y.locator('application[name="Safari"]')
>>> app.child("button[name='Submit']").press()
>>> for element in app.descendant("button").elements():
...     print(element.name)

Exceptions

AccessibilityNotEnabledError

The target app advertises an accessibility tree but it is empty.

ActionNotSupportedError

The requested action is not supported on the target element.

InvalidActionDataError

An action received invalid data (e.g. Locator.nth(0), or a

InvalidSelectorError

The selector string has invalid syntax.

PermissionDeniedError

Accessibility permissions have not been granted.

PlatformError

An OS-level accessibility error occurred.

SelectorNotMatchedError

No element in the tree matched the given selector.

TimeoutError

An operation exceeded its timeout.

XA11yError

Base exception for all xa11y errors.

Classes

App

A running application — the entry point for accessibility queries.

Element

A live element with lazy navigation.

Event

An accessibility event delivered to subscribers.

EventType

Accessibility event type constants.

InputSim

Input-simulation façade for synthesised pointer and keyboard events.

Locator

A resilient element reference that re-queries on each interaction.

Rect

A bounding rectangle in logical screen coordinates (device-independent

Screenshot

A captured image: raw RGBA8 pixels plus dimensions and scale.

Subscription

A live event subscription.

Functions

get_default_timeout(→ float)

Get the effective process-wide default timeout, in seconds.

input_sim(→ InputSim)

Construct an InputSim backed by the platform's native input path.

locator(→ Locator)

Create a top-level Locator searching from the system root.

screenshot(→ Screenshot)

Capture pixels from the screen.

set_default_timeout(→ None)

Set the process-wide default timeout, in seconds.

Package Contents

exception xa11y.AccessibilityNotEnabledError

Bases: XA11yError

The target app advertises an accessibility tree but it is empty.

Raised on Linux when a Chromium/Electron app is launched without --force-renderer-accessibility (or the ACCESSIBILITY_ENABLED=1 environment variable), so the renderer accessibility bridge never populates the window’s subtree.

Initialize self. See help(type(self)) for accurate signature.

exception xa11y.ActionNotSupportedError

Bases: XA11yError

The requested action is not supported on the target element.

Initialize self. See help(type(self)) for accurate signature.

exception xa11y.InvalidActionDataError

Bases: XA11yError

An action received invalid data (e.g. Locator.nth(0), or a text-selection range with start > end).

Initialize self. See help(type(self)) for accurate signature.

exception xa11y.InvalidSelectorError

Bases: XA11yError

The selector string has invalid syntax.

Initialize self. See help(type(self)) for accurate signature.

exception xa11y.PermissionDeniedError

Bases: XA11yError

Accessibility permissions have not been granted.

Initialize self. See help(type(self)) for accurate signature.

exception xa11y.PlatformError

Bases: XA11yError

An OS-level accessibility error occurred.

Initialize self. See help(type(self)) for accurate signature.

exception xa11y.SelectorNotMatchedError

Bases: XA11yError

No element in the tree matched the given selector.

Carries a structured diagnosis (tenet 6) so the failure is understandable without re-running it under manual tree dumps. All attributes are always present (None / [] when not applicable); the same content is rendered into the exception message.

Initialize self. See help(type(self)) for accurate signature.

candidates: list[str]

Bounded near-miss candidates, e.g. same-role elements with different names, or the running applications for app lookups.

condition: str | None

What the operation was waiting for / trying to find, if known.

elapsed: float | None

Always None for this class (present for attribute parity with TimeoutError).

last_observed: str | None

What the failing operation last observed (e.g. 'selector matched 2 element(s); nth(5) requested').

scope: str | None

Bounded rendering of the search scope: a depth-limited tree dump for scoped locators, or the application list for rootless ones.

selector: str | None

The selector that failed to match.

exception xa11y.TimeoutError

Bases: XA11yError

An operation exceeded its timeout.

Carries a structured diagnosis (tenet 6): what the wait was for (condition + selector), what it last observed, and — when the selector never matched — bounded scope context (candidates + scope). All attributes are always present (None / [] when not applicable); the same content is rendered into the message, e.g.:

Timeout after 60.0s; waiting for: visible; selector:
dialog[name^="Submit"]; last observed: selector never matched;
candidates: window "Untitled — MyApp"
search scope (bounded):
...

Initialize self. See help(type(self)) for accurate signature.

candidates: list[str]

Bounded near-miss candidates (same role, different attributes). Collected only when the selector never matched during the wait.

condition: str | None

What the wait was for: 'visible', 'attached', 'press target actionable (visible && enabled)', 'event matching predicate', 'custom predicate', …

elapsed: float | None

Wall-clock seconds the operation waited before giving up.

last_observed: str | None

The last poll’s observation: 'matched button "Export" (visible=false, enabled=true, focused=false)' vs 'selector never matched'.

scope: str | None

Bounded rendering of the search scope. Collected only when the selector never matched during the wait.

selector: str | None

The selector being resolved, when the wait had one.

exception xa11y.XA11yError

Bases: Exception

Base exception for all xa11y errors.

Initialize self. See help(type(self)) for accurate signature.

class xa11y.App

A running application — the entry point for accessibility queries.

as_element() Element

Get an Element handle for the application root.

Useful for invoking Element-level methods (children(), parent(), etc.) without going through a locator.

static by_name(name: str, *, timeout: float | None = None) App

Find an application by exact name.

Polls the accessibility API until the app appears or timeout (seconds) elapses. timeout=None (the default) uses the process-wide default timeout — 5 seconds unless overridden via set_default_timeout() or the XA11Y_DEFAULT_TIMEOUT environment variable. Pass timeout=0 for a single attempt with no waiting. Only “not found” errors trigger a retry; other errors fail fast.

static by_pid(pid: int, *, timeout: float | None = None) App

Find an application by process ID.

This is the supported way to wait for a freshly launched process to surface in the accessibility tree: the lookup polls until the app becomes reachable through the platform bridge or timeout (seconds) elapses, covering the gap between the process starting and its accessibility registration completing. There is no need to hand-roll a poll over App.list(). Where the platform supports it (macOS AX, Windows UIA), the lookup attaches to the process directly instead of filtering app enumeration, so an app whose window is still unnamed mid-startup is found as soon as the accessibility API can reach it. See by_name for timeout semantics.

children() list[Element]

Get direct children (typically windows) of this application.

dump(max_depth: int | None = None) str

Render this application’s accessibility tree as an indented string.

Returns the string without printing it. Same depth semantics as tree(). This is the primary inspection helper — call print(app.dump()) to discover the role and name of every element in the app before writing selectors.

For the same output from the shell, use xa11y tree --app NAME.

static find(predicate: collections.abc.Callable[[App], bool], *, timeout: float | None = None) App

Find an application matching predicate.

predicate is called with an App for each running application on every poll; the first for which it returns truthy is returned. A falsy return means “not this one, keep polling”; if the predicate raises, the search aborts immediately and that exception propagates. See by_name for timeout semantics. There is no need to hand-roll a poll over App.list().

Use this when neither a name nor a PID alone identifies the target — e.g. a Qt dialog that registers as its own accessibility application sharing the host process’s PID:

app = xa11y.App.find(
    lambda a: a.pid == pid and a.name.startswith("My Dialog"),
    timeout=30.0,
)
static foreground(*, timeout: float | None = None) App

Resolve the application that currently holds the system foreground.

Queries the platform’s foreground mechanism directly, so it returns the exact foreground window on Windows and stays reliable when an app shows a modal dialog. “Nothing focused” retries until timeout; see by_name for timeout semantics. The returned app has is_foreground == True.

static list() list[App]

List all running applications.

Single enumeration, no polling. To wait for an app that is still starting up, use by_pid / by_name / find with a timeout instead of polling this in a loop.

locator(selector: str) Locator

Create a Locator scoped to this application’s accessibility tree.

subscribe() Subscription

Subscribe to accessibility events from this application.

tree(max_depth: int | None = None) dict

Capture this application’s accessibility tree as a recursive dict.

Each dict has keys role, name, value, and children (a list of dicts with the same shape). max_depth limits traversal: 0 = only the application node, 1 = application + direct children (typically windows), None = full subtree.

Equivalent to Element.tree(...) on the application’s root element.

property focused: bool

Deprecated alias for is_foreground.

Deprecated since version Use: is_foreground. “focused” refers to element keyboard focus elsewhere in the API; accessing this property emits a DeprecationWarning.

property is_foreground: bool

Whether this application is the foreground application.

Named is_foreground because “focused” is reserved for element-level keyboard focus (Element.focused) elsewhere in the API; this is the foreground-application flag. Populated for apps obtained via list() and find(). A point-in-time snapshot taken when the App was resolved.

On Windows apps are top-level windows, so the foreground process can own several entries; tagging is window-precise, so only the entry actually in the foreground reports is_foreground — not every window of the process. Use foreground() to resolve the exact foreground window directly.

property name: str
property pid: int | None
class xa11y.Element

A live element with lazy navigation.

blur() None

Remove keyboard focus from this element.

children() list[Element]

Get direct children (lazy — each call queries the provider).

collapse() None

Collapse this element.

decrement() None

Decrement this element’s value.

dump(max_depth: int | None = None) str

Render the subtree rooted at this element as an indented string.

Returns the string without printing. Same depth semantics as tree(). Useful as a first step when writing a test against an unfamiliar app — call print(app.dump()) to discover the role and name of every element, then turn those into selectors.

For the same output from the shell, use xa11y tree --app NAME.

expand() None

Expand this element (e.g. tree node, combo box).

focus() None

Move keyboard focus to this element.

increment() None

Increment this element’s value (e.g. slider, spinner).

parent() Element | None

Get parent element (lazy — each call queries the provider).

perform_action(action: str) None

Perform an action by snake_case name.

press() None

Press (default activate) this element.

scroll_into_view() None

Scroll this element into view.

select() None

Select this element (e.g. list item, tab).

select_text(start: int, end: int) None

Select the text range from start to end (0-based offsets).

set_numeric_value(value: float) None

Set this element’s numeric value.

set_value(value: str) None

Replace this element’s text value.

show_menu() None

Show this element’s context menu.

subscribe() Subscription

Subscribe to accessibility events for this element (typically an app).

toggle() None

Toggle this element’s checked state.

tree(max_depth: int | None = None) dict

Capture the subtree rooted at this element as a recursive dict snapshot.

Each dict has keys role, name, value, and children (a list of dicts with the same shape). max_depth limits traversal: 0 = only this node, 1 = node + direct children, None = full subtree.

Use this when you need to inspect or analyze the tree programmatically. For an indented human-readable string, see dump(). For ad-hoc exploration from the shell, see the xa11y tree CLI command.

type_text(text: str) None

Insert text at the current cursor position.

property actions: list[str]
property active: bool

Whether this element is the active (foreground) window.

The window that currently receives the user’s input. Meaningful for window-like elements (windows, dialogs); False elsewhere. Distinct from focused, which is element-level keyboard focus.

property bounds: Rect | None
property busy: bool
property checked: str | None

Tri-state toggle value: 'on', 'off', 'mixed', or None.

None means the element has no checked state (it is not a checkbox / radio button / toggle). These four are the only possible values. Compare against them explicitly — bool(element.checked) is True for every non-None value, including 'off':

is_checked = element.checked == "on"
property description: str | None
property editable: bool
property enabled: bool
property expanded: bool | None
property focusable: bool
property focused: bool
property max_value: float | None
property min_value: float | None
property modal: bool
property name: str | None
property numeric_value: float | None
property pid: int | None
property raw: dict[str, object]

Platform-specific raw data attached to this element.

Keys are provider-defined (e.g. "ax_role" on macOS, "uia_control_type" on Windows). Values are JSON-compatible (strings, numbers, booleans, lists, nested dicts). Intended for debugging and platform-specific queries — prefer the cross-platform fields (role, name, etc.) for portable logic.

property required: bool
property role: str
property selected: bool
property stable_id: str | None
property value: str | None
property visible: bool
class xa11y.Event

An accessibility event delivered to subscribers.

property app_name: str
property app_pid: int
property event_type: str
property state_flag: str | None

For state_changed events: the flag that changed (e.g. 'checked').

None for other event types.

property state_value: bool | None

For state_changed events: the new boolean value of the flag.

None for other event types.

property target: Element | None
class xa11y.EventType

Accessibility event type constants.

Each constant’s value is the string carried in Event.event_type, so handlers can compare against the constant or the literal string interchangeably.

ANNOUNCEMENT: str = 'announcement'
FOCUS_CHANGED: str = 'focus_changed'
MENU_CLOSED: str = 'menu_closed'
MENU_OPENED: str = 'menu_opened'
NAME_CHANGED: str = 'name_changed'
SELECTION_CHANGED: str = 'selection_changed'
STATE_CHANGED: str = 'state_changed'
STRUCTURE_CHANGED: str = 'structure_changed'
TEXT_CHANGED: str = 'text_changed'
VALUE_CHANGED: str = 'value_changed'
WINDOW_ACTIVATED: str = 'window_activated'
WINDOW_CLOSED: str = 'window_closed'
WINDOW_DEACTIVATED: str = 'window_deactivated'
WINDOW_OPENED: str = 'window_opened'
class xa11y.InputSim

Input-simulation façade for synthesised pointer and keyboard events.

Targets are either a (x, y) tuple in logical screen coordinates (same space as Element.bounds), or an Element (uses its bounds centre); each backend converts to physical device pixels at the OS boundary. Key values are strings: printable characters are literal ("a", "7", ";"); named keys use their Pascal name ("Enter", "ArrowUp", "F5"); modifiers are "Shift", "Ctrl", "Alt", "Meta".

Input simulation is distinct from the accessibility action layer — prefer Locator.press() / Locator.type_text() when the target exposes the semantic action. Use InputSim for gestures with no a11y equivalent (drag-and-drop, scroll wheels, global shortcuts).

chord(key: str, held: list[str] = ...) None

Tap key while the keys in held are held down.

click(target: tuple[int, int] | Element) None

Left-click once at target.

double_click(target: tuple[int, int] | Element) None

Left double-click at target.

drag(start: tuple[int, int] | Element, end: tuple[int, int] | Element) None

Left-drag from start to end.

move_to(target: tuple[int, int] | Element) None

Move the pointer to target without pressing any button.

press(key: str) None

Tap a key (press + release).

right_click(target: tuple[int, int] | Element) None

Right-click at target.

scroll(target: tuple[int, int] | Element, dx: int = 0, dy: int = 0) None

Scroll at target. dx positive → right, dy positive → down.

type_text(text: str) None

Type literal text into the currently focused control.

class xa11y.Locator

A resilient element reference that re-queries on each interaction.

Locators never hold a live reference to a UI element. Instead, they store a selector and resolve it on demand, making them immune to staleness. Action methods auto-wait for the element to appear before acting, using the process-wide default timeout (5 seconds unless overridden via set_default_timeout() or the XA11Y_DEFAULT_TIMEOUT environment variable). wait_* methods use the same default when no explicit timeout= is passed.

blur() None

Remove keyboard focus from the matched element.

child(selector: str) Locator

Return a new Locator scoped to direct children matching selector.

collapse() None

Collapse the matched element.

count() int

Count matching elements.

decrement() None

Decrement the matched element (slider, spinner).

descendant(selector: str) Locator

Return a new Locator scoped to descendants matching selector.

dump(max_depth: int | None = None) str

Render the subtree rooted at the matched element as an indented string.

Returns the string without printing it. Same depth and resolution semantics as tree().

element() Element

Get a single Element handle for the matched element.

elements() list[Element]

Get all matching elements.

exists() bool

Check if a matching element exists.

expand() None

Expand the matched element.

first() Locator

Return a new Locator that selects the first match.

focus() None

Set keyboard focus on the matched element.

increment() None

Increment the matched element (slider, spinner).

nth(n: int) Locator

Return a new Locator that selects the n-th match (1-based).

perform_action(action: str) None

Perform an action by snake_case name.

press() None

Click / invoke the matched element.

scroll_into_view() None

Scroll the matched element into the visible area.

select() None

Select the matched element (list item, tab, etc.).

select_text(start: int, end: int) None

Select a text range within the matched element (0-based offsets).

set_numeric_value(value: float) None

Set the numeric value of the matched element (slider, spinner).

set_value(value: str) None

Set the text value of the matched element.

show_menu() None

Show the context menu for the matched element.

toggle() None

Toggle the matched element (checkbox, switch).

tree(max_depth: int | None = None) dict

Capture the subtree rooted at the matched element as a recursive dict.

Each dict has keys role, name, value, and children (a list of dicts with the same shape). max_depth limits traversal: 0 = only this node, 1 = node + direct children, None = full subtree.

Resolves the selector once; fails fast with SelectorNotMatchedError if no match — does not auto-wait.

type_text(text: str) None

Type text at the current cursor position on the matched element.

wait_attached(timeout: float | None = None) Element

Wait until the element exists in the tree.

wait_detached(timeout: float | None = None) None

Wait until the element is removed from the tree.

wait_disabled(timeout: float | None = None) Element

Wait until the element is disabled.

wait_enabled(timeout: float | None = None) Element

Wait until the element is enabled.

wait_focused(timeout: float | None = None) Element

Wait until the element has keyboard focus.

wait_hidden(timeout: float | None = None) None

Wait until the element is hidden or removed.

wait_unfocused(timeout: float | None = None) Element

Wait until the element does not have keyboard focus.

wait_until(predicate: collections.abc.Callable[[Element | None], bool], timeout: float | None = None) None

Wait until an arbitrary predicate is satisfied.

A predicate that raises aborts the wait immediately and propagates the exception — it is not swallowed as “not yet” (which would resurface later as a misleading timeout). Mirrors App.find.

wait_visible(timeout: float | None = None) Element

Wait until the element is visible, polling the provider.

timeout=None (the default) uses the process-wide default timeout — see set_default_timeout(). Applies to all wait_* methods.

property selector: str

The CSS-like selector string for this locator.

class xa11y.Rect

A bounding rectangle in logical screen coordinates (device-independent points) on every platform, origin at the top-left of the primary display.

Same coordinate space as screenshot(region=...) and input targets, so bounds pass straight through. To index into a captured image multiply by the screenshot’s scale (physical = logical * scale).

property height: int
property width: int
property x: int
property y: int
class xa11y.Screenshot

A captured image: raw RGBA8 pixels plus dimensions and scale.

width and height are in physical pixels. scale is the physical-to-logical ratio (1.0 on standard displays, 2.0 on typical Retina). pixels has length width * height * 4.

save_png(path: str | bytes | object) None

Encode as PNG and write to path. Accepts str, bytes or os.PathLike.

to_png() bytes

Encode the image as a PNG and return the bytes.

property height: int
property pixels: bytes

Raw RGBA8 pixel bytes (width * height * 4).

property scale: float
property width: int
class xa11y.Subscription

A live event subscription.

close() None
recv(timeout: float = 5.0) Event
try_recv() Event | None
wait_for(predicate: collections.abc.Callable[[Event], bool], timeout: float = 5.0) Event
xa11y.get_default_timeout() float

Get the effective process-wide default timeout, in seconds.

Resolution order: the set_default_timeout() value, else the XA11Y_DEFAULT_TIMEOUT environment variable, else the built-in 5.0.

xa11y.input_sim() InputSim

Construct an InputSim backed by the platform’s native input path.

xa11y.locator(selector: str) Locator

Create a top-level Locator searching from the system root.

xa11y.screenshot(*, element: Element | None = None, region: tuple[int, int, int, int] | None = None) Screenshot

Capture pixels from the screen.

With no arguments, captures the full primary display. Pass element to capture the pixels under an element’s current bounds, or region as (x, y, width, height) to capture an explicit rectangle in logical screen coordinates. Passing both raises ValueError.

xa11y.set_default_timeout(timeout: float) None

Set the process-wide default timeout, in seconds.

Becomes the default for every auto-waiting action method, wait_* call, and app lookup (App.by_name / App.by_pid / App.find) that doesn’t pass an explicit timeout=. An explicit per-call timeout= always wins. Takes precedence over the XA11Y_DEFAULT_TIMEOUT environment variable (seconds, read once at import).

Pass 0 for “single attempt, no polling” semantics. Raises ValueError for negative or non-finite values.