Skip to content

textual.pilot

This module contains the Pilot class used by App.run_test to programmatically operate an app.

See the guide on how to test Textual apps.

OutOfBounds

Bases: Exception

Raised when the pilot mouse target is outside of the (visible) screen.

Pilot

Pilot(app)

Bases: Generic[ReturnType]

Pilot object to drive an app.

app property

app

click async

click(
    widget=None,
    offset=(0, 0),
    shift=False,
    meta=False,
    control=False,
    times=1,
)

Simulate clicking with the mouse at a specified position.

The final position to be clicked is computed based on the selector provided and the offset specified and it must be within the visible area of the screen.

Implementation note: This method bypasses the normal event processing in App.on_event.

Example

The code below runs an app and clicks its only button right in the middle:

async with SingleButtonApp().run_test() as pilot:
    await pilot.click(Button, offset=(8, 1))

Parameters:

Name Type Description Default

widget

Widget | type[Widget] | str | None

A widget or selector used as an origin for the click offset. If this is not specified, the offset is interpreted relative to the screen. You can use this parameter to try to click on a specific widget. However, if the widget is currently hidden or obscured by another widget, the click may not land on the widget you specified.

None

offset

tuple[int, int]

The offset to click. The offset is relative to the widget / selector provided or to the screen, if no selector is provided.

(0, 0)

shift

bool

Click with the shift key held down.

False

meta

bool

Click with the meta key held down.

False

control

bool

Click with the control key held down.

False

times

int

The number of times to click. 2 will double-click, 3 will triple-click, etc.

1

Raises:

Type Description
OutOfBounds

If the position to be clicked is outside of the (visible) screen.

Returns:

Type Description
bool

True if no selector was specified or if the click landed on the selected widget, False otherwise.

double_click async

double_click(
    widget=None,
    offset=(0, 0),
    shift=False,
    meta=False,
    control=False,
)

Simulate double clicking with the mouse at a specified position.

Alias for pilot.click(..., times=2).

The final position to be clicked is computed based on the selector provided and the offset specified and it must be within the visible area of the screen.

Implementation note: This method bypasses the normal event processing in App.on_event.

Example

The code below runs an app and double-clicks its only button right in the middle:

async with SingleButtonApp().run_test() as pilot:
    await pilot.double_click(Button, offset=(8, 1))

Parameters:

Name Type Description Default

widget

Widget | type[Widget] | str | None

A widget or selector used as an origin for the click offset. If this is not specified, the offset is interpreted relative to the screen. You can use this parameter to try to click on a specific widget. However, if the widget is currently hidden or obscured by another widget, the click may not land on the widget you specified.

None

offset

tuple[int, int]

The offset to click. The offset is relative to the widget / selector provided or to the screen, if no selector is provided.

(0, 0)

shift

bool

Click with the shift key held down.

False

meta

bool

Click with the meta key held down.

False

control

bool

Click with the control key held down.

False

Raises:

Type Description
OutOfBounds

If the position to be clicked is outside of the (visible) screen.

Returns:

Type Description
bool

True if no selector was specified or if the clicks landed on the selected widget, False otherwise.

exit async

exit(result)

Exit the app with the given result.

Parameters:

Name Type Description Default

result

ReturnType

The app result returned by run or run_async.

required

hover async

hover(widget=None, offset=(0, 0))

Simulate hovering with the mouse cursor at a specified position.

The final position to be hovered is computed based on the selector provided and the offset specified and it must be within the visible area of the screen.

Parameters:

Name Type Description Default

widget

Widget | type[Widget] | str | None | None

A widget or selector used as an origin for the hover offset. If this is not specified, the offset is interpreted relative to the screen. You can use this parameter to try to hover a specific widget. However, if the widget is currently hidden or obscured by another widget, the hover may not land on the widget you specified.

None

offset

tuple[int, int]

The offset to hover. The offset is relative to the widget / selector provided or to the screen, if no selector is provided.

(0, 0)

Raises:

Type Description
OutOfBounds

If the position to be hovered is outside of the (visible) screen.

Returns:

Type Description
bool

True if no selector was specified or if the hover landed on the selected widget, False otherwise.

mouse_down async

mouse_down(
    widget=None,
    offset=(0, 0),
    shift=False,
    meta=False,
    control=False,
)

