Skip to content

Commit

Permalink
gh-92734: Add indentation feature to reprlib.Repr (GH-92735)
Browse files Browse the repository at this point in the history
  • Loading branch information
finefoot authored Sep 8, 2022
1 parent aa3b4cf commit c06c001
Show file tree
Hide file tree
Showing 4 changed files with 421 additions and 5 deletions.
62 changes: 61 additions & 1 deletion Doc/library/reprlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ This module provides a class, an instance, and a function:

.. class:: Repr(*, maxlevel=6, maxtuple=6, maxlist=6, maxarray=5, maxdict=4, \
maxset=6, maxfrozenset=6, maxdeque=6, maxstring=30, maxlong=40, \
maxother=30, fillvalue="...")
maxother=30, fillvalue="...", indent=None)

Class which provides formatting services useful in implementing functions
similar to the built-in :func:`repr`; size limits for different object types
Expand Down Expand Up @@ -142,6 +142,66 @@ which format specific object types.
similar manner as :attr:`maxstring`. The default is ``20``.


.. attribute:: Repr.indent

If this attribute is set to ``None`` (the default), the output is formatted
with no line breaks or indentation, like the standard :func:`repr`.
For example:

.. code-block:: pycon
>>> example = [
1, 'spam', {'a': 2, 'b': 'spam eggs', 'c': {3: 4.5, 6: []}}, 'ham']
>>> import reprlib
>>> aRepr = reprlib.Repr()
>>> print(aRepr.repr(example))
[1, 'spam', {'a': 2, 'b': 'spam eggs', 'c': {3: 4.5, 6: []}}, 'ham']
If :attr:`~Repr.indent` is set to a string, each recursion level
is placed on its own line, indented by that string:

.. code-block:: pycon
>>> aRepr.indent = '-->'
>>> print(aRepr.repr(example))
[
-->1,
-->'spam',
-->{
-->-->'a': 2,
-->-->'b': 'spam eggs',
-->-->'c': {
-->-->-->3: 4.5,
-->-->-->6: [],
-->-->},
-->},
-->'ham',
]
Setting :attr:`~Repr.indent` to a positive integer value behaves as if it
was set to a string with that number of spaces:

.. code-block:: pycon
>>> aRepr.indent = 4
>>> print(aRepr.repr(example))
[
1,
'spam',
{
'a': 2,
'b': 'spam eggs',
'c': {
3: 4.5,
6: [],
},
},
'ham',
]
.. versionadded:: 3.12


.. method:: Repr.repr(obj)

The equivalent to the built-in :func:`repr` that uses the formatting imposed by
Expand Down
29 changes: 25 additions & 4 deletions Lib/reprlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class Repr:
def __init__(
self, *, maxlevel=6, maxtuple=6, maxlist=6, maxarray=5, maxdict=4,
maxset=6, maxfrozenset=6, maxdeque=6, maxstring=30, maxlong=40,
maxother=30, fillvalue='...',
maxother=30, fillvalue='...', indent=None,
):
self.maxlevel = maxlevel
self.maxtuple = maxtuple
Expand All @@ -52,6 +52,7 @@ def __init__(
self.maxlong = maxlong
self.maxother = maxother
self.fillvalue = fillvalue
self.indent = indent

def repr(self, x):
return self.repr1(x, self.maxlevel)
Expand All @@ -66,6 +67,26 @@ def repr1(self, x, level):
else:
return self.repr_instance(x, level)

def _join(self, pieces, level):
if self.indent is None:
return ', '.join(pieces)
if not pieces:
return ''
indent = self.indent
if isinstance(indent, int):
if indent < 0:
raise ValueError(
f'Repr.indent cannot be negative int (was {indent!r})'
)
indent *= ' '
try:
sep = ',\n' + (self.maxlevel - level + 1) * indent
except TypeError as error:
raise TypeError(
f'Repr.indent must be a str, int or None, not {type(indent)}'
) from error
return sep.join(('', *pieces, ''))[1:-len(indent) or None]

def _repr_iterable(self, x, level, left, right, maxiter, trail=''):
n = len(x)
if level <= 0 and n:
Expand All @@ -76,8 +97,8 @@ def _repr_iterable(self, x, level, left, right, maxiter, trail=''):
pieces = [repr1(elem, newlevel) for elem in islice(x, maxiter)]
if n > maxiter:
pieces.append(self.fillvalue)
s = ', '.join(pieces)
if n == 1 and trail:
s = self._join(pieces, level)
if n == 1 and trail and self.indent is None:
right = trail + right
return '%s%s%s' % (left, s, right)

Expand Down Expand Up @@ -124,7 +145,7 @@ def repr_dict(self, x, level):
pieces.append('%s: %s' % (keyrepr, valrepr))
if n > self.maxdict:
pieces.append(self.fillvalue)
s = ', '.join(pieces)
s = self._join(pieces, level)
return '{%s}' % (s,)

def repr_str(self, x, level):
Expand Down
Loading

0 comments on commit c06c001

Please sign in to comment.