rst – Convert items to rst format

This module provides the classes to convert test results to reStructuredText format. reStructuredText is a text markup language meant for easy human consumption. The reStructuredText primer provides a good introduction to its syntax.

class valjean.javert.rst.Rst(representation, *, n_workers=None)[source]

Class to convert TestResult objects into reStructuredText format.

__init__(representation, *, n_workers=None)[source]

Initialize a class instance with the given representation.

Parameters:

representation (Representation) – A representation.

clear()[source]

Clear the content of self.

format_report(*, report, author, version)[source]

Format a report.

Most of the work is actually done in the format_report_rec method.

Parameters:

report (TestReport) – the report to format.

Returns:

the formatted report.

Return type:

FormattedRst

format_report_rec(*, report, tree)[source]

Recursively format report sections and populate the tree_dict and text_dict attributes.

Report sections, as represented by the TestReport class, are essentially trees, with TestResult objects playing the role of the tree leaves and TestReport objects playing the role of the tree nodes. This method performs a recursive, pre-order, depth-first traversal of the tree and fills the tree_dict and text_dict dictionaries with the information that is necessary to instantiate the final FormattedRst object.

Given a report object, this method constructs a dictionary key by concatenating in a tuple the titles of the parent reports and the present report. The tuple containing the parent reports’ titles is recursively passed as the tree argument (and it is initialized to the empty tuple in the call from format_report). For instance, consider the following simplified report structure:

>>> from valjean.javert.test_report import TestReport
>>> subsub1 = TestReport(title='SubSub1')
>>> subsub2 = TestReport(title='SubSub2')
>>> sub1 = TestReport(title='Sub1', content=[subsub1, subsub2])
>>> sub2 = TestReport(title='Sub2')
>>> main = TestReport(title='Main', content=[sub1, sub2],
...                   text='Si six scies scient six cyprès, '
...                        'six-cent-six scies scient '
...                        'six-cent-six cyprès.')

We instantiate an Rst object and we format the report:

>>> import valjean.javert.representation as rpr
>>> from valjean.javert.rst import Rst
>>> rst = Rst(rpr.Representation(rpr.FullRepresenter()))
>>> formatted_report = rst.format_report(report=main, author='Me',
...                                      version='0.1')

Let us look at the dictionary keys:

>>> print(sorted(rst.text_dict.keys()))
[(), ('Sub1',), ('Sub1', 'SubSub1'), ('Sub1', 'SubSub2'), ('Sub2',)]

The empty tuple, (), is associated to the main report. The other keys represent subreports. The length of the tuple corresponds to the depth of the nested report; for instance, the 'SubSub2' section appears in the ('Sub1', 'SubSub2') tuple with length 2, because 'SubSub2' is nested twice (Main/Sub1/SubSub2).

The values of the text_dict dictionary are lists of strings representing the text of the given section. For instance, here is what the main section looks like:

>>> print('\n'.join(rst.text_dict[()]))
Main
====

Si six scies scient six cyprès, six-cent-six scies scient six-cent-six cyprès.

The tree_dict dictionary associates a tuple representing a report section to the list of tuples that represent the subsections:

>>> print(rst.tree_dict[()])
[('Sub1',), ('Sub2',)]
>>> print(rst.tree_dict[('Sub1',)])
[('Sub1', 'SubSub1'), ('Sub1', 'SubSub2')]

If a section does not have any subsection, the corresponding tuple does not appear in the dictionary:

>>> ('Sub2',) in rst.tree_dict
False

The tree_dict and text_dict dictionaries contain most of the information required by FormattedRst to write out the report in the form of a reStructuredText file tree (see write).

format_section(section, *, depth)[source]

Format a report section.

Parameters:

report (TestReport) – the report section to format.

Returns:

the formatted report section.

Return type:

str

format_result(result)[source]

Format one test result.

Parameters:

result (TestResult) – A TestResult.

Returns:

the formatted test result.

Return type:

str

class valjean.javert.rst.RstFormatter[source]

Class that dispatches the task of formatting templates as reStructuredText. The concrete formatting is handled by separate classes (RstTable…).

header(name, depth)[source]

Produce the header for formatting a TestResult.

Parameters:
  • name (str) – A header name

  • depth (int) – the depth of this header, 0 being the top.

Returns:

the test header, for inclusion in a reST document.

Return type:

str

text(text)[source]

Format some text.

Parameters:

text (str) – some text to include.

Returns:

the text, in rst format.

Return type:

str

static anchor(fingerprint)[source]

Format an anchor with the given fingerprint.

static format_tabletemplate(table)[source]

Format a TableTemplate.

Parameters:

table (TableTemplate) – A table.

Returns:

the reST table.

Return type:

RstTable

static format_plottemplate(plot)[source]

Format a PlotTemplate.

Parameters:

plot (PlotTemplate) – A plot.

Returns:

the formatted plot

Return type:

RstPlot

static format_texttemplate(text)[source]

Format a TextTemplate.

Parameters:

text (TextTemplate) – A text with its highlight positions

Returns:

the formatted text (text itself)

Return type:

str

