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
The target app advertises an accessibility tree but it is empty. |
|
The requested action is not supported on the target element. |
|
An action received invalid data (e.g. |
|
The selector string has invalid syntax. |
|
Accessibility permissions have not been granted. |
|
An OS-level accessibility error occurred. |
|
No element in the tree matched the given selector. |
|
An operation exceeded its timeout. |
|
Base exception for all xa11y errors. |
Classes
A running application — the entry point for accessibility queries. |
|
A live element with lazy navigation. |
|
An accessibility event delivered to subscribers. |
|
Accessibility event type constants. |
|
Input-simulation façade for synthesised pointer and keyboard events. |
|
A resilient element reference that re-queries on each interaction. |
|
A bounding rectangle in logical screen coordinates (device-independent |
|
A captured image: raw RGBA8 pixels plus dimensions and scale. |
|
A live event subscription. |
Functions
|
Get the effective process-wide default timeout, in seconds. |
|
Construct an |
|
Create a top-level Locator searching from the system root. |
|
Capture pixels from the screen. |
|
Set the process-wide default timeout, in seconds. |
Package Contents
- exception xa11y.AccessibilityNotEnabledError
Bases:
XA11yErrorThe 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 theACCESSIBILITY_ENABLED=1environment 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:
XA11yErrorThe requested action is not supported on the target element.
Initialize self. See help(type(self)) for accurate signature.
- exception xa11y.InvalidActionDataError
Bases:
XA11yErrorAn action received invalid data (e.g.
Locator.nth(0), or a text-selection range withstart > end).Initialize self. See help(type(self)) for accurate signature.
- exception xa11y.InvalidSelectorError
Bases:
XA11yErrorThe selector string has invalid syntax.
Initialize self. See help(type(self)) for accurate signature.
- exception xa11y.PermissionDeniedError
Bases:
XA11yErrorAccessibility permissions have not been granted.
Initialize self. See help(type(self)) for accurate signature.
- exception xa11y.PlatformError
Bases:
XA11yErrorAn OS-level accessibility error occurred.
Initialize self. See help(type(self)) for accurate signature.
- exception xa11y.SelectorNotMatchedError
Bases:
XA11yErrorNo 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
Nonefor this class (present for attribute parity withTimeoutError).
- 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:
XA11yErrorAn 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:
ExceptionBase 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
Elementhandle 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 viaset_default_timeout()or theXA11Y_DEFAULT_TIMEOUTenvironment variable. Passtimeout=0for 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 overApp.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. Seeby_namefortimeoutsemantics.
- 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 — callprint(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.predicateis called with anAppfor 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. Seeby_namefortimeoutsemantics. There is no need to hand-roll a poll overApp.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; seeby_namefortimeoutsemantics. The returned app hasis_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/findwith atimeoutinstead of polling this in a loop.
- 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, andchildren(a list of dicts with the same shape).max_depthlimits 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 aDeprecationWarning.
- property is_foreground: bool
Whether this application is the foreground application.
Named
is_foregroundbecause “focused” is reserved for element-level keyboard focus (Element.focused) elsewhere in the API; this is the foreground-application flag. Populated for apps obtained vialist()andfind(). A point-in-time snapshot taken when theAppwas 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. Useforeground()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.
- 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 — callprint(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).
- perform_action(action: str) None
Perform an action by
snake_casename.
- 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
starttoend(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 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, andchildren(a list of dicts with the same shape).max_depthlimits 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 thexa11y treeCLI 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);
Falseelsewhere. Distinct fromfocused, which is element-level keyboard focus.
- property busy: bool
- property checked: str | None
Tri-state toggle value:
'on','off','mixed', orNone.Nonemeans 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)isTruefor every non-Nonevalue, 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_changedevents: the flag that changed (e.g.'checked').Nonefor other event types.
- property state_value: bool | None
For
state_changedevents: the new boolean value of the flag.Nonefor other event types.
- 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 asElement.bounds), or anElement(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. UseInputSimfor gestures with no a11y equivalent (drag-and-drop, scroll wheels, global shortcuts).- chord(key: str, held: list[str] = ...) None
Tap
keywhile the keys inheldare held down.
- drag(start: tuple[int, int] | Element, end: tuple[int, int] | Element) None
Left-drag from
starttoend.
- move_to(target: tuple[int, int] | Element) None
Move the pointer to
targetwithout pressing any button.
- press(key: str) None
Tap a key (press + release).
- scroll(target: tuple[int, int] | Element, dx: int = 0, dy: int = 0) None
Scroll at
target.dxpositive → right,dypositive → 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 theXA11Y_DEFAULT_TIMEOUTenvironment variable).wait_*methods use the same default when no explicittimeout=is passed.- blur() None
Remove keyboard focus from the matched element.
- collapse() None
Collapse the matched element.
- count() int
Count matching elements.
- decrement() None
Decrement the matched element (slider, spinner).
- 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().
- exists() bool
Check if a matching element exists.
- expand() None
Expand the matched element.
- focus() None
Set keyboard focus on the matched element.
- increment() None
Increment the matched element (slider, spinner).
- 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 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, andchildren(a list of dicts with the same shape).max_depthlimits traversal:0= only this node,1= node + direct children,None= full subtree.Resolves the selector once; fails fast with
SelectorNotMatchedErrorif no match — does not auto-wait.
- type_text(text: str) None
Type text at the current cursor position on the matched element.
- wait_detached(timeout: float | None = None) None
Wait until the element is removed from the tree.
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 — seeset_default_timeout(). Applies to allwait_*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’sscale(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.
widthandheightare in physical pixels.scaleis the physical-to-logical ratio (1.0 on standard displays, 2.0 on typical Retina).pixelshas lengthwidth * height * 4.- save_png(path: str | bytes | object) None
Encode as PNG and write to
path. Acceptsstr,bytesoros.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
- xa11y.get_default_timeout() float
Get the effective process-wide default timeout, in seconds.
Resolution order: the
set_default_timeout()value, else theXA11Y_DEFAULT_TIMEOUTenvironment variable, else the built-in 5.0.
- 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
elementto capture the pixels under an element’s current bounds, orregionas(x, y, width, height)to capture an explicit rectangle in logical screen coordinates. Passing both raisesValueError.
- 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 explicittimeout=. An explicit per-calltimeout=always wins. Takes precedence over theXA11Y_DEFAULT_TIMEOUTenvironment variable (seconds, read once at import).Pass
0for “single attempt, no polling” semantics. RaisesValueErrorfor negative or non-finite values.