Skip to content

Commit

Permalink
Merge branch 'master' into feat/add-ipython-extension
Browse files Browse the repository at this point in the history
  • Loading branch information
willmcgugan committed Jun 9, 2021
2 parents 090fab4 + 7592f09 commit 9a65f49
Show file tree
Hide file tree
Showing 22 changed files with 630 additions and 234 deletions.
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [10.3.0] - 2021-06-09

### Added

- Added Console.size setter
- Added Console.width setter
- Added Console.height setter
- Added angular style Rich reprs
- Added an IPython extension. Load via `%load_ext rich`

### Changed

- Changed the logic for retrieving the calling frame in console logs to a faster one for the Python implementations that support it.

## [10.2.2] - 2021-05-19


Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ The following people have contributed to the development of Rich:
- [Will McGugan](https://github.com/willmcgugan)
- [Nathan Page](https://github.com/nathanrpage97)
- [Clément Robert](https://github.com/neutrinoceros)
- [Gabriele N. Tornetta](https://github.com/p403n1x87)
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Welcome to Rich's documentation!
markup.rst
text.rst
highlighting.rst
pretty.rst
logging.rst
traceback.rst
prompt.rst
Expand Down
162 changes: 162 additions & 0 deletions docs/source/pretty.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
Pretty
======

In addition to syntax highlighting, Rich will automatically format any containers (lists, dicts, sets etc) you print to the console. The formatting method used by Rich follows well established conventions in the Python world to present data in a more readable way.

Run the following command to see an example of pretty printed output::

python -m rich.pretty

Note how the output will change if you change the width of the terminal.

pprint method
-------------

The :func:`~rich.pretty.pprint` method offers a few more argument you can use to tweak how objects are pretty printed. Here's how you would import it::

>>> from rich.pretty import pprint
>>> pprint(locals())

Indent guides
~~~~~~~~~~~~~

Rich can draw *intent guides* which are vertical lines to highlight the indent level of a data structure. These can make it easier to read more deeply nested output. The pprint method enables indent guides by default, but you can set ``indent_guides=False`` to disable this feature.

Expand all
~~~~~~~~~~

Rich is quite conservative about expanding data structures and will try to fit as much in each line as it can. If you prefer, you can tell Rich to fully expand all data structures by setting ``expand_all=True``. Here's an example::

>>> pprint(["eggs", "ham"], expand_all=True)

Truncating pretty output
~~~~~~~~~~~~~~~~~~~~~~~~

Very long data structures can be difficult to read and you may find yourself scrolling through multiple pages to find the output you are interested in. Rich can truncate containers and long strings if you just need a general overview a data structure.

If you set the ``max_length`` argument to an integer then Rich will truncate containers with more than the given number of elements. If data is truncated, Rich will display an ellipsis ``...`` and the number of elements not shown.

Here's an example::

>>> pprint(locals(), max_length=2)

Truncating long strings
~~~~~~~~~~~~~~~~~~~~~~~

If you set the `max_string` argument to an integer, Rich will truncate strings over that length. Truncated string will be appended with the number of characters that have not been shown. Here's an example::

>>> pprint("Where there is a Will, there is a Way", max_string=21)

Pretty renderable
-----------------

Rich offers a :class:`~rich.pretty.Pretty` class which you can user to insert pretty printed data in to another renderable.

The following example, prints pretty printed output within a simple panel::

from rich import print
from rich.pretty import Pretty
from rich.panel import Panel

pretty = Pretty(locals())
panel = Panel(pretty)
print(panel)

There are a large number of options to tweak the pretty formatting, See the :class:`~rich.pretty.Pretty` reference for details.

Rich Repr
---------

Rich is able to syntax highlight any output, but the extra formatting done by the pretty printing is restricted to builtin containers, dataclasses, and other objects Rich knows about, such as objects generated by the `attrs <https://www.attrs.org/en/stable/>`_ library. Fortunately Rich offers a simple protocol you can use to add pretty printable output to any object.

First, let's look at a class that might benefit from a Rich repr::

class Bird:
def __init__(self, name, eats=None, fly=True, extinct=False):
self.name = name
self.eats = list(eats) if eats else []
self.fly = fly
self.extinct = extinct

def __repr__(self):
return f"Bird({self.name!r}, eats={self.eats!r}, fly={self.fly!r}, extinct={self.extinct!r})"

BIRDS = {
"gull": Bird("gull", eats=["fish", "chips", "ice cream", "sausage rolls"]),
"penguin": Bird("penguin", eats=["fish"], fly=False),
"dodo": Bird("dodo", eats=["fruit"], fly=False, extinct=True)
}
print(BIRDS)

The result of this script would be::

{'gull': Bird('gull', eats=['fish', 'chips', 'ice cream', 'sausage rolls'], fly=True, extinct=False), 'penguin': Bird('penguin', eats=['fish'], fly=False, extinct=False), 'dodo': Bird('dodo', eats=['fruit'], fly=False, extinct=True)}

The output is long enough to wrap on to the next line, which can make it hard to read. The repr strings are informative but a little verbose since they include default arguments. If we print this with Rich, things are improved somewhat::

{
'gull': Bird('gull', eats=['fish', 'chips', 'ice cream', 'sausage rolls'],
fly=True, extinct=False),
'penguin': Bird('penguin', eats=['fish'], fly=False, extinct=False),
'dodo': Bird('dodo', eats=['fruit'], fly=False, extinct=True)
}

Rich knows how to format the container dict, but the repr strings are still verbose, and there is some wrapping of the output (assuming an 80 character terminal).

We can solve both these issues by adding the following ``__rich_repr__`` method::

def __rich_repr__(self):
yield self.name
yield "eats", self.eats
yield "fly", self.fly, True
yield "extinct", self.extinct, False

Now if we print the same object with Rich we would get the following::

{
'gull': Bird(
'gull',
eats=['fish', 'chips', 'ice cream', 'sausage rolls']
),
'penguin': Bird('penguin', eats=['fish'], fly=False),
'dodo': Bird('dodo', eats=['fruit'], fly=False, extinct=True)
}

The default arguments have been omitted, and the output has been formatted nicely. Even if we have less room in the terminal, the result is still quite readable::

{
'gull': Bird(
'gull',
eats=[
'fish',
'chips',
'ice cream',
'sausage rolls'
]
),
'penguin': Bird(
'penguin',
eats=['fish'],
fly=False
),
'dodo': Bird(
'dodo',
eats=['fruit'],
fly=False,
extinct=True
)
}

You can add a ``__rich_repr__`` method to any class to enable Rich reprs. This method should return an iterable of tuples. You could return a list of tuples, but it's easier to express with the ``yield`` keywords, making it a *generator*.

Each tuple specifies an element in the output.

- ``yield value`` will generate a positional argument.
- ``yield name, value`` will generate a keyword argument.
- ``yield name, value, default`` will generate a keyword argument *if* ``value`` is not equal to ``default``.

You can also tell Rich to generate the *angular bracket* style of repr, which tend to be used where there is no easy way to recreate the object's constructor. To do this set the function attribute ``"angluar"`` to ``True`` immediately after your ``__rich_repr__`` methods. For example::

__rich_repr__.angular = True

Note that you can add ``__rich_repr__`` methods to third-party libraries *without* including Rich as a dependency. If Rich is not installed, then nothing will break. Hopefully many more libraries will adopt Rich repr methods in the future, which will aid debugging!
26 changes: 26 additions & 0 deletions examples/repr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from rich.repr import rich_repr


@rich_repr
class Bird:
def __init__(self, name, eats=None, fly=True, extinct=False):
self.name = name
self.eats = list(eats) if eats else []
self.fly = fly
self.extinct = extinct

def __rich_repr__(self):
yield self.name
yield "eats", self.eats
yield "fly", self.fly, True
yield "extinct", self.extinct, False


from rich import print

BIRDS = {
"gull": Bird("gull", eats=["fish", "chips", "ice cream", "sausage rolls"]),
"penguin": Bird("penguin", eats=["fish"], fly=False),
"dodo": Bird("dodo", eats=["fruit"], fly=False, extinct=True),
}
print(BIRDS)
Loading

0 comments on commit 9a65f49

Please sign in to comment.