class valjean.javert.rst.RstTable(table, num_fmt='{:11.6g}')[source]

Convert a TableTemplate into a reStructuredText table.

__init__(table, num_fmt='{:11.6g}')[source]

Construct an RstTable from the given TableTemplate.

Parameters:
  • table (TableTemplate) – The table to convert.

  • num_fmt (str) – A format string to specify how numerical table entries should be represented. The default value for this option is '{:11.6g}'.

__str__()[source]

Yield the table, as a reST string. This is probably the method that you want to call.

classmethod tabularize(headers, rows, *, indent=0)[source]

Transform a list of headers and a list of rows into a nice reST table.

The headers argument must be a list of strings. The rows argument represents the table rows, as a list of lists of strings. Each sublist represents a table row, and it must have the same length as headers, or terrible things will happen.

The column widths are automatically computed to accommodate the largest template in each column. Smaller templates are automatically right-justified.

>>> headers = ['name', 'quest', 'favourite colour']
>>> rows = [['Lancelot', 'to seek the Holy Grail', 'blue'],
...         ['Galahad', 'to seek the Holy Grail', 'yellow']]
>>> table = RstTable.tabularize(headers, rows)
>>> print(table)
========  ======================  ================
  name            quest           favourite colour
========  ======================  ================
Lancelot  to seek the Holy Grail              blue
 Galahad  to seek the Holy Grail            yellow
========  ======================  ================

You can also indent the table by a given amount of spaces with the indent keyword argument:

>>> table = RstTable.tabularize(headers, rows, indent=4)
>>> print(table)
    ========  ======================  ================
      name            quest           favourite colour
    ========  ======================  ================
    Lancelot  to seek the Holy Grail              blue
     Galahad  to seek the Holy Grail            yellow
    ========  ======================  ================
Parameters:
Returns:

The reST table, as a string.

static transpose(columns)[source]

Given a matrix as a list of columns, yield the matrix rows. (Equivalently, if the matrix is given as a list of rows, yield the columns). For instance, consider the following list:

>>> matrix = [(11, 21, 31), (12, 22, 32), (13, 23, 33)]
>>> for column in matrix:
...     print(' '.join(str(elem) for elem in column))
11 21 31
12 22 32
13 23 33

If the tuples are interpreted as columns, matrix represents the following matrix:

\begin{pmatrix}11&12&13\\21&22&23\\31&32&33\end{pmatrix}

Transposing it yields:

>>> transposed = RstTable.transpose(matrix)
>>> for row in transposed:
...     print(' '.join(str(elem) for elem in row))
11 12 13
21 22 23
31 32 33

Note

The matrix elements returned by transpose are actually 0-dimensional numpy.ndarray objects. For the purpose of their further manipulation this is of little consequence, as they transparently support most numerical operations.

Parameters:

columns (list(list) or list(tuple) or list(numpy.ndarray)) – An iterable yielding columns

Raises:

ValueError – if the columns do not have the same length.

Returns:

a generator for the rows of the transposed matrix.

classmethod format_columns(columns, highlights, num_fmt)[source]

Transform a bunch of columns (containing arbitrary data: floats, bools, ints, strings…) into an iterable of lists of (optionally highlighted) strings representing the table rows.

Example:

>>> cols = [('European swallow', 'African swallow'),
...         (27.7, 35.1),
...         ('km/h', 'km/h')]
>>> highs = [[False, False], [False, True], [False, True]]
>>> for row in RstTable.format_columns(cols, highs, '{:13.8f}'):
...     print(row)
['European swallow', '  27.70000000', 'km/h']
['African swallow', ':hl:`35.10000000`', ':hl:`km/h`']
Parameters:
  • columns – An iterable yielding columns. The table columns may contain any kind of data; if the data type is numeric, it will be formatted according to the num_fmt argument; other types will just get stringified (str).

  • highlights – An iterable yielding a collection of bools for each column. The j-th element of the i-th iteration decides whether the element in the j-th column of the i-th row should be highlighted.

  • num_fmt – The format string to be used for numerical types.

Returns:

a generator yielding lists of strings, representing the table rows.

static compute_column_widths(headers, rows)[source]

Compute the width of the columns that are necessary to accommodate all the headers and table rows.

>>> headers = ['swallow sub-species', 'airspeed', 'laden']
>>> rows = [['European', '27.7 km/h', 'no'],
...         ['European', '22.0 km/h', 'yes'],
...         ['African', '35.1 km/h', 'no'],
...         ['African', '26.2 km/h', 'yes']]
>>> RstTable.compute_column_widths(headers, rows)
[19, 9, 5]
>>> [len('swallow sub-species'), len('27.7 km/h'), len('laden')]
[19, 9, 5]
Parameters:
  • headers (list(str)) – A list of column headers.

  • rows (list(list(str))) – A list of table rows.

Returns:

a list of integers indicating how wide each columns must be to accommodate all the table elements and the headers.

Return type:

list(int)

classmethod highlight(val, flag)[source]

Wrap val in highlight reST markers if flag is True.

