Source code for pytool.text

"""
This module contains text related things that make life easier.
"""

import shutil
import textwrap


[docs] def columns(default=79): """ Return the number of text columns for the current console or TTY. On certain systems it may be necessary to set the ``COLUMNS`` environment variable if you want this method to work correctly. Uses `default` (79) if no value can be found. """ width, _ = shutil.get_terminal_size() return width - 1
[docs] def wrap(text, width=None, indent=""): """ Return `text` wrapped to `width` while trimming leading indentation and preserving paragraphs. This function is handy for turning indented inline strings into unindented strings that preserve paragraphs, whitespace, and any indentation beyond the baseline. The default wrap width is the number of columns available in the current console or TTY. If the columns can't be found, then the default wrap width is ``79``. :param text: Text to wrap :param width: Width to wrap text at (default: text column width) :param indent: String to indent text with (default: '') :type text: str :type width: int :type indent: str :: >>> import pytool >>> text = ''' All this is indented by 8, but will be 0. This is indented by 16, and a really long long long line which is hard wrapped at a random character width, but will be wrapped appropriately at 70 chars afterwards. This is indented by 8 again. ''' >>> print pytool.text.wrap(text) All this is indented by 8, but will be 0. This is indented by 16, and a really long long long line which is hard wrapped at a random character width, but will be wrapped appropriately at 70 chars afterwards. This is indented by 8 again. >>> """ width = width or columns() # Preserve this for later, since we reuse `indent` as a variable initial_indent = indent # De-indent the text and remove any leading newlines text = textwrap.dedent(text).lstrip("\n") # Split the text into lines lines = text.split("\n") # Hey, if there's no lines, we do nothing, but this shouldn't ever happen if not lines: # pragma: no cover return # Iterate over the lines looking for differences in indentation, which # indicates a paragraph break c = 0 # The current paragraph index line = lines[0] # The current line we're parsing last_indent = len(line) - len(line.lstrip()) # Last line indent # List of paragraphs, primed with the first wrapped line fragment paragraphs = [line + " "] for i in range(1, len(lines)): # Strip trailing spaces, which may just be random whitespace line = lines[i].rstrip() # If the line is empty, it's a blank line, so we treat it specially if not line: paragraphs.append(line) c += 1 # Set the last indentation to -1, so it is always a new # paragraph last_indent = -1 continue # Get the current line's indentation indent = len(line) - len(line.lstrip()) # If it doesn't match the last line, it's a new paragraph if indent != last_indent: # Start a new unwrapped line c += 1 last_indent = indent paragraphs.append(line + " ") continue # If we got this far, the indentation matched, so it was part of the # same paragraph, and we just add it to the current paragraph paragraphs[c] += line.lstrip() + " " # Iterate over the paragraphs rewrapping them at 70 chrs for i in range(len(paragraphs)): # Get the paragraph as a single line line = paragraphs[i] # Calculate the indentation sub_indent = len(line) - len(line.lstrip()) sub_indent = initial_indent + (sub_indent * " ") # Wrap the line into a paragraph wrapper = textwrap.TextWrapper( initial_indent=initial_indent, subsequent_indent=sub_indent, width=width ) line = wrapper.fill(line) # Remove trailing whitespace paragraphs[i] = line.rstrip() # Join all the wrapped paragraphs into a single string text = "\n".join(paragraphs) return text