Python API Reference
The xa11y Python package provides bindings to the xa11y Rust library via PyO3.
Install from PyPI:
pip install xa11yQuick 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()Module Functions
Section titled “Module Functions”xa11y.locator() → Locator
Section titled “xa11y.locator() → Locator”Create a top-level Locator searching from the system root.
| Parameter | Type | Default |
|---|---|---|
selector |
str |
xa11y.set_default_timeout() → None
Section titled “xa11y.set_default_timeout() → 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.
| Parameter | Type | Default |
|---|---|---|
timeout |
float |
xa11y.get_default_timeout() → float
Section titled “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
Section titled “xa11y.input_sim() → InputSim”Construct an InputSim backed by the platform’s native input path.
xa11y.screenshot() → Screenshot
Section titled “xa11y.screenshot() → 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.
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 |
Classes
Section titled “Classes”Locator
Section titled “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.
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
selector |
str |
The CSS-like selector string for this locator. |
Navigation
Section titled “Navigation”| 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. |
Inspection
Section titled “Inspection”| 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. |
Actions
Section titled “Actions”| Method | Returns | Description |
|---|---|---|
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, end: int) |
Select a text range within the matched element (0-based offsets). | |
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. |
Waiting
Section titled “Waiting”| 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_until(predicate: Callable[[Element | None], bool], timeout: float | None = None) |
Wait until an arbitrary predicate is satisfied. |
EventType
Section titled “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.
Constants
Section titled “Constants”| 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' |
|
ANNOUNCEMENT |
'announcement' |
An accessibility event delivered to subscribers.
Properties
Section titled “Properties”| 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. |
Subscription
Section titled “Subscription”A live event subscription.
Methods
Section titled “Methods”| 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.
Properties
Section titled “Properties”| 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. |
Methods
Section titled “Methods”| Method | Returns | Description |
|---|---|---|
by_name(name: str, *, timeout: float | None = None) |
App |
Find an application by exact name. |
by_pid(pid: int, *, timeout: float | None = None) |
App |
Find an application by process ID. |
find(predicate: Callable[[App], bool], *, timeout: float | None = None) |
App |
Find an application matching predicate. |
foreground(*, timeout: float | None = None) |
App |
Resolve the application that currently holds the system foreground. |
list() |
list[App] |
List all running applications. |
locator(selector: str) |
Locator |
Create a Locator scoped to this application’s accessibility tree. |
subscribe() |
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. |
as_element() |
Element |
Get an Element handle for the application root. |
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. |
ShellSurface
Section titled “ShellSurface”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.
Properties
Section titled “Properties”| 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 |
Methods
Section titled “Methods”| 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. |
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. |
Element
Section titled “Element”A live element with lazy navigation.
Properties
Section titled “Properties”| 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 |
|
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 |
|
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. |
Methods
Section titled “Methods”| Method | Returns | Description |
|---|---|---|
children() |
list[Element] |
Get direct children (lazy — each call queries the provider). |
parent() |
Element | None |
Get parent element (lazy — each call queries the provider). |
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() |
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, end: int) |
Select the text range from start to end (0-based offsets). |
|
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. |
InputSim
Section titled “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).
Methods
Section titled “Methods”| 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. dx positive → right, dy positive → down. |
|
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. |
LegendEntry
Section titled “LegendEntry”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”)
Properties
Section titled “Properties”| 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 |
Omission
Section titled “Omission”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.
Properties
Section titled “Properties”| 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 |
Screenshot
Section titled “Screenshot”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.
Properties
Section titled “Properties”| 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 |
Methods
Section titled “Methods”| 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. |
_TestActionProbe
Section titled “_TestActionProbe”Methods
Section titled “Methods”| Method | Returns | Description |
|---|---|---|
locator(selector: str) |
Locator |
|
actions() |
list[list[object]] |
|
clear() |
Data Classes
Section titled “Data Classes”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).
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
x |
int |
|
y |
int |
|
width |
int |
|
height |
int |
Exceptions
Section titled “Exceptions”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), or a |
PlatformError |
An OS-level accessibility error occurred. |