def __init__(self, args, table=None): if args.color == 'auto': self.table = MultiTable(initial_section=False, column_separator='|') elif args.color == 'off': styler = Styler() self.table = MultiTable(initial_section=False, column_separator='|', styler=styler) elif args.color == 'on': styler = ColorizedStyler() self.table = MultiTable(initial_section=False, column_separator='|', styler=styler) else: raise ValueError("Unknown color option: %s" % args.color)
def setUp(self): styler = Styler() self.table = MultiTable(initial_section=False, column_separator='|', styler=styler, auto_reformat=False) self.formatter = TableFormatter(Object(color='off')) self.formatter.table = self.table self.stream = six.StringIO()
class TestVerticalTableConversion(unittest.TestCase): def setUp(self): self.table = MultiTable() def test_convert_section_to_vertical(self): self.table.add_title("foo") self.table.add_row_header(["key1", "key2", "key3"]) self.table.add_row(["val1", "val2", "val3"]) convert_to_vertical_table(self.table._sections) # To convert to a vertical table, there should be no headers: section = self.table._sections[0] self.assertEqual(section.headers, []) # Then we should create a two column row with key val pairs. self.assertEqual(section.rows, [["key1", "val1"], ["key2", "val2"], ["key3", "val3"]])
def __init__(self, args, table=None): super(TableFormatter, self).__init__(args) if args.color == 'auto': self.table = MultiTable(initial_section=False, column_separator='|') elif args.color == 'off': styler = Styler() self.table = MultiTable(initial_section=False, column_separator='|', styler=styler) elif args.color == 'on': styler = ColorizedStyler() self.table = MultiTable(initial_section=False, column_separator='|', styler=styler) else: raise ValueError("Unknown color option: %s" % args.color)
class TestVerticalTableConversion(unittest.TestCase): def setUp(self): self.table = MultiTable() def test_convert_section_to_vertical(self): self.table.add_title('foo') self.table.add_row_header(['key1', 'key2', 'key3']) self.table.add_row(['val1', 'val2', 'val3']) convert_to_vertical_table(self.table._sections) # To convert to a vertical table, there should be no headers: section = self.table._sections[0] self.assertEqual(section.headers, []) # Then we should create a two column row with key val pairs. self.assertEqual( section.rows, [['key1', 'val1'], ['key2', 'val2'], ['key3', 'val3']])
def tabulate(operation, data): """ Formats a data structure as a table :param operation: the title of the table :param data: list or dict to print """ if data is None: return "" table = MultiTable(initial_section=False, column_separator='|', styler=Styler(), auto_reformat=False) formatter = TableFormatter( type('dummy', (object, ), { "color": "on", "query": None })) formatter.table = table stream = six.StringIO() formatter(operation, data, stream=stream) return stream.getvalue()
def setUp(self): self.table = MultiTable()
class TestMultiTable(unittest.TestCase): def setUp(self): self.table = MultiTable() def test_max_width_calculation(self): self.table.add_title('foo') self.table.add_row_header(['one', 'two', 'three']) self.table.add_row(['one', 'two', 'three']) self.table.new_section('bar') self.table.add_row_header(['one', 'two']) self.table.add_row(['12345', '1234567'])
class TableFormatter(FullyBufferedFormatter): """Pretty print a table from a given response. The table formatter is able to take any generic response and generate a pretty printed table. It does this without using the output definition from the model. """ def __init__(self, args, table=None): super(TableFormatter, self).__init__(args) if args.color == 'auto': self.table = MultiTable(initial_section=False, column_separator='|') elif args.color == 'off': styler = Styler() self.table = MultiTable(initial_section=False, column_separator='|', styler=styler) elif args.color == 'on': styler = ColorizedStyler() self.table = MultiTable(initial_section=False, column_separator='|', styler=styler) else: raise ValueError("Unknown color option: %s" % args.color) def _format_response(self, command_name, response, stream): if self._build_table(command_name, response): try: self.table.render(stream) except IOError: # If they're piping stdout to another process which exits before # we're done writing all of our output, we'll get an error about a # closed pipe which we can safely ignore. pass def _build_table(self, title, current, indent_level=0): if not current: return False if title is not None: self.table.new_section(title, indent_level=indent_level) if isinstance(current, list): if isinstance(current[0], dict): self._build_sub_table_from_list(current, indent_level, title) else: for item in current: if self._scalar_type(item): self.table.add_row([item]) elif all(self._scalar_type(el) for el in item): self.table.add_row(item) else: self._build_table(title=None, current=item) if isinstance(current, dict): # Render a single row section with keys as header # and the row as the values, unless the value # is a list. self._build_sub_table_from_dict(current, indent_level) return True def _build_sub_table_from_dict(self, current, indent_level): # Render a single row section with keys as header # and the row as the values, unless the value # is a list. headers, more = self._group_scalar_keys(current) if len(headers) == 1: # Special casing if a dict has a single scalar key/value pair. self.table.add_row([headers[0], current[headers[0]]]) elif headers: self.table.add_row_header(headers) self.table.add_row([current[k] for k in headers]) for remaining in more: self._build_table(remaining, current[remaining], indent_level=indent_level + 1) def _build_sub_table_from_list(self, current, indent_level, title): headers, more = self._group_scalar_keys_from_list(current) self.table.add_row_header(headers) first = True for element in current: if not first and more: self.table.new_section(title, indent_level=indent_level) self.table.add_row_header(headers) first = False # Use .get() to account for the fact that sometimes an element # may not have all the keys from the header. self.table.add_row([element.get(header, '') for header in headers]) for remaining in more: # Some of the non scalar attributes may not necessarily # be in every single element of the list, so we need to # check this condition before recursing. if remaining in element: self._build_table(remaining, element[remaining], indent_level=indent_level + 1) def _scalar_type(self, element): return not isinstance(element, (list, dict)) def _group_scalar_keys_from_list(self, list_of_dicts): # We want to make sure we catch all the keys in the list of dicts. # Most of the time each list element has the same keys, but sometimes # a list element will have keys not defined in other elements. headers = set() more = set() for item in list_of_dicts: current_headers, current_more = self._group_scalar_keys(item) headers.update(current_headers) more.update(current_more) headers = list(sorted(headers)) more = list(sorted(more)) return headers, more def _group_scalar_keys(self, current): # Given a dict, separate the keys into those whose values are # scalar, and those whose values aren't. Return two lists, # one is the scalar value keys, the second is the remaining keys. more = [] headers = [] for element in current: if self._scalar_type(current[element]): headers.append(element) else: more.append(element) headers.sort() more.sort() return headers, more
class TableFormatter(FullyBufferedFormatter): """Pretty print a table from a given response. The table formatter is able to take any generic response and generate a pretty printed table. It does this without using the output definition from the model. """ def __init__(self, args, table=None): super(TableFormatter, self).__init__(args) if args.color == 'auto': self.table = MultiTable(initial_section=False, column_separator='|') elif args.color == 'off': styler = Styler() self.table = MultiTable(initial_section=False, column_separator='|', styler=styler) elif args.color == 'on': styler = ColorizedStyler() self.table = MultiTable(initial_section=False, column_separator='|', styler=styler) else: raise ValueError("Unknown color option: %s" % args.color) def _format_response(self, operation, response, stream): if self._build_table(operation.name, response): try: self.table.render(stream) except IOError: # If they're piping stdout to another process which exits before # we're done writing all of our output, we'll get an error about a # closed pipe which we can safely ignore. pass def _build_table(self, title, current, indent_level=0): if not current: return False if title is not None: self.table.new_section(title, indent_level=indent_level) if isinstance(current, list): if isinstance(current[0], dict): self._build_sub_table_from_list(current, indent_level, title) else: for item in current: if self._scalar_type(item): self.table.add_row([item]) elif all(self._scalar_type(el) for el in item): self.table.add_row(item) else: self._build_table(title=None, current=item) if isinstance(current, dict): # Render a single row section with keys as header # and the row as the values, unless the value # is a list. self._build_sub_table_from_dict(current, indent_level) return True def _build_sub_table_from_dict(self, current, indent_level): # Render a single row section with keys as header # and the row as the values, unless the value # is a list. headers, more = self._group_scalar_keys(current) if len(headers) == 1: # Special casing if a dict has a single scalar key/value pair. self.table.add_row([headers[0], current[headers[0]]]) elif headers: self.table.add_row_header(headers) self.table.add_row([current[k] for k in headers]) for remaining in more: self._build_table(remaining, current[remaining], indent_level=indent_level + 1) def _build_sub_table_from_list(self, current, indent_level, title): headers, more = self._group_scalar_keys_from_list(current) self.table.add_row_header(headers) first = True for element in current: if not first and more: self.table.new_section(title, indent_level=indent_level) self.table.add_row_header(headers) first = False # Use .get() to account for the fact that sometimes an element # may not have all the keys from the header. self.table.add_row([element.get(header, '') for header in headers]) for remaining in more: # Some of the non scalar attributes may not necessarily # be in every single element of the list, so we need to # check this condition before recursing. if remaining in element: self._build_table(remaining, element[remaining], indent_level=indent_level + 1) def _scalar_type(self, element): return not isinstance(element, (list, dict)) def _group_scalar_keys_from_list(self, list_of_dicts): # We want to make sure we catch all the keys in the list of dicts. # Most of the time each list element has the same keys, but sometimes # a list element will have keys not defined in other elements. headers = set() more = set() for item in list_of_dicts: current_headers, current_more = self._group_scalar_keys(item) headers.update(current_headers) more.update(current_more) headers = list(sorted(headers)) more = list(sorted(more)) return headers, more def _group_scalar_keys(self, current): # Given a dict, separate the keys into those whose values are # scalar, and those whose values aren't. Return two lists, # one is the scalar value keys, the second is the remaining keys. more = [] headers = [] for element in current: if self._scalar_type(current[element]): headers.append(element) else: more.append(element) headers.sort() more.sort() return headers, more
class TableFormatter(Formatter): """Pretty print a table from a given response. The table formatter is able to take any generic response and generate a pretty printed table. It does this without using the output definition from the model. """ def __init__(self, args, table=None): if args.color == 'auto': self.table = MultiTable(initial_section=False, column_separator='|') elif args.color == 'off': styler = Styler() self.table = MultiTable(initial_section=False, column_separator='|', styler=styler) elif args.color == 'on': styler = ColorizedStyler() self.table = MultiTable(initial_section=False, column_separator='|', styler=styler) else: raise ValueError("Unknown color option: %s" % args.color) def __call__(self, operation, response, stream=None): if stream is None: # Retrieve stdout on invocation instead of at import time # so that if anything wraps stdout we'll pick up those changes # (specifically colorama on windows wraps stdout). stream = sys.stdout self._build_table(operation.name, response) try: self.table.render(stream) except IOError: # If they're piping stdout to another process which exits before # we're done writing all of our output, we'll get an error about a # closed pipe which we can safely ignore. pass def _build_table(self, title, current, indent_level=0): if not current: return self.table.new_section(title, indent_level=indent_level) if isinstance(current, list): if isinstance(current[0], dict): self._build_sub_table_from_list(current, indent_level, title) else: for item in current: self.table.add_row([item]) if isinstance(current, dict): # Render a single row section with keys as header # and the row as the values, unless the value # is a list. self._build_sub_table_from_dict(current, indent_level) def _build_sub_table_from_dict(self, current, indent_level): # Render a single row section with keys as header # and the row as the values, unless the value # is a list. headers, more = self._group_scalar_keys(current) if len(headers) == 1: # Special casing if a dict has a single scalar key/value pair. self.table.add_row([headers[0], current[headers[0]]]) elif headers: self.table.add_row_header(headers) self.table.add_row([current[k] for k in headers]) for remaining in more: self._build_table(remaining, current[remaining], indent_level=indent_level + 1) def _build_sub_table_from_list(self, current, indent_level, title): headers, more = self._group_scalar_keys(current[0]) self.table.add_row_header(headers) first = True for element in current: if not first and more: self.table.new_section(title, indent_level=indent_level) self.table.add_row_header(headers) first = False self.table.add_row([element[header] for header in headers]) for remaining in more: # Some of the non scalar attributes may not necessarily # be in every single element of the list, so we need to # check this condition before recursing. if remaining in element: self._build_table(remaining, element[remaining], indent_level=indent_level + 1) def _scalar_type(self, element): return not isinstance(element, (list, dict)) def _group_scalar_keys(self, current): # Given a dict, separate the keys into those whose values are # scalar, and those whose values aren't. Return two lists, # one is the scalar value keys, the second is the remaining keys. more = [] headers = [] for element in current: if self._scalar_type(current[element]): headers.append(element) else: more.append(element) headers.sort() more.sort() return headers, more
class TestMultiTable(unittest.TestCase): def setUp(self): self.table = MultiTable() def test_max_width_calculation(self): self.table.add_title("foo") self.table.add_row_header(["one", "two", "three"]) self.table.add_row(["one", "two", "three"]) self.table.new_section("bar") self.table.add_row_header(["one", "two"]) self.table.add_row(["12345", "1234567"])
from awscli.compat import six from awscli.formatter import TableFormatter from awscli.table import MultiTable, ColorizedStyler import sys import json class Object(object): def __init__(self, **kwargs): self.__dict__.update(kwargs) self.query = None stream = six.StringIO() styler = ColorizedStyler() table = MultiTable(initial_section=False, column_separator='|', styler=styler) formatter = TableFormatter(Object(color='on')) formatter.table = table DATA = [] for line in sys.stdin.readlines(): if line.strip(): DATA.append(json.loads(line)) formatter(sys.argv[1], DATA, stream=stream) output = stream.getvalue() if output: print(output)