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-msg=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 xrange(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 xrange(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)
dataset/ETHPy150Open google/textfsm/texttable.py/TextTable.FormattedTable/original