Testing Helpers¶
Rendering¶
- urwid.testing.render_text(widget: AbstractWidget, size: _Size, *, focus: bool = False, strip: bool = False) list[str]¶
Render widget and return the text of each row.
>>> from urwid import Pile, Text >>> render_text(Text("ab cd"), (3,)) ['ab ', 'cd '] >>> render_text(Text("ab cd"), (3,), strip=True) ['ab', 'cd'] >>> render_text(Pile([Text("x")]), ()) ['x']
- Parameters:
widget – widget to render
size – size to render widget with:
(),(cols,)or(cols, rows)focus – whether widget is rendered in focus
strip – remove trailing whitespace from every row
- Returns:
one string per canvas row, decoded with the target encoding.
- urwid.testing.render_runs(widget: AbstractWidget, size: _Size, *, focus: bool = False) list[list[tuple[Hashable, str]]]¶
Render widget and return each row as runs of display attribute and text.
Adjacent runs with the same attribute are merged, so the result does not depend on where the canvas splits runs for character sets or run-length encoding.
>>> from urwid import Text >>> render_runs(Text([("bold", "Hi"), "!"]), (3,)) [[('bold', 'Hi'), (None, '!')]]
- Parameters:
widget – widget to render
size – size to render widget with:
(),(cols,)or(cols, rows)focus – whether widget is rendered in focus
- Returns:
one list of
(attr, text)pairs per canvas row, the text decoded with the target encoding.
- urwid.testing.resolve_attr(attr: Hashable, palette: Iterable[PaletteEntry], *, colors: int = 256) AttrSpec¶
Return the
AttrSpeca screen with palette draws display attribute attr with.>>> resolve_attr("bold", [("bold", "dark red,bold", "default")], colors=16) AttrSpec('dark red,bold', 'default')
- Parameters:
attr – display attribute of a canvas run, such as one returned by
render_runs()palette – palette entries, as passed to
MainLoopcolors – number of colors of the screen: 1, 16, 88, 256 or
2**24
- Returns:
the attribute the screen resolves attr to; see
urwid.BaseScreen.resolve_attr().- Raises:
ValueError – colors is not one of the supported numbers of colors.
- urwid.testing.resolve_runs(runs: Iterable[Iterable[tuple[Hashable, str]]], palette: Iterable[PaletteEntry], *, colors: int = 256) list[list[tuple[AttrSpec, str]]]¶
Resolve the attribute of every run returned by
render_runs(), keeping the text.Runs are not merged again, although two attributes may resolve to the same
AttrSpec.>>> from urwid import Text >>> runs = render_runs(Text([("bold", "Hi"), "!"]), (3,)) >>> resolve_runs(runs, [("bold", "dark red,bold", "default")], colors=16) [[(AttrSpec('dark red,bold', 'default'), 'Hi'), (AttrSpec('default', 'default'), '!')]]
- Parameters:
runs – rows of
(attr, text)pairspalette – palette entries, as passed to
MainLoopcolors – number of colors of the screen: 1, 16, 88, 256 or
2**24
- Returns:
rows of
(AttrSpec, text)pairs, in the shape of runs.- Raises:
ValueError – colors is not one of the supported numbers of colors.
Sizing checks¶
- urwid.testing.check_sizing(widget: AbstractWidget, *, cols: int = 80, rows: int = 24) None¶
Run the sizing check for each sizing mode in
widget.sizing().Warning
The check empties the canvas cache before every rendered case, so it is for tests, not for a running application.
>>> from urwid import Pile, Text >>> check_sizing(Pile([Text("x")]))
- Parameters:
widget – widget to check
cols – number of columns, passed to
check_box_sizing()andcheck_flow_sizing()rows – number of rows, passed to
check_box_sizing()
- Raises:
AssertionError –
widget.sizing()is empty, or one of the checks fails.
- urwid.testing.check_box_sizing(widget: AbstractWidget, *, cols: int = 80, rows: int = 24) None¶
Check that the box widget widget renders canvases of the requested size, including degenerate sizes.
The sizes
(cols, 0),(cols, 1),(0, rows),(1, rows)and(cols, rows)are rendered with focus off and on. Each canvas must have the requested columns and rows, and a cursor, when set, must lie inside it.Warning
The check empties the canvas cache before every rendered case, so it is for tests, not for a running application.
>>> from urwid import Filler, Text >>> check_box_sizing(Filler(Text("x")))
- Parameters:
widget – box widget to check
cols – number of columns of the full size
rows – number of rows of the full size
- Raises:
AssertionError – a canvas has the wrong size or its cursor lies outside it.
- urwid.testing.check_flow_sizing(widget: AbstractFlowWidget, *, cols: int = 80) None¶
Check that the flow widget widget renders as many rows as its
rows()reports.The widths 0, 1 and cols are rendered with focus off and on. Each canvas must have the requested columns and the rows
widget.rows()returned before the render; in focus, its cursor must equalwidget.get_cursor_coords()when the widget has that method; a cursor, when set, must lie inside it.Warning
The check empties the canvas cache before every rendered case, so it is for tests, not for a running application.
>>> from urwid import Edit >>> check_flow_sizing(Edit("> ", "abc"))
- Parameters:
widget – flow widget to check
cols – number of columns of the full width
- Raises:
AssertionError – a canvas has the wrong size or a cursor that does not match the widget.
- urwid.testing.check_fixed_sizing(widget: AbstractWidget) None¶
Check that the fixed widget widget renders a canvas of the size its
pack(())reports.The widget is rendered with focus off and on; a cursor, when set, must lie inside the canvas.
Warning
The check empties the canvas cache before every rendered case, so it is for tests, not for a running application.
>>> from urwid import Text >>> check_fixed_sizing(Text("x"))
- Parameters:
widget – fixed widget to check
- Raises:
AssertionError – a canvas size differs from
pack(())or its cursor lies outside it.
Input¶
- urwid.testing.press(widget: AbstractWidget, size: _Size, *keys: str) list[str]¶
Send keys to
widget.keypress()in order and return the keys it did not handle.The returned key is collected, not the sent one, since a widget may translate a key.
>>> from urwid import Edit >>> edit = Edit("> ") >>> press(edit, (10,), "a", "b", "esc") ['esc'] >>> edit.edit_text 'ab'
- Parameters:
widget – widget to send the keys to
size – size passed to
keypress()keys – key names, such as
"a"or"enter"
- Returns:
the keys
keypress()returned, in order.
- urwid.testing.click(widget: AbstractWidget, size: _Size, col: int, row: int, *, button: int = 1, event: str = 'mouse press', focus: bool = True) bool¶
Send one mouse event to
widget.mouse_event()and return whether it was handled.>>> from urwid import Button >>> click(Button("OK"), (10,), 1, 0) True
- Parameters:
widget – widget to send the event to
size – size passed to
mouse_event()col – column of the event, relative to the widget
row – row of the event, relative to the widget
button – mouse button number
event – event name, such as
"mouse press"or"mouse release"focus – whether widget is in focus
- Returns:
Truewhenmouse_event()returned a true value.
Harness screen¶
- class urwid.testing.ScriptedScreen(*, size: tuple[int, int] = (80, 24), inputs: Iterable[Iterable[_Input]] = ())¶
Screen without a terminal that records what is drawn and feeds scripted input to
MainLoop.MainLoop(widget, palette, screen=ScriptedScreen(...))works withrun(), which feeds one batch of inputs after every redraw and stops withExitMainLooponce they are used up, and withprocess_input()plusdraw_screen()called by hand.A draw whose size, content (text and attributes) and cursor equal those of the last recorded frame is not recorded again, so
canvasesis the same sequence of distinct screens under every event loop, while the number of draws differs between them. Assert the frames andlast_text, not a count of draws.The input batches are consumed, so one screen scripts one
run(). Alarms still pending after the last batch never fire.>>> from urwid import Edit, Filler, MainLoop >>> screen = ScriptedScreen(size=(6, 1), inputs=[["a", "b"], ["backspace"]]) >>> MainLoop(Filler(Edit("> ")), screen=screen).run() >>> [frame.decoded_text for frame in screen.canvases] [('> ',), ('> ab ',), ('> a ',)] >>> screen.last_text ['> a ']
Create an unstarted screen.
- Parameters:
size –
(cols, rows)returned byget_cols_rows(); assignsizeto resize, then script a"window resize"keyinputs – batches of keys and mouse event tuples, one batch returned by each
get_input()
- Raises:
TypeError – a batch is not a sequence of keys and mouse event tuples.
- draw_count¶
number of
draw_screen()calls, recorded or not
- draw_screen(size: tuple[int, int], canvas: Canvas) None¶
Record canvas unless it shows the same frame as the last one, then schedule the next input batch.
- Parameters:
size –
(cols, rows)the canvas was rendered forcanvas – canvas to show
- get_input(raw_keys: Literal[False] = False) list[_Input]¶
- get_input(raw_keys: Literal[True]) tuple[list[_Input], list[int]]
Return the next input batch.
- Parameters:
raw_keys – also return the raw key codes, which are always empty
- Returns:
the batch, or
(batch, [])when raw_keys is true.- Raises:
ExitMainLoop – the scripted batches are used up.
- hook_event_loop(event_loop: EventLoop, callback: Callable[[list[_Input], list[int]], Any]) None¶
Feed the input batches to callback through alarms of event_loop, one after every draw.
- Parameters:
event_loop – event loop to schedule the batches on
callback – function taking the keys and the raw key codes of one batch
- property last_text: list[str]¶
Return the text of each row of the frame drawn last.
- Raises:
IndexError – nothing has been drawn yet.
- size¶
(cols, rows)returned byget_cols_rows()
- unhook_event_loop(event_loop: EventLoop) None¶
Cancel a pending input batch and forget the event loop.
- Parameters:
event_loop – event loop passed to
hook_event_loop()