API¤
Pretty printing¤
The main two functions are wadler_lindig.pprint
and wadler_lindig.pformat
, to pretty-print (to stdout) or pretty-format (return a string) any Python object.
wadler_lindig.pprint(obj: Any, *, width: int = 88, indent: int = 2, short_arrays: bool = True, custom: Callable[[Any], None | AbstractDoc] = <function _none>, hide_defaults: bool = True, seen_ids: None | set[int] = None, **kwargs) -> None
¤
Pretty-prints an object to stdout.
Arguments:
obj
: the object to pretty-print.width
: a best-effort maximum width to allow. May be exceeded if there are unbroken pieces of text which are wider than this.indent
: when the contents of a structured type are too large to fit on one line, they will be indented by this amount and placed on separate lines.short_arrays
: whether to print a NumPy array / PyTorch tensor / JAX array as a short summary of the formf32[3,4]
(here indicating afloat32
matrix of shape(3, 4)
)custom
: a way to pretty-print custom types. This will be called on every object it . If its return isNone
then the default behaviour will be performed. If its return is anwadler_lindig.AbstractDoc
then that will be used instead.hide_defaults
: whether to show the default values of dataclass fields.seen_ids
: theid(...)
of any Python objects that have already been seen, and should not be further introspected to avoid recursion errors (e.g.x = []; x.append(x)
). Note that for efficiency, this argument will be mutated with the ids encountered.**kwargs
: all other unrecognized kwargs are forwarded on to any__pdoc__
methods encountered, as an escape hatch for custom behaviour.
Returns:
A string representing obj
.
Info
The behaviour of this function can be customised in two ways.
First, any object which implements a
__pdoc__(self, **kwargs) -> None | AbstractDoc
method will have that method
called to determine its pretty-doc.
Second, the custom
argument to this function can be used. This is particularly
useful to provide custom pretty-docs for objects provided by third-party
libraries. (For which you cannot add a __pdoc__
method.)
wadler_lindig.pformat(obj: Any, *, width: int = 88, indent: int = 2, short_arrays: bool = True, custom: Callable[[Any], None | AbstractDoc] = <function _none>, hide_defaults: bool = True, **kwargs) -> str
¤
As wadler_lindig.pprint
, but returns a string instead of printing to
stdout.
Pretty diffs¤
As a utility we offer wadler_lindig.pdiff
as a quick way to diff two strings. This is a thin wrapper around the Python built-in library difflib
.
wadler_lindig.pdiff(minus: str, plus: str) -> str
¤
Returns a pretty-diff between two strings.
(This is just a thin wrapper around the builtin difflib
, and is just here as a
helper for common use-cases.)
Example
minus = "hello\nthere\nobi wan kenobi"
plus = "hello\nthere\npatrick kidger"
print(wadler_lindig.pdiff(minus, plus))
# hello
# there
# - obi wan kenobi
# + patrick kidger
Arguments:
minus
: any lines unique to this string will be prefixed with a-
.plus
: any lines unique to this string will be prefixed with a+
.
Returns:
A diff between the two tsrings minus
and plus
, showing their shared lines once
and the unique lines from each.
Colours and ANSI escape codes¤
As a utility we offer some support for adding colours via ANSI escape codes, or to remove all ANSI escape codes from a string.
wadler_lindig.ansi_format(text: str, fg_colour: str, bold: bool) -> str
¤
Formats text
with a foreground colour fg_colour
, and optionally mark it
bold
, using ANSI colour codes.
wadler_lindig.ansi_strip(text: str) -> str
¤
Removes all ANSI codes from a string.
Wadler–Lindig documents¤
wadler_lindig.AbstractDoc
¤
Base class for all document types.
For more on the following shorthand methods, see the methods example.
__add__(self, other: AbstractDoc) -> ConcatDoc
¤
doc1 + doc2
offers a convenient shorthand for ConcatDoc(doc1, doc2)
.
nest(self, indent: int) -> NestDoc
¤
doc.nest(indent)
offers a convenient shorthand for
NestDoc(doc, indent)
.
group(self) -> GroupDoc
¤
doc.group()
offers a convenient shorthand for GroupDoc(doc)
.
wadler_lindig.BreakDoc (AbstractDoc)
¤
If in vertical mode then this is a valid place to insert a newline. If in
horizontal mode then self.text
will be displayed instead.
__init__(self, text: str)
¤
Arguments:
text
: the string of text to display if a newline is not inserted. Common values are" "
(for example between elements of a list) or""
(for example between the final element of a list and a closing ']').
wadler_lindig.ConcatDoc (AbstractDoc)
¤
Concatenate multiple documents together, to be displayed one after another.
If for example these consist only of TextDoc
s and other ConcatDoc
s then there is
no implied breaking between them, so the formatted text may exceed the maximum
width. You may wish to separate pieces with BreakDoc
s to indicate this, for
example.
__init__(self, *args, *, children = None)
¤
Arguments:
Can be called as any of:
ConcatDoc(doc1, doc2, doc3, ...)
ConcatDoc(children=(doc1, doc2, doc3, ...))
doc1 + doc2 + doc3 + ...
wadler_lindig.GroupDoc (AbstractDoc)
¤
Groups the parts of a child document to be laid out all horizontally together or all vertically together.
This decision will persist everywhere outside any child GroupDoc
s, within which
their own local rule is used. For example using [...]
to denote a grouping:
[
foo,
bar,
[baz, qux]
]
foo
, bar
and [baz, qux]
are laid out vertically, but the sub-group
[baz, qux]
is judged to have enough space, and so is laid out horizontally.
__init__(self, child: AbstractDoc)
¤
Arguments:
child
: the child document to display.
wadler_lindig.NestDoc (AbstractDoc)
¤
If in vertical mode, increase the indent after each newline by indent
whilst
displaying child
.
__init__(self, child: AbstractDoc, indent: int)
¤
Arguments:
child
: the child document to display.indent
: how much to increase the indent.
Frequently child
will be ConcatDoc(BreakDoc(""), another_doc)
, so that the first
line of another_doc
will be indented as much as its later lines. See also the
The (break-group).nest-break
example.
wadler_lindig.TextDoc (AbstractDoc)
¤
Represents an unbroken piece of text to display. May include newlines.
__init__(self, text: str)
¤
Arguments:
text
: the string of text.
wadler_lindig.pdoc(obj: Any, indent: int = 2, short_arrays: bool = True, custom: Callable[[Any], None | AbstractDoc] = <function _none>, hide_defaults: bool = True, seen_ids: None | set[int] = None, **kwargs) -> AbstractDoc
¤
Formats an object into a Wadler–Lindig document. Such documents are essentially strings that haven't yet been pretty-formatted to a particular width.
Arguments:
obj
: the object to pretty-doc.indent
: when the contents of a structured type are too large to fit on one line, they will be indented by this amount and placed on separate lines.short_arrays
: whether to print a NumPy array / PyTorch tensor / JAX array as a short summary of the formf32[3,4]
(here indicating afloat32
matrix of shape(3, 4)
)custom
: a way to pretty-doc custom types. This will be called on every object it encounters. If its return isNone
then the usual behaviour will be performed. If its return is anAbstractDoc
then that will be used instead.hide_defaults
: whether to show the default values of dataclass fields.seen_ids
: theid(...)
of any Python objects that have already been seen, and should not be further introspected to avoid recursion errors (e.g.x = []; x.append(x)
). Note that for efficiency, this argument will be mutated with the ids encountered.**kwargs
: all kwargs are forwarded on to all__pdoc__
calls, as an escape hatch for custom behaviour.
Returns:
A pretty-doc representing obj
.
Info
The behaviour of this function can be customised in two ways.
First, any object which implements a
__pdoc__(self, **kwargs) -> None | AbstractDoc
method will have that method
called to determine its pretty-doc.
Second, the custom
argument to this function can be used. This is particularly
useful to provide custom pretty-docs for objects provided by third-party
libraries. (For which you cannot add a __pdoc__
method yourself.)
Utilities¤
wadler_lindig.array_summary(shape: tuple[int, ...], dtype: str, kind: None | str) -> TextDoc
¤
Summarises an array based on its shape/dtype/kind. (Where 'kind' refers to NumPy vs PyTorch vs JAX etc.)
Arguments:
shape
: a tuple of integers.dtype
: a string, for which common dtypes will be contracted (float -> f
,uint -> u
,int -> i
,complex -> c
)kind
: optional. If provided it is written in brackets afterwards.
Returns:
A wadler_lindig.TextDoc
with text looking like e.g. f32[2,3,4](numpy)
for a
NumPy array of shape (2, 3, 4)
and float32
dtype.
wadler_lindig.bracketed(begin: AbstractDoc, docs: Sequence[AbstractDoc], sep: AbstractDoc, end: AbstractDoc, indent: int) -> AbstractDoc
¤
A helper for formatting a 'bracketed' object: tuples, lists, classes, etc, which are all represented in essentially similar ways: a pair of brackets (whether round, square, etc.), a sequence of values in between -- which are indented if laid out in vertical mode, and possibly a name as prefix.
See the (break-group).nest-break
example for more on the
pattern that this enables.
Arguments:
begin
: appears at the start, before any indent.docs:
: a sequence of documents. They will either be laid out horizontally together or vertically together.sep
: each element ofdocs
will be separated bysep
.end
: appears at the end, after any indent.indent
: how much to indent (forwadler_lindig.NestDoc
to use) when laying out vertically.
Returns:
A document in (break-group).nest-break
form.
Example
Formatting a list, which do not have any name prefix:
import wadler_lindig as wl
wl.bracketed(
begin=wl.TextDoc("["),
docs=[wl.pdoc(x) for x in obj],
sep=wl.comma,
end=wl.TextDoc("]"),
indent=indent,
)
Formatting a frozenset, which does have a name prefix:
import wadler_lindig as wl
wl.bracketed(
begin=wl.TextDoc("frozenset({"),
docs=[wl.pdoc(x) for x in obj],
sep=wl.comma,
end=wl.TextDoc("})"),
indent=indent,
)
wadler_lindig.comma
¤
A shorthand for TextDoc(',') + BreakDoc(' ')
.
wadler_lindig.join(sep: AbstractDoc, docs: Sequence[AbstractDoc]) -> AbstractDoc
¤
Concatenates objs
together separated by sep
.
Arguments:
sep
: the separate to use.docs
: a sequence of documents to join.
Returns:
ConcatDoc(docs[0], sep, docs[1], sep, docs[2], ..., sep, docs[-1])
wadler_lindig.named_objs(pairs: Iterable[tuple[str, Any]], **kwargs) -> list[AbstractDoc]
¤
Formats key-value pairs in the form 'key=value'.
Arguments:
pairs
: an iterable of(key, value)
pairs.**kwargs
: passed on to eachpdoc(value, **kwargs)
Returns:
A list of documents TextDoc(key) + TextDoc("=") + pdoc(value, **kwargs)
for each
key-value pair.