byexample

Write snippets of code in your documentation and execute them as regression tests.

View project on GitHub

Terminal Emulation

byexample can use different emulation modes to process the output of the runners.

By default byexample uses a dumb terminal (+term=dumb) which is fast and works well in most cases.

But you can change this to disable emulation with +term=as-is or to enable full ANSI terminal emulation with +term=ansi.

Dumb terminal

In this mode, byexample emulates a very simple terminal, processing only the white spaces.

It does not have the concept of a cursor, does not interpret escape codes and does not break lines automatically.

Even if a geometry is defined with +geometry, the dumb terminal does not force any boundaries: the following example will print a string longer than the width of the terminal.

>>> print("aaaabbbb" * 8)      # byexample: +geometry 24x32
aaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbb

White space processing

The dumb terminal removes any trailing whitespace, converts tabs to spaces and standardizes the new lines.

This suits most examples, reducing the need to add tabs into the examples or requiring the normalization of whitespace with +norm-ws.

>>> print("\tfoo\tbar\nbaz\tsaz    \rtaz")
        foo     bar
baz     saz
taz

If you need to check whitespace characters, you can use +term=as-is to disable terminal emulation.

Escape/Control sequences filter

Since 11.0.0, byexample strips any escape and control sequence.

The following example prints a message partially in red using the special \033[31m and \033[0m sequences.

The dumb terminal will filter them by default:

$ echo -e "This is a \033[31mmessage in red\033[0m but not panic"
<...>This is a message in red but not panic

If you want the pre-11.0.0 behaviour, you can turn off the filtering:

$ echo -e "This is a \033[31mmessage in red\033[0m but not panic"      # byexample: -filter-esc-seqs
<...>This is a <...>[31mmessage in red<...>[0m but not panic

If filtering is not enough (the example’s output does not display well) you probably will need to use a full terminal emulation with +term=ansi.

In those cases byexample will print a hint about that:

$ byexample -l shell test/ds/cli.md     # byexample: +norm-ws +timeout 8
<...>
Failed example:
    echo -e "This is a broken \033[23;2Hmessage\033[23;77H"
<...>
- Escape/control sequences were detected. If the output looks
scrambled or dirty, you may try a full terminal emulation with
'+term=ansi'
<...>

As-is terminal

When +term=as-is is activated the output is passed as is without any modification except for standardization of new lines.

It can be useful in some special cases to check white spaces that are removed by the dumb or ANSI terminals.

>>> print("\tfoo\tbar\nbaz\tsaz    \rtaz")       # byexample: +term=as-is
	foo	bar
baz	saz    
taz

Following the message printed partially in red, the as-is terminal will not filter any escape/control sequence:

$ echo -e "This is a \033[31mmessage in red\033[0m but not panic"      # byexample: +term=as-is
<...>This is a <...>[31mmessage in red<...>[0m but not panic

ANSI terminal

Some programs may need a real terminal or at least an emulated ANSI terminal.

byexample can emulate one capable of interpreting and emulating all the control sequences and escape codes using +term=ansi.

A full emulation allows you to render correctly any output, not just filtering the escape/control sequences but interpreting it correctly.

$ echo -e "This is a \033[31mmessage in red\033[0m but not panic"      # byexample: +term=ansi
<...>This is a message in red but not panic

The ANSI terminal emulation comes with some reasonable but unexpected behaviours, some gotchas and a slightly slower performance.

Note: terminal emulation is not fully supported in Python 2.7. It should work but using a modern Python version is recommended.

Terminal boundaries

Keep in mind that an emulated terminal will honor its own boundaries or geometry: if an example prints a string longer than the width of the terminal, the string will spawn multiple lines (a newline is added automatically).

>>> print("aaaabbbb" * 8)      # byexample: +term=ansi +geometry 24x32
aaaabbbbaaaabbbbaaaabbbbaaaabbbb
aaaabbbbaaaabbbbaaaabbbbaaaabbbb

This is especially useful to work with ncurses or other advanced programs.

ncurses support

Some applications use advanced terminal features (like the ones that use ncurses) and require a terminal emulator.

Examples of this are programs like less, more, top and man.

$ less test/ds/python-tutorial.v2.md # byexample: +term=ansi +rm=  +stop-on-timeout
 This is a 101 Python tutorial
 The following is an example written in Python about arithmetics
 
     ```
     >>> from __future__ import print_function
     >>> 1 + 2
     3
     ```
 
 The next examples show you about complex numbers in Python
 
     ```
     >>> 2j * 2
     4j
 
     >>> 2j + 4j
     6j
     ```
 
 <...>(END)

Try the above example with +term=as-is and see what happens.

Pagination

byexample will not emulate pagination. When the output is larger than the height of the terminal, lines that scroll off the top are discarded.

The following example prints more lines than are available in the terminal so only the last lines are shown.

>>> for i in range(1,33):       # byexample: +term=ansi +geometry=5x80
...     print("line %i" % i)
line 29
line 30
line 31
line 32

If this is a problem change the geometry: increase the count of rows that the terminal has with +geometry.

Performance

Emulation is slightly slower than the normal mode (+term=dumb). Keep that in mind and try to not enable it by default.