Commit 0471cbdb authored by Greg Ward's avatar Greg Ward

Make 'width' an instance attribute rather than an argument to the wrap()

and fill() methods.  Keep interface of existing wrap() and fill()
functions by going back to having them construct a new TextWrapper
instance on each call, with the preferred width passed to the
constructor.
parent 84b8509f
...@@ -18,8 +18,10 @@ class TextWrapper: ...@@ -18,8 +18,10 @@ class TextWrapper:
If you want to completely replace the main wrapping algorithm, If you want to completely replace the main wrapping algorithm,
you'll probably have to override _wrap_chunks(). you'll probably have to override _wrap_chunks().
Several boolean instance attributes control various aspects of Several instance attributes control various aspects of wrapping:
wrapping: width (default: 70)
the maximum width of wrapped lines (unless break_long_words
is false)
expand_tabs (default: true) expand_tabs (default: true)
Expand tabs in input text to spaces before further processing. Expand tabs in input text to spaces before further processing.
Each tab will become 1 .. 8 spaces, depending on its position in Each tab will become 1 .. 8 spaces, depending on its position in
...@@ -34,9 +36,8 @@ class TextWrapper: ...@@ -34,9 +36,8 @@ class TextWrapper:
by two spaces. Off by default becaus the algorithm is by two spaces. Off by default becaus the algorithm is
(unavoidably) imperfect. (unavoidably) imperfect.
break_long_words (default: true) break_long_words (default: true)
Break words longer than the line width constraint. If false, Break words longer than 'width'. If false, those words will not
those words will not be broken, and some lines might be longer be broken, and some lines might be longer than 'width'.
than the width constraint.
""" """
whitespace_trans = string.maketrans(string.whitespace, whitespace_trans = string.maketrans(string.whitespace,
...@@ -61,10 +62,12 @@ class TextWrapper: ...@@ -61,10 +62,12 @@ class TextWrapper:
def __init__ (self, def __init__ (self,
width=70,
expand_tabs=True, expand_tabs=True,
replace_whitespace=True, replace_whitespace=True,
fix_sentence_endings=False, fix_sentence_endings=False,
break_long_words=True): break_long_words=True):
self.width = width
self.expand_tabs = expand_tabs self.expand_tabs = expand_tabs
self.replace_whitespace = replace_whitespace self.replace_whitespace = replace_whitespace
self.fix_sentence_endings = fix_sentence_endings self.fix_sentence_endings = fix_sentence_endings
...@@ -121,15 +124,15 @@ class TextWrapper: ...@@ -121,15 +124,15 @@ class TextWrapper:
else: else:
i += 1 i += 1
def _handle_long_word(self, chunks, cur_line, cur_len, width): def _handle_long_word(self, chunks, cur_line, cur_len):
"""_handle_long_word(chunks : [string], """_handle_long_word(chunks : [string],
cur_line : [string], cur_line : [string],
cur_len : int, width : int) cur_len : int)
Handle a chunk of text (most likely a word, not whitespace) that Handle a chunk of text (most likely a word, not whitespace) that
is too long to fit in any line. is too long to fit in any line.
""" """
space_left = width - cur_len space_left = self.width - cur_len
# If we're allowed to break long words, then do so: put as much # If we're allowed to break long words, then do so: put as much
# of the next chunk onto the current line as will fit. # of the next chunk onto the current line as will fit.
...@@ -149,20 +152,21 @@ class TextWrapper: ...@@ -149,20 +152,21 @@ class TextWrapper:
# cur_len will be zero, so the next line will be entirely # cur_len will be zero, so the next line will be entirely
# devoted to the long word that we can't handle right now. # devoted to the long word that we can't handle right now.
def _wrap_chunks(self, chunks, width): def _wrap_chunks(self, chunks):
"""_wrap_chunks(chunks : [string], width : int) -> [string] """_wrap_chunks(chunks : [string]) -> [string]
Wrap a sequence of text chunks and return a list of lines of Wrap a sequence of text chunks and return a list of lines of
length 'width' or less. (If 'break_long_words' is false, some length 'self.width' or less. (If 'break_long_words' is false,
lines may be longer than 'width'.) Chunks correspond roughly to some lines may be longer than this.) Chunks correspond roughly
words and the whitespace between them: each chunk is indivisible to words and the whitespace between them: each chunk is
(modulo 'break_long_words'), but a line break can come between indivisible (modulo 'break_long_words'), but a line break can
any two chunks. Chunks should not have internal whitespace; come between any two chunks. Chunks should not have internal
ie. a chunk is either all whitespace or a "word". Whitespace whitespace; ie. a chunk is either all whitespace or a "word".
chunks will be removed from the beginning and end of lines, but Whitespace chunks will be removed from the beginning and end of
apart from that whitespace is preserved. lines, but apart from that whitespace is preserved.
""" """
lines = [] lines = []
width = self.width
while chunks: while chunks:
...@@ -188,7 +192,7 @@ class TextWrapper: ...@@ -188,7 +192,7 @@ class TextWrapper:
# The current line is full, and the next chunk is too big to # The current line is full, and the next chunk is too big to
# fit on *any* line (not just this one). # fit on *any* line (not just this one).
if chunks and len(chunks[0]) > width: if chunks and len(chunks[0]) > width:
self._handle_long_word(chunks, cur_line, cur_len, width) self._handle_long_word(chunks, cur_line, cur_len)
# If the last chunk on this line is all whitespace, drop it. # If the last chunk on this line is all whitespace, drop it.
if cur_line and cur_line[-1].strip() == '': if cur_line and cur_line[-1].strip() == '':
...@@ -204,26 +208,25 @@ class TextWrapper: ...@@ -204,26 +208,25 @@ class TextWrapper:
# -- Public interface ---------------------------------------------- # -- Public interface ----------------------------------------------
def wrap(self, text, width): def wrap(self, text):
"""wrap(text : string, width : int) -> [string] """wrap(text : string) -> [string]
Split 'text' into multiple lines of no more than 'width' Split 'text' into multiple lines of no more than 'self.width'
characters each, and return the list of strings that results. characters each, and return the list of strings that results.
Tabs in 'text' are expanded with string.expandtabs(), and all Tabs in 'text' are expanded with string.expandtabs(), and all
other whitespace characters (including newline) are converted to other whitespace characters (including newline) are converted to
space. space.
""" """
text = self._munge_whitespace(text) text = self._munge_whitespace(text)
if len(text) <= width: if len(text) <= self.width:
return [text] return [text]
chunks = self._split(text) chunks = self._split(text)
if self.fix_sentence_endings: if self.fix_sentence_endings:
self._fix_sentence_endings(chunks) self._fix_sentence_endings(chunks)
return self._wrap_chunks(chunks, width) return self._wrap_chunks(chunks)
def fill(self, text, width, initial_tab="", subsequent_tab=""): def fill(self, text, initial_tab="", subsequent_tab=""):
"""fill(text : string, """fill(text : string,
width : int,
initial_tab : string = "", initial_tab : string = "",
subsequent_tab : string = "") subsequent_tab : string = "")
-> string -> string
...@@ -234,17 +237,15 @@ class TextWrapper: ...@@ -234,17 +237,15 @@ class TextWrapper:
lengths of the tab strings are accounted for when wrapping lines lengths of the tab strings are accounted for when wrapping lines
to fit in 'width' columns. to fit in 'width' columns.
""" """
lines = self.wrap(text, width) lines = self.wrap(text)
sep = "\n" + subsequent_tab sep = "\n" + subsequent_tab
return initial_tab + sep.join(lines) return initial_tab + sep.join(lines)
# Convenience interface # Convenience interface
_wrapper = TextWrapper()
def wrap(text, width): def wrap(text, width):
return _wrapper.wrap(text, width) return TextWrapper(width=width).wrap(text)
def fill(text, width, initial_tab="", subsequent_tab=""): def fill(text, width, initial_tab="", subsequent_tab=""):
return _wrapper.fill(text, width, initial_tab, subsequent_tab) return TextWrapper(width=width).fill(text, initial_tab, subsequent_tab)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment