Skip to content

Python API Reference

The xa11y Python package provides bindings to the xa11y Rust library via PyO3.

Install from PyPI:

Terminal window
pip install xa11y

Quick start:

import xa11y
safari = xa11y.App.by_name("Safari")
for button in safari.locator("button").elements():
print(button.name)
safari.locator("button[name='OK']").press()

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

Parameter Type Default
selector str

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.

Parameter Type 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.

What a platform’s backend can report: one answer per feature, read from a static table without connecting to any app.

With no arguments, describes the running platform and OS version. Pass platform to describe another one, and os_version to narrow it to an OS release.

Parameter Type Default
platform PlatformName | None None
os_version str | None None
backend str | None None

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

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.

annotate draws a numbered box over every element each locator matches, and fills in Screenshot.legend, Screenshot.omitted and Screenshot.truncated. Each entry is one group, with its own colour and tag letter, and must be scoped to an application: app.locator("button"). An entry that is neither a Locator nor a str raises TypeError.

A rootless group raises InvalidSelectorError — a bare str, or locator, searches every application, and each entry’s <selector>:nth(n) would then count within one application while the legend counts across all of them. The two agree only for a single application, so the selector on an entry would name a different element than the box it labels.

Cropping and annotating are independent: annotations outside the captured area land in omitted rather than being clamped to an edge.

shot = xa11y.screenshot( element=app.locator(“window”).element(), annotate=[app.locator(“button”), app.locator(“text_field”)], )

Parameter Type Default
element Element | None None
region tuple[int, int, int, int] | None None
annotate list[Locator | str] | None None

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.

