예제 #1
0
 def testIssue1(self):
   self.assertEqual(10, len(terminal.StripAnsiText('boembabies' '\033[0m')))
   terminal.TerminalSize = lambda: (10, 10)
   text1 = terminal.LineWrap('\033[32m' + 'boembabies, ' * 10 + 'boembabies' +
                             '\033[0m', omit_sgr=True)
   text2 = ('\033[32m' +
            terminal.LineWrap('boembabies, ' * 10 + 'boembabies') +
            '\033[0m')
   self.assertEqual(text1, text2)
예제 #2
0
    def _TextJustify(self, text, col_size):
        """Formats text within column with white space padding.

    A single space is prefixed, and a number of spaces are added as a
    suffix such that the length of the resultant string equals the col_size.

    If the length of the text exceeds the column width available then it
    is split into words and returned as a list of string, each string
    contains one or more words padded to the column size.

    Args:
      text: String of text to format.
      col_size: integer size of column to pad out the text to.

    Returns:
      List of strings col_size in length.

    Raises:
      TableError: If col_size is too small to fit the words in the text.
    """
        result = []
        if '\n' in text:
            for paragraph in text.split('\n'):
                result.extend(self._TextJustify(paragraph, col_size))
            return result

        wrapper = textwrap.TextWrapper(width=col_size - 2,
                                       break_long_words=False,
                                       expand_tabs=False)
        try:
            text_list = wrapper.wrap(text)
        except ValueError:
            raise TableError('Field too small (minimum width: 3)')

        if not text_list:
            return [' ' * col_size]

        for current_line in text_list:
            stripped_len = len(terminal.StripAnsiText(current_line))
            ansi_color_adds = len(current_line) - stripped_len
            # +2 for white space on either side.
            if stripped_len + 2 > col_size:
                raise TableError(
                    'String contains words that do not fit in column.')

            result.append(' %-*s' %
                          (col_size - 1 + ansi_color_adds, current_line))

        return result
예제 #3
0
    def _SmallestColSize(self, text):
        """Finds the largest indivisible word of a string.

    ...and thus the smallest possible column width that can contain that
    word unsplit over rows.

    Args:
      text: A string of text potentially consisting of words.

    Returns:
      Integer size of the largest single word in the text.
    """
        if not text:
            return 0
        stripped = terminal.StripAnsiText(text)
        return max(len(word) for word in stripped.split())
