Document Printer

A Wadler-Leijen Pretty Printer in Python.

Documents

class doc_printer.doc.Doc

The abstract class for all documents.

doc_printer.doc.DocLike

DocLike is an alias for any type which can be coerced into a document:

  • a string;

  • a document;

  • a sequence of things that can be coerced into documents.

Strings are converted using Text.lines(). alias of str | Doc | Iterable[DocLike] | None

Basic tokens

Text is the type for all concrete tokens in the document.

Text tokens are assumed to be free from whitespaces and newlines, but the constructor cannot guarantee that. The safe way to construct tokens from unknown strings is to use Text.lines().

class doc_printer.doc.Text(text: str)

A single line of text.

The type of string tokens.

classmethod lines(text: str, *, collapse_whitespace: bool = False) Doc
classmethod words(text: str, *, collapse_whitespace: bool = False) Doc

There are several singleton instances of Text:

doc_printer.doc.Empty: Text = Empty

A single line of text.

The empty document.

doc_printer.doc.Space: Text = Space

A single line of text.

Encodes all whitespace in the document.

doc_printer.doc.Line: Text = Line

A single line of text.

Encodes all newlines in the document.

Concatenating documents

Two documents can be combined as doc1 / doc2 or, separated by a space, as doc1 // doc2.

Doc.__truediv__(other: str | Doc | Iterable[DocLike] | None) Doc

Compose two documents.

Doc.__floordiv__(other: str | Doc | Iterable[DocLike] | None) Doc

Compose two documents, separated by a space.

There are corresponding implementations of __rtruediv__ and __rfloordiv__, so that either the left or the right argument to the operator can be a string.

These methods are implemented in terms of Doc.then().

Doc.then(other: str | Doc | Iterable[DocLike] | None) Doc

Compose two documents.

A stream of documents can be combined with a separator using Doc.join().

Doc.join(*others: str | Doc | Iterable[DocLike] | None) Doc

Compose a series of documents separated by this document.

All of these methods are implemented in terms of cat().

doc_printer.doc.cat(*doclike: str | Doc | Iterable[DocLike] | None) Doc

Concatenate a series of documents or document-like objects.

NOTE: cat and Empty form a monoid, where Empty acts as a unit for cat

Smart constructor for Cat

class doc_printer.doc.Cat(docs: Tuple[Doc, ...])

Concatenated documents.

The type of concatenated documents.

Parentheses

doc_printer.doc.parens(*doclike: str | Doc | Iterable[DocLike] | None) Doc
doc_printer.doc.brackets(*doclike: str | Doc | Iterable[DocLike] | None) Doc
doc_printer.doc.braces(*doclike: str | Doc | Iterable[DocLike] | None) Doc
doc_printer.doc.angles(*doclike: str | Doc | Iterable[DocLike] | None) Doc
doc_printer.doc.single_quote(*doclike: str | Doc | Iterable[DocLike] | None, auto_escape: bool = True, auto_unescape: bool = True) Doc
doc_printer.doc.double_quote(*doclike: str | Doc | Iterable[DocLike] | None, auto_escape: bool = True, auto_unescape: bool = True) Doc
doc_printer.doc.smart_quote(*doclike: str | Doc | Iterable[DocLike] | None) Doc

Alternative layout options

Two alternative layouts can be combined as alt1 | alt2.

Doc.__or__(other: str | Doc | Iterable[DocLike] | None) Doc

Combine two documents as alternatives.

When alternatives are combined, it’s important that the alternative whose first line is the shortest comes first.

There is a corresponding implementations of __ror__, so that either the left or the right argument to the operator can be a string.

This method is implemented in terms of alt().

doc_printer.doc.alt(*doclike: str | Doc | Iterable[DocLike] | None) Doc

Smart constructor for Alt

class doc_printer.doc.Alt(alts: Tuple[Doc, ...])

Alternatives for the document layout.

The type of alternate document layouts.

There are several singleton instances of Alt:

doc_printer.doc.Fail = Fail

Alternatives for the document layout.

The empty list of alternatives, meaning the current layout has failed.

doc_printer.doc.SoftLine = SoftLine

Alternatives for the document layout.

The optional newline, which lets the renderer know it is allowed to insert a newline in this place.

Identation

doc_printer.doc.nest(indent: int, *doclike: str | Doc | Iterable[DocLike] | None, overlap: bool = False) Doc

Smart constructor for Nest.

