diff options
author | Antoine Pitrou <solipsis@pitrou.net> | 2013-08-12 20:39:09 (GMT) |
---|---|---|
committer | Antoine Pitrou <solipsis@pitrou.net> | 2013-08-12 20:39:09 (GMT) |
commit | 389dec8bcf776cd60aa6abaea3d98dc904203b2d (patch) | |
tree | c8fedfc1f6620c1e05ba59d253bfa3e60959d6d3 /Lib/textwrap.py | |
parent | d6cbd34aadd9647c5c7e15e75ed224717567410c (diff) | |
download | cpython-389dec8bcf776cd60aa6abaea3d98dc904203b2d.zip cpython-389dec8bcf776cd60aa6abaea3d98dc904203b2d.tar.gz cpython-389dec8bcf776cd60aa6abaea3d98dc904203b2d.tar.bz2 |
Issue #18585: Add :func:`textwrap.shorten` to collapse and truncate a piece of text to a given length.
Diffstat (limited to 'Lib/textwrap.py')
-rw-r--r-- | Lib/textwrap.py | 53 |
1 files changed, 51 insertions, 2 deletions
diff --git a/Lib/textwrap.py b/Lib/textwrap.py index 7024d4d..b19f124 100644 --- a/Lib/textwrap.py +++ b/Lib/textwrap.py @@ -19,6 +19,8 @@ __all__ = ['TextWrapper', 'wrap', 'fill', 'dedent', 'indent'] # since 0xa0 is not in range(128). _whitespace = '\t\n\x0b\x0c\r ' +_default_placeholder = ' (...)' + class TextWrapper: """ Object for wrapping/filling text. The public interface consists of @@ -277,6 +279,9 @@ class TextWrapper: return lines + def _split_chunks(self, text): + text = self._munge_whitespace(text) + return self._split(text) # -- Public interface ---------------------------------------------- @@ -289,8 +294,7 @@ class TextWrapper: and all other whitespace characters (including newline) are converted to space. """ - text = self._munge_whitespace(text) - chunks = self._split(text) + chunks = self._split_chunks(text) if self.fix_sentence_endings: self._fix_sentence_endings(chunks) return self._wrap_chunks(chunks) @@ -304,6 +308,36 @@ class TextWrapper: """ return "\n".join(self.wrap(text)) + def shorten(self, text, *, placeholder=_default_placeholder): + """shorten(text: str) -> str + + Collapse and truncate the given text to fit in 'self.width' columns. + """ + max_length = self.width + if max_length < len(placeholder.strip()): + raise ValueError("placeholder too large for max width") + sep = ' ' + sep_len = len(sep) + parts = [] + cur_len = 0 + chunks = self._split_chunks(text) + for chunk in chunks: + if not chunk.strip(): + continue + chunk_len = len(chunk) + sep_len if parts else len(chunk) + if cur_len + chunk_len > max_length: + break + parts.append(chunk) + cur_len += chunk_len + else: + # No truncation necessary + return sep.join(parts) + max_truncated_length = max_length - len(placeholder) + while parts and cur_len > max_truncated_length: + last = parts.pop() + cur_len -= len(last) + sep_len + return (sep.join(parts) + placeholder).strip() + # -- Convenience interface --------------------------------------------- @@ -332,6 +366,21 @@ def fill(text, width=70, **kwargs): w = TextWrapper(width=width, **kwargs) return w.fill(text) +def shorten(text, width, *, placeholder=_default_placeholder, **kwargs): + """Collapse and truncate the given text to fit in the given width. + + The text first has its whitespace collapsed. If it then fits in + the *width*, it is returned as is. Otherwise, as many words + as possible are joined and then the placeholder is appended:: + + >>> textwrap.shorten("Hello world!", width=12) + 'Hello world!' + >>> textwrap.shorten("Hello world!", width=11) + 'Hello (...)' + """ + w = TextWrapper(width=width, **kwargs) + return w.shorten(text, placeholder=placeholder) + # -- Loosely related functionality ------------------------------------- |