Simulate a MouseDown event at a specified position.

The final position for the event is computed based on the selector provided and the offset specified and it must be within the visible area of the screen.

Parameters:

Name Type Description Default

widget

Widget | type[Widget] | str | None

A widget or selector used as an origin for the event offset. If this is not specified, the offset is interpreted relative to the screen. You can use this parameter to try to target a specific widget. However, if the widget is currently hidden or obscured by another widget, the event may not land on the widget you specified.

None

offset

tuple[int, int]

The offset for the event. The offset is relative to the selector / widget provided or to the screen, if no selector is provided.

(0, 0)

shift

bool

Simulate the event with the shift key held down.

False

meta

bool

Simulate the event with the meta key held down.

False

control

bool

Simulate the event with the control key held down.

False

Raises:

Type Description
OutOfBounds

If the position for the event is outside of the (visible) screen.

Returns:

Type Description
bool

True if no selector was specified or if the event landed on the selected widget, False otherwise.

mouse_up async

mouse_up(
    widget=None,
    offset=(0, 0),
    shift=False,
    meta=False,
    control=False,
)

Simulate a MouseUp event at a specified position.

The final position for the event is computed based on the selector provided and the offset specified and it must be within the visible area of the screen.

Parameters:

Name Type Description Default

widget

Widget | type[Widget] | str | None

A widget or selector used as an origin for the event offset. If this is not specified, the offset is interpreted relative to the screen. You can use this parameter to try to target a specific widget. However, if the widget is currently hidden or obscured by another widget, the event may not land on the widget you specified.

None

offset

tuple[int, int]

The offset for the event. The offset is relative to the widget / selector provided or to the screen, if no selector is provided.

(0, 0)

shift

bool

Simulate the event with the shift key held down.

False

meta

bool

Simulate the event with the meta key held down.

False

control

bool

Simulate the event with the control key held down.

False

Raises:

Type Description
OutOfBounds

If the position for the event is outside of the (visible) screen.

Returns:

Type Description
bool

True if no selector was specified or if the event landed on the selected widget, False otherwise.

pause async

pause(delay=None)

Insert a pause.

Parameters:

Name Type Description Default

delay

float | None

Seconds to pause, or None to wait for cpu idle.

None

press async

press(*keys)

Simulate key-presses.

Parameters:

Name Type Description Default

*keys

str

Keys to press.

()

resize_terminal async

resize_terminal(width, height)

Resize the terminal to the given dimensions.

Parameters:

Name Type Description Default

width

int

The new width of the terminal.

required

height

int

The new height of the terminal.

required

triple_click async

triple_click(
    widget=None,
    offset=(0, 0),
    shift=False,
    meta=False,
    control=False,
)

Simulate triple clicking with the mouse at a specified position.

Alias for pilot.click(..., times=3).

The final position to be clicked is computed based on the selector provided and the offset specified and it must be within the visible area of the screen.

Implementation note: This method bypasses the normal event processing in App.on_event.

Example

The code below runs an app and triple-clicks its only button right in the middle:

async with SingleButtonApp().run_test() as pilot:
    await pilot.triple_click(Button, offset=(8, 1))

Parameters:

Name Type Description Default

widget

Widget | type[Widget] | str | None

A widget or selector used as an origin for the click offset. If this is not specified, the offset is interpreted relative to the screen. You can use this parameter to try to click on a specific widget. However, if the widget is currently hidden or obscured by another widget, the click may not land on the widget you specified.

None

offset

tuple[int, int]

The offset to click. The offset is relative to the widget / selector provided or to the screen, if no selector is provided.

(0, 0)

shift

bool

Click with the shift key held down.

False

meta

bool

Click with the meta key held down.

False

control

bool

Click with the control key held down.

False

Raises:

Type Description
OutOfBounds

If the position to be clicked is outside of the (visible) screen.

Returns:

Type Description
bool

True if no selector was specified or if the clicks landed on the selected widget, False otherwise.

wait_for_animation async

wait_for_animation()

Wait for any current animation to complete.

wait_for_scheduled_animations async

wait_for_scheduled_animations()

Wait for any current and scheduled animations to complete.

WaitForScreenTimeout

Bases: Exception

Exception raised if messages aren't being processed quickly enough.

If this occurs, the most likely explanation is some kind of deadlock in the app code.