Close #13857: Added textwrap.indent() function (initial patch by Ezra

Berch)
This commit is contained in:
Nick Coghlan 2012-06-11 23:07:51 +10:00
parent 3c4acd8bf9
commit 4fae8cdaea
6 changed files with 201 additions and 7 deletions

View File

@ -12,7 +12,7 @@
The :mod:`textwrap` module provides two convenience functions, :func:`wrap` and
:func:`fill`, as well as :class:`TextWrapper`, the class that does all the work,
and a utility function :func:`dedent`. If you're just wrapping or filling one
and two utility functions, :func:`dedent` and :func:`indent`. If you're just wrapping or filling one
or two text strings, the convenience functions should be good enough;
otherwise, you should use an instance of :class:`TextWrapper` for efficiency.
@ -45,9 +45,10 @@ Text is preferably wrapped on whitespaces and right after the hyphens in
hyphenated words; only then will long words be broken if necessary, unless
:attr:`TextWrapper.break_long_words` is set to false.
An additional utility function, :func:`dedent`, is provided to remove
indentation from strings that have unwanted whitespace to the left of the text.
Two additional utility function, :func:`dedent` and :func:`indent`, are
provided to remove indentation from strings that have unwanted whitespace
to the left of the text and to add an arbitrary prefix to selected lines
in a block of text.
.. function:: dedent(text)
@ -72,6 +73,32 @@ indentation from strings that have unwanted whitespace to the left of the text.
print(repr(dedent(s))) # prints 'hello\n world\n'
.. function:: indent(text, prefix, predicate=None)
Add *prefix* to the beginning of selected lines in *text*.
Lines are separated by calling ``text.splitlines(True)``.
By default, *prefix* is added to all lines that do not consist
solely of whitespace (including any line endings).
For example::
>>> s = 'hello\n\n \nworld'
>>> indent(s, ' ')
' hello\n\n \n world'
The optional *predicate* argument can be used to control which lines
are indented. For example, it is easy to add *prefix* to even empty
and whitespace-only lines::
>>> print(indent(s, '+ ', lambda line: True))
+ hello
+
+
+ world
.. class:: TextWrapper(**kwargs)
The :class:`TextWrapper` constructor accepts a number of optional keyword

View File

@ -1406,6 +1406,14 @@ sys
(:issue:`11223`)
textwrap
--------
* The :mod:`textwrap` module has a new :func:`~textwrap.indent` that makes
it straightforward to add a common prefix to selected lines in a block
of text.
(:issue:`13857`)
time
----

View File

