Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions Doc/library/textwrap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ functions should be good enough; otherwise, you should use an instance of
replace_whitespace=True, fix_sentence_endings=False, \
break_long_words=True, drop_whitespace=True, \
break_on_hyphens=True, tabsize=8, max_lines=None, \
placeholder=' [...]')
fold_space_newline=False, placeholder=' [...]')

Wraps the single paragraph in *text* (a string) so every line is at most
*width* characters long. Returns a list of output lines, without final
Expand All @@ -40,7 +40,8 @@ functions should be good enough; otherwise, you should use an instance of
replace_whitespace=True, fix_sentence_endings=False, \
break_long_words=True, drop_whitespace=True, \
break_on_hyphens=True, tabsize=8, \
max_lines=None, placeholder=' [...]')
max_lines=None, fold_space_newline=False, \
placeholder=' [...]')

Wraps the single paragraph in *text*, and returns a single string containing the
wrapped paragraph. :func:`fill` is shorthand for ::
Expand All @@ -53,7 +54,7 @@ functions should be good enough; otherwise, you should use an instance of

.. function:: shorten(text, width, *, fix_sentence_endings=False, \
break_long_words=True, break_on_hyphens=True, \
placeholder=' [...]')
placeholder=' [...]', fold_space_newline=False)

Collapse and truncate the given *text* to fit in the given *width*.

Expand Down Expand Up @@ -206,6 +207,18 @@ hyphenated words; only then will long words be broken if necessary, unless
be split into paragraphs (using :meth:`str.splitlines` or similar)
which are wrapped separately.

.. attribute:: fold_space_newline

(default: ``False``) If true, and *replace_whitespace* is also ``True``,
newlines will be folded into adjacent spaces if present (if no adjacent
spaces, newlines are replaced by a space as normal).

.. note::

When combined with *drop_whitespace* set to ``False``, this option allows
for stable text wrapping, i.e. wrapping can be applied multiple times with
no changes after the first application. Stable wrapping is not guaranteed with any
other options in this class.

.. attribute:: drop_whitespace

Expand Down
4 changes: 3 additions & 1 deletion Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ Important deprecations, removals or restrictions:
New Features
============


* New option *fold_space_newline* (default: ``False``) was added to
:class:`~textwrap.TextWrapper`. When set to true, newlines may be folded
into adjacent spaces; see class documentation for details.

Other Language Changes
======================
Expand Down
5 changes: 3 additions & 2 deletions Lib/idlelib/idle_test/test_calltip.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,13 @@ class SB: __call__ = None
non-overlapping occurrences o...''')

def test_signature_wrap(self):
# Test revised for 3.11, bpo-32397.
if textwrap.TextWrapper.__doc__ is not None:
self.assertEqual(get_spec(textwrap.TextWrapper), '''\
(width=70, initial_indent='', subsequent_indent='', expand_tabs=True,
replace_whitespace=True, fix_sentence_endings=False, break_long_words=True,
drop_whitespace=True, break_on_hyphens=True, tabsize=8, *, max_lines=None,
placeholder=' [...]')
drop_whitespace=True, break_on_hyphens=True, tabsize=8, fold_space_newline=False,
*, max_lines=None, placeholder=' [...]')
Comment thread
akulakov marked this conversation as resolved.
Object for wrapping/filling text. The public interface consists of
the wrap() and fill() methods; the other methods are just there for
subclasses to override in order to tweak the default behaviour.
Expand Down
33 changes: 33 additions & 0 deletions Lib/test/test_textwrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,39 @@ def test_drop_whitespace_whitespace_indent(self):
self.check_wrap("abcd efgh", 6, [" abcd", " efgh"],
initial_indent=" ", subsequent_indent=" ")

def test_replace_whitespace(self):
# Check that special whitespace chars like \n are replaced with a space.
self.check_wrap("abcd\nefgh", 10, ["abcd efgh"])
self.check_wrap("abcd \nefgh", 10, ["abcd efgh"])

self.check_wrap("abcd \nefgh", 6, ["abcd ", "efgh"],
drop_whitespace=False)

self.check_wrap("abcd \nefgh", 10, ["abcd \nefgh"],
replace_whitespace=False)

def test_fold_space_newline(self):
# Check that \n and \r\n are folded into adjacent space if present,
# otherwise \n and \r\n would be replaced with spaces.
self.check_wrap("abcd\nefgh", 10, ["abcd efgh"],
fold_space_newline=True)

self.check_wrap("abcd \nefgh", 10, ["abcd efgh"],
fold_space_newline=True)

# this is the test for combination of arguments that allow "stable"
# wrapping (see docstring for _munge_whitespace())
self.check_wrap("abcd \nefgh", 6, ["abcd ", "efgh"],
fold_space_newline=True, drop_whitespace=False)

self.check_wrap("abcd\n efgh", 10, ["abcd efgh"],
fold_space_newline=True)

self.check_wrap("abcd\r\n efgh", 12, ["abcd efgh"])

self.check_wrap("abcd\r\n efgh", 10, ["abcd efgh"],
fold_space_newline=True)

def test_split(self):
# Ensure that the standard _split() method works as advertised
# in the comments
Expand Down
17 changes: 16 additions & 1 deletion Lib/textwrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ class TextWrapper:
compound words.
drop_whitespace (default: true)
Drop leading and trailing whitespace from lines.
fold_space_newline (default: false)
When replacing whitespace (replace_whitespace=True), fold newlines into
adjoining space character if present. This option allows to perform stable
text wrapping when combined with drop_whitespace=False.
max_lines (default: None)
Truncate wrapped lines.
placeholder (default: ' [...]')
Expand Down Expand Up @@ -120,6 +124,7 @@ def __init__(self,
drop_whitespace=True,
break_on_hyphens=True,
tabsize=8,
fold_space_newline=False,
*,
max_lines=None,
placeholder=' [...]'):
Expand All @@ -135,7 +140,7 @@ def __init__(self,
self.tabsize = tabsize
self.max_lines = max_lines
self.placeholder = placeholder

self.fold_space_newline = fold_space_newline

# -- Private methods -----------------------------------------------
# (possibly useful for subclasses to override)
Expand All @@ -146,10 +151,20 @@ def _munge_whitespace(self, text):
Munge whitespace in text: expand tabs and convert all other
whitespace characters to spaces. Eg. " foo\\tbar\\n\\nbaz"
becomes " foo bar baz".

If `fold_space_newline=True`, fold newlines into adjacent space (if
present). This allows for stable wrapping when combined with
`drop_whitespace=False`, i.e. repeated wrapping operation results in
the same output.
"""
if self.expand_tabs:
text = text.expandtabs(self.tabsize)
if self.replace_whitespace:
if self.fold_space_newline:
if re.search(r' \r?\n', text):
text = re.sub(r' \r?\n', ' ', text)
if re.search(r'\r?\n ', text):
text = re.sub(r'\r?\n ', ' ', text)
text = text.translate(self.unicode_whitespace_trans)
return text

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
New option *fold_space_newline* (default: ``False``) was added to
:class:`~textwrap.TextWrapper`. When set to true, newlines may be folded
into adjacent spaces; see class documentation for details.