예제 #4
0
    def FormattedTable(self,
                       width=80,
                       force_display=False,
                       ml_delimiter=True,
                       color=True,
                       display_header=True,
                       columns=None):
        """Returns whole table, with whitespace padding and row delimiters.

    Args:
      width: An int, the max width we want the table to fit in.
      force_display: A bool, if set to True will display table when the table
          can't be made to fit to the width.
      ml_delimiter: A bool, if set to False will not display the multi-line
          delimiter.
      color: A bool. If true, display any colours in row.colour.
      display_header: A bool. If true, display header.
      columns: A list of str, show only columns with these names.

    Returns:
      A string.  The tabled output.

    Raises:
      TableError: Width too narrow to display table.
    """
        def _FilteredCols():
            """Returns list of column names to display."""
            if not columns:
                return self._Header().values
            return [col for col in self._Header().values if col in columns]

        # Largest is the biggest data entry in a column.
        largest = {}
        # Smallest is the same as above but with linewrap i.e. largest unbroken
        # word in the data stream.
        smallest = {}
        # largest == smallest for a column with a single word of data.
        # Initialise largest and smallest for all columns.
        for key in _FilteredCols():
            largest[key] = 0
            smallest[key] = 0

        # Find the largest and smallest values.
        # Include Title line in equation.
        # pylint: disable=E1103
        for row in self._table:
            for key, value in row.items():
                if key not in _FilteredCols():
                    continue
                # Convert lists into a string.
                if isinstance(value, list):
                    value = ', '.join(value)
                value = terminal.StripAnsiText(value)
                largest[key] = max(len(value), largest[key])
                smallest[key] = max(self._SmallestColSize(value),
                                    smallest[key])
        # pylint: enable=E1103

        min_total_width = 0
        multi_word = []
        # Bump up the size of each column to include minimum pad.
        # Find all columns that can be wrapped (multi-line).
        # And the minimum width needed to display all columns (even if wrapped).
        for key in _FilteredCols():
            # Each column is bracketed by a space on both sides.
            # So increase size required accordingly.
            largest[key] += 2
            smallest[key] += 2
            min_total_width += smallest[key]
            # If column contains data that 'could' be split over multiple lines.
            if largest[key] != smallest[key]:
                multi_word.append(key)

        # Check if we have enough space to display the table.
        if min_total_width > width and not force_display:
            raise TableError('Width too narrow to display table.')

        # We have some columns that may need wrapping over several lines.
        if multi_word:
            # Find how much space is left over for the wrapped columns to use.
            # Also find how much space we would need if they were not wrapped.
            # These are 'spare_width' and 'desired_width' respectively.
            desired_width = 0
            spare_width = width - min_total_width
            for key in multi_word:
                spare_width += smallest[key]
                desired_width += largest[key]

            # Scale up the space we give each wrapped column.
            # Proportional to its size relative to 'desired_width' for all columns.
            # Rinse and repeat if we changed the wrap list in this iteration.
            # Once done we will have a list of columns that definitely need wrapping.
            done = False
            while not done:
                done = True
                for key in multi_word:
                    # If we scale past the desired width for this particular column,
                    # then give it its desired width and remove it from the wrapped list.
                    if (largest[key] <= round(
                        (largest[key] / float(desired_width)) * spare_width)):
                        smallest[key] = largest[key]
                        multi_word.remove(key)
                        spare_width -= smallest[key]
                        desired_width -= largest[key]
                        done = False
                    # If we scale below the minimum width for this particular column,
                    # then leave it at its minimum and remove it from the wrapped list.
                    elif (smallest[key] >= round(
                        (largest[key] / float(desired_width)) * spare_width)):
                        multi_word.remove(key)
                        spare_width -= smallest[key]
                        desired_width -= largest[key]
                        done = False

            # Repeat the scaling algorithm with the final wrap list.
            # This time we assign the extra column space by increasing 'smallest'.
            for key in multi_word:
                smallest[key] = int(
                    round((largest[key] / float(desired_width)) * spare_width))

        total_width = 0
        row_count = 0
        result_dict = {}
        # Format the header lines and add to result_dict.
        # Find what the total width will be and use this for the ruled lines.
        # Find how many rows are needed for the most wrapped line (row_count).
        for key in _FilteredCols():
            result_dict[key] = self._TextJustify(key, smallest[key])
            if len(result_dict[key]) > row_count:
                row_count = len(result_dict[key])
            total_width += smallest[key]

        # Store header in header_list, working down the wrapped rows.
        header_list = []
        for row_idx in range(row_count):
            for key in _FilteredCols():
                try:
                    header_list.append(result_dict[key][row_idx])
                except IndexError:
                    # If no value than use whitespace of equal size.
                    header_list.append(' ' * smallest[key])
            header_list.append('\n')

        # Format and store the body lines
        result_dict = {}
        body_list = []
        # We separate multi line rows with a single line delimiter.
        prev_muli_line = False
        # Unless it is the first line in which there is already the header line.
        first_line = True
        for row in self:
            row_count = 0
            for key, value in row.items():
                if key not in _FilteredCols():
                    continue
                # Convert field contents to a string.
                if isinstance(value, list):
                    value = ', '.join(value)
                # Store results in result_dict and take note of wrapped line count.
                result_dict[key] = self._TextJustify(value, smallest[key])
                if len(result_dict[key]) > row_count:
                    row_count = len(result_dict[key])

            if row_count > 1:
                prev_muli_line = True
            # If current or prior line was multi-line then include delimiter.
            if not first_line and prev_muli_line and ml_delimiter:
                body_list.append('-' * total_width + '\n')
                if row_count == 1:
                    # Our current line was not wrapped, so clear flag.
                    prev_muli_line = False

            row_list = []
            for row_idx in range(row_count):
                for key in _FilteredCols():
                    try:
                        row_list.append(result_dict[key][row_idx])
                    except IndexError:
                        # If no value than use whitespace of equal size.
                        row_list.append(' ' * smallest[key])
                row_list.append('\n')

            if color and row.color is not None:
                body_list.append(
                    terminal.AnsiText(''.join(row_list)[:-1],
                                      command_list=row.color))
                body_list.append('\n')
            else:
                body_list.append(''.join(row_list))

            first_line = False

        header = ''.join(header_list) + '=' * total_width
        if color and self._Header().color is not None:
            header = terminal.AnsiText(header,
                                       command_list=self._Header().color)
        # Add double line delimiter between header and main body.
        if display_header:
            return '%s\n%s' % (header, ''.join(body_list))
        return '%s' % ''.join(body_list)
예제 #5
0
 def testStripAnsi(self):
   text = 'ansi length'
   self.assertEqual(text, terminal.StripAnsiText(text))
   ansi_text = '\033[5;32;44mansi\033[0m length'
   self.assertEqual(text, terminal.StripAnsiText(ansi_text))