Property Type Description
selector str The CSS-like selector string for this locator.
Method Returns Description
nth(n: int) Locator Return a new Locator that selects the n-th match (1-based).
first() Locator Return a new Locator that selects the first match.
child(selector: str) Locator Return a new Locator scoped to direct children matching selector.
descendant(selector: str) Locator Return a new Locator scoped to descendants matching selector.
Method Returns Description
exists() bool Check if a matching element exists.
count() int Count matching elements.
element() Element Get a single Element handle for the matched element.
elements() list[Element] Get all matching elements.
Method Returns Description
capture(*, max_depth: int | None = None, max_nodes: int = 2000, max_text_bytes: int = 65536, detail: Iterable[CaptureDetailName] | None = None, timeout: float | None = None, max_runs: int = 4096, cancel: CancelToken | None = None) Capture Read the subtree into a bounded Capture: every node read,
tree(max_depth: int | None = None) dict Capture the subtree rooted at the matched element as a recursive dict.
dump(max_depth: int | None = None) str Render the subtree rooted at the matched element as an indented string.
press() Click / invoke the matched element.
focus() Set keyboard focus on the matched element.
blur() Remove keyboard focus from the matched element.
toggle() Toggle the matched element (checkbox, switch).
expand() Expand the matched element.
collapse() Collapse the matched element.
select() Select the matched element (list item, tab, etc.).
show_menu() Show the context menu for the matched element.
scroll_into_view() Scroll the matched element into the visible area.
increment() Increment the matched element (slider, spinner).
decrement() Decrement the matched element (slider, spinner).
set_value(value: str) Set the text value of the matched element.
set_numeric_value(value: float) Set the numeric value of the matched element (slider, spinner).
type_text(text: str) Type text at the current cursor position on the matched element.
select_text(start: int | RangeLike, end: int | None = None) Select a text range within the matched element: (start, end) or a
text() Text The matched element’s text, read by range. Re-resolves the locator on
perform_action(action: str) Perform an action by snake_case name.
activate() Activate the matched window: bring it to the foreground and give it focus.
minimize() Minimize the matched window.
maximize() Maximize the matched window.
enter_fullscreen() Put the matched window in native fullscreen.
restore() Restore the matched window to its normal state (from minimized,
close() Close the matched window.
move_to(x: int, y: int) Move the matched window to the given logical screen coordinates (top-left origin).
resize_to(width: int, height: int) Resize the matched window to the given logical dimensions.
Method Returns Description
wait_visible(timeout: float | None = None) Element Wait until the element is visible, polling the provider.
wait_attached(timeout: float | None = None) Element Wait until the element exists in the tree.
wait_detached(timeout: float | None = None) Wait until the element is removed from the tree.
wait_enabled(timeout: float | None = None) Element Wait until the element is enabled.
wait_hidden(timeout: float | None = None) Wait until the element is hidden or removed.
wait_disabled(timeout: float | None = None) Element Wait until the element is disabled.
wait_focused(timeout: float | None = None) Element Wait until the element has keyboard focus.
wait_unfocused(timeout: float | None = None) Element Wait until the element does not have keyboard focus.
wait_stable(timeout: float | None = None) Element Wait until visible bounds are observed unchanged for at least 750 ms
wait_until(predicate: Callable[[Element | None], bool], timeout: float | None = None) Wait until an arbitrary predicate is satisfied.

The element no longer names a live node, so nothing was done to it.

A subclass of SelectorNotMatchedError, so existing handlers keep catching it. xa11y never re-matches a stale element by name: re-resolve with a locator to act on whatever is there now.

Attribute Type Description
reason Literal['removed', 'process_exited', 'process_restarted', 'identity_changed', 'evicted'] Why: "removed" (the node is gone), "process_exited",
element str What was captured: role "name", or the handle when that is all
selector str | None The same description as element. A stale element was never found

A text range is out of bounds, reversed, or splits a surrogate pair.

Offsets are UTF-16 code units. length is the text’s current length when the check knew it, so a range made stale by an edit says how long the text is now.

Attribute Type Description
start int Start of the refused range (UTF-16 code units).
end int End of the refused range (UTF-16 code units).
length int | None The text’s length when the check knew it, else None.

Size of a table, grid or tree grid. Each count is None when not reported.

Property Type Description
row_count CountValue | None Number of rows: int, "unknown", or None.
column_count CountValue | None Number of columns: int, "unknown", or None.

Row, column and spans of a table cell. Every index is 0-based; a field the platform did not report is None.

Property Type Description
row int | None 0-based row index.
column int | None 0-based column index.
row_span int | None Rows the cell covers (at least 1), or None when not reported.
column_span int | None Columns the cell covers (at least 1), or None when not reported.
kind Literal['data', 'column_header', 'row_header'] | None "data", "column_header" or "row_header", or None when
header Literal['column', 'row'] | None "column" or "row" for a header cell, else None (a data cell,

Row index of a table or tree-grid row (0-based).

Property Type Description
index int | None 0-based row index in the table, or None when not reported.

Position of an item in a list, tree, tab list or radio group.

index and depth are 0-based: the first item is 0, a top-level tree item has depth 0. To show “3 of 10”, add 1 to index.

Property Type Description
index int | None 0-based position in the set.
size CountValue | None Number of items in the set: int, "unknown", or None.
depth int | None 0-based nesting depth.

Live-region settings of an element. Every field is None when the platform did not report it.

Property Type Description
politeness PolitenessName | None "off", "polite" or "assertive".
atomic bool | None Whether the whole region is announced when part of it changes.
relevant str | None Which changes are announced, as the platform spells it

One kind of relation and the elements it points at, as Element.relations returns it.

Property Type Description
kind Literal['labelled_by', 'label_for', 'described_by', 'description_for', 'details', 'details_for', 'controls', 'controlled_by', 'flows_to', 'flows_from', 'error_message', 'error_for', 'owns', 'member_of', 'linked'] The relation kind, in snake_case ("labelled_by", "controls").
elements list[Element] The related elements, in platform order; [] for a kind the

Stops a capture in progress. Pass it as capture(cancel=token) and call cancel from any thread: the walk checks it between nodes.

Method Returns Description
cancel() Ask every capture holding this token to stop. Idempotent.
is_cancelled() bool Whether cancel has been called.

How many platform reads a capture made, by kind. A structure-only capture makes only children and identity reads.

Property Type Description
children_reads int Child-list reads, one per node whose children were read.
identity_reads int Identity reads, one per node.
text_reads int Text reads ("text" detail).
formatting_reads int Attribute runs read ("formatting" detail).
relation_reads int Relation reads ("relations" detail).
text_bytes int Bytes of text read.

Which limits dropped something. A limit that was reached without dropping anything is not reported.

Property Type Description
by_depth bool Children below max_depth were left out.
by_nodes bool Nodes past max_nodes were left out.
by_text bool Text past max_text_bytes was left out.
by_time bool The time budget ran out before the walk finished.
by_formatting bool Attribute runs past max_runs were left out.
by_cancel bool The cancel token was cancelled before the walk finished.
Method Returns Description
any() bool Whether any limit dropped something.

An element’s text, as far as the capture’s text budget reached.

Property Type Description
content str The text read, from offset 0.
length int The element’s full text length, in UTF-16 code units.
truncated bool Whether content stops short of length.
runs list[TextRun] | None With detail={"formatting"}: the attribute runs over
runs_truncated bool Whether runs past max_runs were left out of runs.

A read of one node that failed. The capture continued past it.

Property Type Description
step Literal['children', 'identity', 'text', 'formatting', 'relations', 'cycle'] What was being read: "children", "identity", "text",
error XA11yError The exception the read raised, as an xa11y exception instance.

One kind of relation on a CapturedNode and its targets.

Property Type Description
kind Literal['labelled_by', 'label_for', 'described_by', 'description_for', 'details', 'details_for', 'controls', 'controlled_by', 'flows_to', 'flows_from', 'error_message', 'error_for', 'owns', 'member_of', 'linked'] The relation kind, in snake_case ("labelled_by", "controls").
targets list[int | Element] The targets, in platform order: an int index into

One node of a Capture.

Property Type Description
index int Index in Capture.nodes, which is pre-order. 0-based.
parent int | None The parent’s index; None on the root.
depth int Depth below the root, which is 0.
element Element The element, which names exactly this node: actions on it reach it or
children list[int] Indices of the children in the capture, in platform order.
children_count int | None How many children the node has, including ones the capture left out.
children_truncated bool Whether children of this node are missing from the capture.
errors list[CaptureError] Reads of this node that failed.
text CapturedText | None The node’s text, with detail={"text"}, when it has a text
relations list[CapturedRelation] With detail={"relations"}: one CapturedRelation per kind
role str Shortcut for element.role.
name str | None Shortcut for element.name.

A bounded read of a subtree: every node read, pre-order, and which limits cut it. See Element.capture.

Property Type Description
nodes list[CapturedNode] Every node read, pre-order. The root is index 0.
truncated Truncation Which limits dropped something.
vanished int Nodes that disappeared while the capture read them, and were dropped.
started float When the walk began, in seconds since the epoch.
finished float When the walk ended, in seconds since the epoch.
backend str | None Which platform API read the nodes: "atspi", "ax",
stats CaptureStats How many platform reads the capture made, by kind.
Method Returns Description
root() CapturedNode The root node.
subtree(index: int) list[CapturedNode] The nodes of index’s subtree, itself first, in pre-order. Empty

A half-open range [start, end) of UTF-16 code units.

Anywhere a range is accepted, a (start, end) tuple works too.

Property Type Description
start int 0-based offset of the first unit.
end int 0-based offset one past the last unit.
Method Returns Description
len() int Number of UTF-16 code units covered.
is_empty() bool Whether the range covers nothing (a caret position).

Formatting of a run of text. Every attribute is None when the platform did not report it; nothing is defaulted.

Property Type Description
font_family str | None Font family name.
font_size float | None Font size in points.
font_weight int | None Font weight on the CSS scale: 400 normal, 700 bold.
italic bool | None Italic or oblique.
underline bool | None Underlined.
strikethrough bool | None Struck through.
foreground str | None Text colour as "#rrggbb" or "#rrggbbaa".
background str | None Background colour as "#rrggbb" or "#rrggbbaa".
script Literal['baseline', 'superscript', 'subscript'] | None "baseline", "superscript" or "subscript".
spelling_error bool | None Marked as a spelling error.

A run of text sharing one set of attributes.

Property Type Description
range TextRange The text the run covers.
attributes TextAttributes Its formatting.

One page of rendered lines from Text.lines.

Property Type Description
lines list[TextRange] The lines, each clipped to the requested range.
truncated bool Whether the limit stopped the walk before the end of the range.
next_start int | None Where to start the next page; set exactly when truncated.

One page of formatting runs from Text.runs.

Property Type Description
runs list[TextRun] The runs, each clipped to the requested range.
truncated bool Whether the limit stopped the walk before the end of the range.
next_start int | None Where to start the next page; set exactly when truncated.

What a text_changed event inserted or deleted (Event.text_change).

Each field is what the platform supplied: range is None when it counts offsets in a unit other than UTF-16 (on Linux, every toolkit but Qt), and text is None when it does not send the text.

Property Type Description
kind Literal['inserted', 'deleted'] "inserted" or "deleted".
range TextRange | None Where, in UTF-16 code units: for an insertion the range the new text
text str | None The inserted or deleted text, or None when not supplied.

An element’s text, read by range. Every method is a live platform read; offsets are UTF-16 code units, so use read rather than slicing Element.value when the text may contain emoji.

From Element.text() it stays bound to that element. From Locator.text() it re-resolves the locator on every read.

Method Returns Description
length() int Length in UTF-16 code units, without reading the text where the
caret() int | None The caret offset. None when the element is not focused. While a
selections() list[TextRange] The selected ranges in document order; empty when nothing is selected.
read(range: TextRange | tuple[int, int]) str The text in range (a TextRange or (start, end)), exactly
read_all() str The whole text.
find(needle: str, *, occurrence: int = 0, within: RangeLike | None = None) TextRange | None The occurrence-th (0-based) exact match of needle, searching
object_at(offset: int) Element | None Live embedded object at a UTF-16 offset; unsupported on default backends.
range_at(offset: int, unit: Literal['character', 'word', 'line', 'paragraph']) TextRange The unit ("character", "word", "line",
lines(range: TextRange | tuple[int, int], limit: int = 200) TextLines Rendered lines covering range, at most limit per page.
bounds(range: TextRange | tuple[int, int]) list[Rect] Screen rectangles of range, one per rendered line fragment, in the
runs(range: TextRange | tuple[int, int], limit: int = 200) TextRuns Formatting runs covering range, at most limit per page.

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.

Constant Value Description
FOCUS_CHANGED 'focus_changed'
VALUE_CHANGED 'value_changed'
NAME_CHANGED 'name_changed'
STATE_CHANGED 'state_changed'
STRUCTURE_CHANGED 'structure_changed'
WINDOW_OPENED 'window_opened'
WINDOW_CLOSED 'window_closed'
WINDOW_ACTIVATED 'window_activated'
WINDOW_DEACTIVATED 'window_deactivated'
SELECTION_CHANGED 'selection_changed'
MENU_OPENED 'menu_opened'
MENU_CLOSED 'menu_closed'
TEXT_CHANGED 'text_changed'
TEXT_SELECTION_CHANGED 'text_selection_changed'
ANNOUNCEMENT 'announcement'

An accessibility event delivered to subscribers.

Property Type Description
event_type str
app_name str
app_pid int
target Element | None
state_flag str | None For state_changed events: the flag that changed (e.g. 'checked').
state_value bool | None For state_changed events: the new boolean value of the flag.
text_change TextChange | None For text_changed events: what was inserted or deleted, when the
sequence int Position in this subscription’s stream, from 0. A gap means events
announcement AnnouncementInfo | None For announcement events: the origin and, where sent, the text.
time float When the event was received, in seconds since the epoch.

What an announcement event carries (Event.announcement).

Property Type Description
origin Literal['request', 'live_region', 'alert'] "request" (an app asked for text to be spoken), "live_region"
text str | None The text to announce, for a request where the platform sends it.
politeness PolitenessName | None "polite" or "assertive", normalised across platforms.
platform_priority str | None The platform’s own priority, as it spells it.
activity_id str | None UIA’s activityId (Windows only).

A live event subscription.

Property Type Description
dropped int How many events the buffer has dropped, in total, because it was full.
Method Returns Description
try_recv() Event | None
recv(timeout: float = 5.0) Event
wait_for(predicate: Callable[[Event], bool], timeout: float = 5.0) Event
close()
__iter__() Iterator[Event]

A running application — the entry point for accessibility queries.

Property Type Description
name str
pid int | None
is_foreground bool Whether this application is the foreground application.
focused bool Deprecated alias for is_foreground.
Method Returns Description
by_name(name: str, *, timeout: float | None = None, backend: str | None = None) App Find an application by exact name.
by_pid(pid: int, *, timeout: float | None = None, backend: str | None = None) App Find an application by process ID.
find(predicate: Callable[[App], bool], *, timeout: float | None = None, backend: str | None = None) App Find an application matching predicate.
foreground(*, timeout: float | None = None, backend: str | None = None) App Resolve the application that currently holds the system foreground.
list(*, backend: str | None = None) list[App] List all running applications.
locator(selector: str) Locator Create a Locator scoped to this application’s accessibility tree.
subscribe(*, kinds: Iterable[str] | None = None, buffer: int | None = None) Subscription Subscribe to accessibility events from this application.
children() list[Element] Get direct children (typically windows) of this application.
windows() list[Element] List the top-level windows of this application.
enable_web_accessibility(*, timeout: float | None = None) Turn on the accessibility tree for the app’s web content, then wait
as_element() Element Get an Element handle for the application root.
capture(*, max_depth: int | None = None, max_nodes: int = 2000, max_text_bytes: int = 65536, detail: Iterable[CaptureDetailName] | None = None, timeout: float | None = None, max_runs: int = 4096, cancel: CancelToken | None = None) Capture Read the subtree into a bounded Capture: every node read,
tree(max_depth: int | None = None) dict Capture this application’s accessibility tree as a recursive dict.
dump(max_depth: int | None = None) str Render this application’s accessibility tree as an indented string.

One OS-owned shell surface — the taskbar, a desktop panel, the dock, the menu bar, a process’s status items, the desktop, or an open flyout.

ShellSurface is not an Element. It represents the surface as a whole and provides a locator() to search its accessibility tree, exactly as App does for an application.

Property Type Description
kind Literal['menu_bar', 'status_items', 'taskbar', 'panel', 'dock', 'desktop', 'flyout', 'unknown'] What this surface is, as a snake_case string ("taskbar",
name str The owning app for per-app surfaces ("Safari" menu bar), the
pid int | None Owning process where the platform reports one honestly. On Windows
Method Returns Description
list() list[ShellSurface] List the OS shell surfaces currently on screen.
by_kind(kind: Literal['menu_bar', 'status_items', 'taskbar', 'panel', 'dock', 'desktop', 'flyout', 'unknown'], *, timeout: float | None = None) ShellSurface Wait for exactly one shell surface of kind.
locator(selector: str) Locator Create a Locator scoped to this surface’s accessibility tree.
children() list[Element] Get direct children of the surface root.
as_element() Element Get an Element handle for the surface root.
capture(*, max_depth: int | None = None, max_nodes: int = 2000, max_text_bytes: int = 65536, detail: Iterable[CaptureDetailName] | None = None, timeout: float | None = None, max_runs: int = 4096, cancel: CancelToken | None = None) Capture Read the subtree into a bounded Capture: every node read,
tree(max_depth: int | None = None) dict Capture this surface’s accessibility tree as a recursive dict.
dump(max_depth: int | None = None) str Render this surface’s accessibility tree as an indented string.

A live element with lazy navigation.

Property Type Description
role str
name str | None
value str | None
description str | None
numeric_value float | None
min_value float | None
max_value float | None
stable_id str | None
pid int | None
actions list[str]
bounds Rect | None
raw dict[str, object] Platform-specific raw data attached to this element.
enabled bool
visible bool
hidden_reason HiddenReasonName | None Why the element is not visible, or None when it is.
focused bool
active bool Whether this element is the active (foreground) window.
checked str | None Tri-state toggle value: 'on', 'off', 'mixed', or None.
selected bool
expanded bool | None
editable bool
focusable bool
modal bool
required bool
busy bool
password bool Whether this is a password (secure text) field.
minimized bool | None Whether the window is minimized.
maximized bool | None Whether the window is maximized. None = unknown / not a window.
fullscreen bool | None Whether the window is fullscreen. None = unknown / not a window.
table TableInfo | None Size of a table, grid or tree grid; None when not a table or not
cell CellInfo | None Row, column and spans of a table cell (0-based); None otherwise.
table_row RowInfo | None Row index of a table row (0-based); None otherwise.
collection CollectionInfo | None Position in a list, tree, tab list or radio group (0-based index and
heading_level int | None A heading’s level, 1 for a top-level heading (a level, not an
landmark_type LandmarkName | None Which landmark a landmark element is, as its ARIA role;
web_role str | None The ARIA role web content declares, verbatim; None outside web
link_url str | None A link’s target URL; None on a non-link or when not reported.
live_region LiveRegionInfo | None Live-region settings; None when the element has none.
Method Returns Description
same_as(other: Element) bool Whether other names the same platform node, checked live with
check_alive() Prove this element still names the node it was found as. Raises
children() list[Element] Get direct children (lazy — each call queries the provider).
parent() Element | None Get parent element (lazy — each call queries the provider).
capture(*, max_depth: int | None = None, max_nodes: int = 2000, max_text_bytes: int = 65536, detail: Iterable[CaptureDetailName] | None = None, timeout: float | None = None, max_runs: int = 4096, cancel: CancelToken | None = None) Capture Read the subtree into a bounded Capture: every node read,
tree(max_depth: int | None = None) dict Capture the subtree rooted at this element as a recursive dict snapshot.
dump(max_depth: int | None = None) str Render the subtree rooted at this element as an indented string.
subscribe(*, kinds: Iterable[str] | None = None, buffer: int | None = None) Subscription Subscribe to accessibility events for this element (typically an app).
press() Click / invoke this element.
focus() Move keyboard focus to this element.
blur() Remove keyboard focus from this element.
toggle() Toggle this element’s checked state.
expand() Expand this element (e.g. tree node, combo box).
collapse() Collapse this element.
select() Select this element (e.g. list item, tab).
show_menu() Show this element’s context menu.
scroll_into_view() Scroll this element into view.
increment() Increment this element’s value (e.g. slider, spinner).
decrement() Decrement this element’s value.
set_value(value: str) Replace this element’s text value.
set_numeric_value(value: float) Set this element’s numeric value.
type_text(text: str) Insert text at the current cursor position.
select_text(start: int | RangeLike, end: int | None = None) Select a text range: select_text(start, end), or
relations(kinds: list[RelationKindName] | None = None) list[Relation] The elements this one is related to, as a list of Relation
text() Text This element’s text, read by range (UTF-16 offsets). Reads nothing
cell_at(row: int, column: int) Element | None On a table: the cell at 0-based row / column (a merged cell
column_headers() list[Element] On a table: its column header cells.
row_headers() list[Element] On a table: its row header cells.
column_header_cells() list[Element] On a cell: the column header cells that head it.
row_header_cells() list[Element] On a cell: the row header cells that head it.
selected_items() list[Element] On a list, tree, table or tab list: the selected items.
perform_action(action: str) Perform an action by snake_case name.
activate() Activate this window: bring it to the foreground and give it focus.
minimize() Minimize this window.
maximize() Maximize this window.
enter_fullscreen() Put this window in native fullscreen (macOS AXFullScreen).
restore() Restore this window to its normal state (from minimized/maximized/fullscreen).
close() Close this window.
move_to(x: int, y: int) Move this window to the given logical screen coordinates (top-left origin).
resize_to(width: int, height: int) Resize this window to the given logical width and height.

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).

Built-in backends reject pointer targets outside every active display before sending input, with InvalidActionDataError. On Wayland, raw points are checked against the compositor’s logical output layout, or the XA11Y_SCREEN_WIDTH / XA11Y_SCREEN_HEIGHT override. This checks screen membership, not visibility or hit testing. The error’s pointer_target carries the requested point, checked coordinate space, displays, and any element bounds and anchor. Both drag endpoints are checked before the gesture. Captured moves while this simulator holds a mouse button, and button releases, remain possible; explicit clicks and scrolls still require an on-display target.

Method Returns Description
click(target: tuple[int, int] | Element, *, button: Literal['left', 'right', 'middle'] = 'left', count: int = 1, held: list[str] | None = None, anchor: AnchorName | tuple[int, int] | None = None) Click at target.
double_click(target: tuple[int, int] | Element) Left double-click at target.
right_click(target: tuple[int, int] | Element) Right-click at target.
move_to(target: tuple[int, int] | Element) Move the pointer to target without pressing any button.
mouse_down(button: Literal['left', 'right', 'middle'] = 'left') Press a mouse button at the current pointer location, without
mouse_up(button: Literal['left', 'right', 'middle'] = 'left') Release a mouse button at the current pointer location.
drag(start: tuple[int, int] | Element, end: tuple[int, int] | Element, *, button: Literal['left', 'right', 'middle'] = 'left', held: list[str] | None = None, duration: float = 0.15) Drag from start to end.
scroll(target: tuple[int, int] | Element, dx: int = 0, dy: int = 0) Scroll at target, in wheel ticks. Positive dy scrolls down, toward
press(key: str) Tap a key (press + release).
chord(key: str, held: list[str] = ...) Tap key while the keys in held are held down.
key_down(key: str) Press key without releasing it. Pair with key_up.
key_up(key: str) Release a key previously pressed with key_down.
type_text(text: str) Type literal text into the currently focused control.

One drawn annotation box: the tag in the image, and the element it came from.

A model reads a tag off the PNG; selector is what acts on the element that tag labels: entry = next(e for e in shot.legend if e.tag == “B1”) app.locator(entry.selector).set_value(“42”)

Property Type Description
tag str What is drawn in the box — "B7". A group letter and a 1-based
group int 1-based, matching the position of this element’s locator in
index int 1-based, and exactly the :nth(n) argument in selector.
selector str A selector usable as-is against the same scope the group had —
role str The element’s role, snake_case as everywhere else.
name str | None The element’s accessible name, when it has one.
bounds Rect The element’s bounds in logical screen coordinates.
color tuple[int, int, int] The box colour as an (r, g, b) tuple, for correlating a box

An element that matched an annotate= selector but is not in the image.

Reported rather than dropped: a legend that disagreed with the picture, with no way to find out why, is what this exists to prevent.

Property Type Description
selector str The selector that would reach this element.
role str The element’s role, snake_case.
name str | None The element’s accessible name, when it has one.
reason Literal['no_bounds', 'zero_area', 'outside_capture'] Why it could not be drawn: no bounds at all, zero-area bounds, or

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

width and height describe the actual returned image. scale is a compatibility hint for simple single-display captures; use the explicit coordinate methods for mixed-DPI, cropped, or resized images. pixels has length width * height * 4.

legend, omitted and truncated describe what annotate= drew. They are [], [] and 0 on an unannotated capture, so consumers need no version check.

Property Type Description
width int
height int
scale float
mapping_available bool Whether desktop/image coordinate conversion metadata is available.
pixels bytes Raw RGBA8 pixel bytes (width * height * 4).
legend list[LegendEntry] One entry per drawn box, in group order and then match order.
omitted list[Omission] Elements that matched an annotate= selector but could not be
truncated int How many matched elements were not described at all because the
Method Returns Description
to_png() bytes Encode the image as a PNG and return the bytes.
save_png(path: str | bytes | object) Encode as PNG and write to path. Accepts str, bytes or os.PathLike.
desktop_to_image(x: int, y: int) tuple[int, int] Convert a desktop point to a pixel in this image.
image_to_desktop(x: int, y: int) tuple[int, int] Convert an image pixel to a desktop point suitable for input.
desktop_rect_to_image(x: int, y: int, width: int, height: int) list[Rect] Convert a desktop rectangle to one image rectangle per display piece.
crop(x: int, y: int, width: int, height: int) Screenshot Crop by image pixels while preserving coordinate mapping.
resize(width: int, height: int) Screenshot Resize image pixels while scaling coordinate mapping.

One feature’s answer on one platform.

Property Type Description
name str Feature name, <area>:<name> ("event:menu_opened").
description str What the feature is, in one sentence.
support Literal['supported', 'partial', 'unsupported'] "supported", "partial" or "unsupported".
note str | None How the platform provides it, or why it does not; None when there
since str | None The first OS version that supports it, when older ones do not.

Property Type Description
backend str
platform Literal['linux', 'macos', 'windows'] The platform described.
os_version str | None The OS version the answers were narrowed to, or None when none
Method Returns Description
names() list[str] Every feature name the table knows, in table order.
entries() list[Capability] Every feature, in table order.
get(name: str) Capability The capability named name. Raises InvalidActionDataError
support(name: str) Literal['supported', 'partial', 'unsupported'] Shorthand for get(name).support.
note(name: str) str | None Shorthand for get(name).note.
contains(name: str) bool Whether name is a feature the table knows (also name in caps).
__iter__() Iterator[Capability]

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 Type Description
x int
y int
width int
height int

All xa11y exceptions inherit from xa11y.XA11yError.

Exception Description
XA11yError Base exception for all xa11y errors.
PermissionDeniedError Accessibility permissions have not been granted.
AccessibilityNotEnabledError The target app advertises an accessibility tree but it is empty.
SelectorNotMatchedError No element in the tree matched the given selector.
ActionNotSupportedError The requested action is not supported on the target element.
TimeoutError An operation exceeded its timeout.
InvalidSelectorError The selector string has invalid syntax.
InvalidActionDataError An action received invalid data (e.g. Locator.nth(0), a
PlatformError An OS-level accessibility error occurred.