>>> RstTable.highlight('dis', False)
'dis'
>>> RstTable.highlight('DAT', True)
':hl:`DAT`'
classmethod concat_rows(widths, rows, just='>')[source]

Concatenate the given rows and justify them according to the just parameter (see the Format Specification Mini-Language). The columns widths (for justification) must be provided using the width argument.

Parameters:
  • widths (list(int)) – The list of columns widths.

  • rows (list(list(str))) – The list of rows; each row must be a list of strings.

  • just (str) – A format character for the justification (usually one of ‘<’, ‘^’, ‘>’).

Returns:

the concatenated, justified rows.

Return type:

str

class valjean.javert.rst.RstPlot(plot)[source]

This class models a plot in an reStructuredText document. It converts a PlotTemplate object into an MplPlot, and it provides the .. image:: directive to include in the .rst file.

__init__(plot)[source]
__str__()[source]

Return str(self).

filename()[source]

Make up a(n almost) unique filename for this plot.

Returns:

the filename from fingerprint (png format)

Return type:

str

class valjean.javert.rst.RstText(text)[source]

Construct an RstText from the given TextTemplate.

__init__(text)[source]
__str__()[source]

Return str(self).

class valjean.javert.rst.FormattedRst(*, author, title, version, tree_dict, text_dict, plots, n_workers=None)[source]

This class represents a formatted rst document tree, which typically consists of an index file and of several sections.

__init__(*, author, title, version, tree_dict, text_dict, plots, n_workers=None)[source]

Create a FormattedRst object. The author, title and version arguments are expected to be strings and are self-explanatory.

The tree_dict and text_dict arguments must be dictionaries. The tree_dict dictionary represents the tree structure of the reStructuredText document, and the text_dict represent the contents of each section. The report sections, which appear as keys in both dictionaries, are expected to be tuples of strings, with each string representing an additional layer in the document hierarchy. The section contents (the values of text_dict) are expected to be lists of strings to be written to disk.

Finally, the plots argument is a list of the plots referenced by the text sections. The plots will be written to disk with filenames of the form plot_fingerprint.png, where fingerprint is the plot fingerprint.

Parameters:
  • author (str) – the author of this report.

  • title (str) – the title of this report.

  • version (str) – the version number for this report.

  • tree_dict (dict) – dictionary mapping tuples of sections to lists of tuples of sections.

  • text_dict (dict) – dictionary mapping tuples of sections to lists of strings.

  • plots (list(MplPlot)) – list of plots to be written to disk.

  • n_workers (int or None) – number of subprocesses to use to write out the plots in parallel. If None is given, write the plots in sequential mode.

write(path)[source]

Write the text files and the plots into the directory specified by path.

Parameters:

path (str or pathlib.Path) – path to the directory that will be written to. It is OK if the directory does not exist.

static tree_to_path(*, base, tree)[source]

Convert a tree to a file path.

Parameters:
  • base (pathlib.Path) – the base path for all subtrees.

  • tree (tuple(str)) – a sequence of tree nodes, starting from the tree root.

toc(toc_title, subtrees)[source]

Build an rst table of contents.

Parameters:

toc_title (str) – the title for the table of contents (e.g. 'Contents').

setup(path)[source]

Set up the output directory for write.

Parameters:

path (pathlib.Path) – the path to the directory.

static configure(resource, dest, formatting=True, **kwargs)[source]

Copy a package resource to the specified destination, optionally formatting the resource content using str.format.

For more information about resources, see pkg_resources.

Parameters:
  • resource (str) – the name of the resource.

  • dest (pathlib.Path) – the destination path.

  • formatting (bool) – whether formatting should be applied.

  • kwargs (dict) – any additional keyword arguments will be passed to the formatting.

class valjean.javert.rst.RstTestReportTask(name, *, report_task, representation, author, version, deps=None, soft_deps=None)[source]

Task class that transforms a list of tests into a test report. TestResult objects in the environment.

classmethod from_tasks(name, *, make_report, eval_tasks, representation, author, version, kwargs=None, deps=None, soft_deps=None)[source]

Construct an RstTestReportTask from a list of test evaluation tasks and a function to classify test results and put them in test reports.

__init__(name, *, report_task, representation, author, version, deps=None, soft_deps=None)[source]

Initialize the task with a function, a tuple of arguments and a dictionary of kwargs.

Parameters:
  • name (str) – The name of the task.

  • func – A function to be executed.

  • args (tuple) – A tuple of positional arguments to func, or None if none are required.

  • kwargs (dict) – A dictionary of keyword arguments to func, or None if none are required.

  • env_kwarg (str) – The name of the keyword argument that will be used to pass the environment to the function, or None if the environment should not be passed.

  • config_kwarg (str) – The name of the keyword argument that will be used to pass the config to the function, or None if the config should not be passed.

  • deps (None or collection of Task objects.) – If this task depends on other tasks (and valjean cannot automatically discover this), pass them (as a list) to the deps parameter.

  • soft_deps (None or collection of Task objects.) – If this task has a soft dependency on other tasks (and valjean cannot automatically discover this), pass them (as a list) to the soft_deps parameter.