@ -11,7 +11,7 @@
import unittest
from test import support
from textwrap import TextWrapper, wrap, fill, dedent
from textwrap import TextWrapper, wrap, fill, dedent, indent
class BaseTestCase(unittest.TestCase):
@ -594,11 +594,147 @@ def foo():
self.assertEqual(expect, dedent(text))
# Test textwrap.indent
class IndentTestCase(unittest.TestCase):
# The examples used for tests. If any of these change, the expected
# results in the various test cases must also be updated.
# The roundtrip cases are separate, because textwrap.dedent doesn't
# handle Windows line endings
ROUNDTRIP_CASES = (
# Basic test case
"Hi.\nThis is a test.\nTesting.",
# Include a blank line
"Hi.\nThis is a test.\n\nTesting.",
# Include leading and trailing blank lines
"\nHi.\nThis is a test.\nTesting.\n",
)
CASES = ROUNDTRIP_CASES + (
# Use Windows line endings
"Hi.\r\nThis is a test.\r\nTesting.\r\n",
# Pathological case
"\nHi.\r\nThis is a test.\n\r\nTesting.\r\n\n",
)
def test_indent_nomargin_default(self):
# indent should do nothing if 'prefix' is empty.
for text in self.CASES:
self.assertEqual(indent(text, ''), text)
def test_indent_nomargin_explicit_default(self):
# The same as test_indent_nomargin, but explicitly requesting
# the default behaviour by passing None as the predicate
for text in self.CASES:
self.assertEqual(indent(text, '', None), text)
def test_indent_nomargin_all_lines(self):
# The same as test_indent_nomargin, but using the optional
# predicate argument
predicate = lambda line: True
for text in self.CASES:
self.assertEqual(indent(text, '', predicate), text)
def test_indent_no_lines(self):
# Explicitly skip indenting any lines
predicate = lambda line: False
for text in self.CASES:
self.assertEqual(indent(text, ' ', predicate), text)
def test_roundtrip_spaces(self):
# A whitespace prefix should roundtrip with dedent
for text in self.ROUNDTRIP_CASES:
self.assertEqual(dedent(indent(text, ' ')), text)
def test_roundtrip_tabs(self):
# A whitespace prefix should roundtrip with dedent
for text in self.ROUNDTRIP_CASES:
self.assertEqual(dedent(indent(text, '\t\t')), text)
def test_roundtrip_mixed(self):
# A whitespace prefix should roundtrip with dedent
for text in self.ROUNDTRIP_CASES:
self.assertEqual(dedent(indent(text, ' \t \t ')), text)
def test_indent_default(self):
# Test default indenting of lines that are not whitespace only
prefix = ' '
expected = (
# Basic test case
" Hi.\n This is a test.\n Testing.",
# Include a blank line
" Hi.\n This is a test.\n\n Testing.",
# Include leading and trailing blank lines
"\n Hi.\n This is a test.\n Testing.\n",
# Use Windows line endings
" Hi.\r\n This is a test.\r\n Testing.\r\n",
# Pathological case
"\n Hi.\r\n This is a test.\n\r\n Testing.\r\n\n",
)
for text, expect in zip(self.CASES, expected):
self.assertEqual(indent(text, prefix), expect)
def test_indent_explicit_default(self):
# Test default indenting of lines that are not whitespace only
prefix = ' '
expected = (
# Basic test case
" Hi.\n This is a test.\n Testing.",
# Include a blank line
" Hi.\n This is a test.\n\n Testing.",
# Include leading and trailing blank lines
"\n Hi.\n This is a test.\n Testing.\n",
# Use Windows line endings
" Hi.\r\n This is a test.\r\n Testing.\r\n",
# Pathological case
"\n Hi.\r\n This is a test.\n\r\n Testing.\r\n\n",
)
for text, expect in zip(self.CASES, expected):
self.assertEqual(indent(text, prefix, None), expect)
def test_indent_all_lines(self):
# Add 'prefix' to all lines, including whitespace-only ones.
prefix = ' '
expected = (
# Basic test case
" Hi.\n This is a test.\n Testing.",
# Include a blank line
" Hi.\n This is a test.\n \n Testing.",
# Include leading and trailing blank lines
" \n Hi.\n This is a test.\n Testing.\n",
# Use Windows line endings
" Hi.\r\n This is a test.\r\n Testing.\r\n",
# Pathological case
" \n Hi.\r\n This is a test.\n \r\n Testing.\r\n \n",
)
predicate = lambda line: True
for text, expect in zip(self.CASES, expected):
self.assertEqual(indent(text, prefix, predicate), expect)
def test_indent_empty_lines(self):
# Add 'prefix' solely to whitespace-only lines.
prefix = ' '
expected = (
# Basic test case
"Hi.\nThis is a test.\nTesting.",
# Include a blank line
"Hi.\nThis is a test.\n \nTesting.",
# Include leading and trailing blank lines
" \nHi.\nThis is a test.\nTesting.\n",
# Use Windows line endings
"Hi.\r\nThis is a test.\r\nTesting.\r\n",
# Pathological case
" \nHi.\r\nThis is a test.\n \r\nTesting.\r\n \n",
)
predicate = lambda line: not line.strip()
for text, expect in zip(self.CASES, expected):
self.assertEqual(indent(text, prefix, predicate), expect)
def test_main():
support.run_unittest(WrapTestCase,
LongWordTestCase,
IndentTestCases,
DedentTestCase)
DedentTestCase,
IndentTestCase)
if __name__ == '__main__':
test_main()

View File

@ -7,7 +7,7 @@
import re
__all__ = ['TextWrapper', 'wrap', 'fill', 'dedent']
__all__ = ['TextWrapper', 'wrap', 'fill', 'dedent', 'indent']
# Hardcode the recognized whitespace characters to the US-ASCII
# whitespace characters. The main reason for doing this is that in
@ -386,6 +386,25 @@ def dedent(text):
text = re.sub(r'(?m)^' + margin, '', text)
return text
def indent(text, prefix, predicate=None):
"""Adds 'prefix' to the beginning of selected lines in 'text'.
If 'predicate' is provided, 'prefix' will only be added to the lines
where 'predicate(line)' is True. If 'predicate' is not provided,
it will default to adding 'prefix' to all non-empty lines that do not
consist solely of whitespace characters.
"""
if predicate is None:
def predicate(line):
return line.strip()
def prefixed_lines():
for line in text.splitlines(True):
yield (prefix + line if predicate(line) else line)
return ''.join(prefixed_lines())
if __name__ == "__main__":
#print dedent("\tfoo\n\tbar")
#print dedent(" \thello there\n \t how are you?")

View File

@ -82,6 +82,7 @@ Alexander “Саша” Belopolsky
Eli Bendersky
Andrew Bennetts
Andy Bensky
Ezra Berch
Michel Van den Bergh
Julian Berman
Brice Berna

View File

@ -21,6 +21,9 @@ Core and Builtins
Library
-------
- Issue #13857: Added textwrap.indent() function (initial patch by Ezra
Berch)
- Issue #2736: Added datetime.timestamp() method.
- Issue #13854: Make multiprocessing properly handle non-integer