class doc_printer.doc.Nest(indent: int, doc: Doc, overlap: bool = False)

Indented documents.

The type of indented documents.

Alignment

Alignment is done via Row and Table. You can create tables using row() and table().

doc_printer.doc.row(*doclike: str | Doc | Iterable[DocLike] | None, table_type: str | None = None, hpad: str | Text = Space, hsep: str | Text = Space, min_col_widths: Tuple[int | None, ...] = ()) Doc

Smart constructor for Row.

doc_printer.doc.table(rows: Iterator[Row]) Doc

Smart constructor for Table.

Alternatively, you can merge existing rows into a table using create_tables().

doc_printer.doc.create_tables(docs: Iterator[Doc], *, separator: Text = Line) Iterator[Doc]

Merges sequences of rows with the same RowInfo.table_type into a table, and inserts it as an alternative into the document.

class doc_printer.doc.RowInfo(table_type: str | None, hpad: Text, hsep: Text, min_col_widths: Tuple[int | None, ...])
hpad: Text
hsep: Text
table_type: str | None
class doc_printer.doc.Row(cells: Tuple[Doc, ...], info: RowInfo)

The type of rows.

class doc_printer.doc.Table(rows: Tuple[Row, ...])

The type of tables

Rendering

Documents are rendered as a stream of doc_printer.doc.Token, which are joined into a string by doc_printer.abc.DocRenderer.to_str.

doc_printer.doc.Token: TypeAlias = <class 'doc_printer.doc.Text'>

A single line of text.

doc_printer.doc.TokenStream

alias of Iterator[Text]

class doc_printer.abc.DocRenderer
abstract render(doc: Doc) Iterator[Text]

Render a document as a stream of tokens.

to_str(doc: Doc) str

Rendering Naively

The simple renderer for documents ignores all alternatives, simply choosing the first alternative, which should have the shortest line length.

class doc_printer.simple.SimpleDocRenderer(simple_layout: doc_printer.simple.SimpleLayout = <SimpleLayout.ShortestLines: 0>, on_emit: List[Callable[[doc_printer.doc.Text], doc_printer.doc.Text]] = <factory>)
render_simple(doc: Doc) Iterator[Text]
render_simple(doc: Text) Iterator[Text]
render_simple(doc: Alt) Iterator[Text]
render_simple(doc: Cat) Iterator[Text]
render_simple(doc: Row) Iterator[Text]
render_simple(doc: Table) Iterator[Text]
render_simple(doc: Nest) Iterator[Text]
render_simple(doc: Edit) Iterator[Text]

Rendering Tables

Tables are cached, to calculate their column widths and insert the appropriate spacing.

To create these buffers, SimpleDocRenderer.buffer_table() and SimpleDocRenderer.buffer_row() are provided.

SimpleDocRenderer.buffer_table(table: Table) TableBuffer
SimpleDocRenderer.buffer_row(row: Row) RowBuffer

Table buffers are rendered using the TableBuffer.render().

It is important to call TableBuffer.update() once, just before rendering, as this calculates the needed column widths.

class doc_printer.table.TableBuffer
append(row: RowBuffer) None
extend(rows: Iterable[RowBuffer]) None
render() Iterator[Text]
update() None
class doc_printer.table.RowBuffer(hsep: Text, min_col_widths: Tuple[int | None, ...])
append(cell: CellBuffer) None
extend(cells: Iterable[CellBuffer]) None
render() Iterator[Text]
class doc_printer.table.CellBuffer(hpad: Text, width: int = 0)
append(token: Text) None
extend(tokens: Iterable[Text]) None
render(*, padding: bool = True) Iterator[Text]

Rendering with Lookahead

Finally, the smart renderer uses one level of lookahead.

When rendering a series of alternatives, it renders the first line of each alternative using the simple renderer, and picks the alternative that best fills up the current line.

class doc_printer.smart.SmartDocRenderer(simple_layout: doc_printer.simple.SimpleLayout = <SimpleLayout.ShortestLines: 0>, on_emit: List[Callable[[doc_printer.doc.Text], doc_printer.doc.Text]] = <factory>, max_line_width: int = 80)
render_with_lookahead(doc: Doc, *, width_hint: WidthHint = Unknown) Iterator[Text]
render_with_lookahead(doc: Cat, *, width_hint: WidthHint = Unknown) Iterator[Text]
render_with_lookahead(doc: Alt, *, width_hint: WidthHint = Unknown) Iterator[Text]