diff --git a/cli/src/katello/client/utils/printer.py b/cli/src/katello/client/utils/printer.py index 923defaa054..37753b1c0d3 100644 --- a/cli/src/katello/client/utils/printer.py +++ b/cli/src/katello/client/utils/printer.py @@ -23,14 +23,45 @@ class PrinterStrategy: + """ + Strategy of formatting the data and printing them on the output. + """ def print_item(self, heading, columns, item): + """ + Print one item + @type heading: string + @param heading: Title for the list of items + @type columns: list of dicts + @param columns: definition of columns + @type item: dict + @param item: data to be printed, one item + """ self.print_items(heading, columns, [item]) def print_items(self, heading, columns, items): + """ + Print list of items + @type heading: string + @param heading: Title for the list of items + @type columns: list of dicts + @param columns: definition of columns + @type items: list of dicts + @param items: data to be printed, list of items + """ pass def _get_column_value(self, column, item): + """ + Returns string that should be displayed in the column. + It's either a given value or attribute of the item. Formatters + are applied if they are available. + @type column: dict + @param column: column definition + @type item: dict + @param item: data to get the value from + @rtype: string + """ value = item.get(column['attr_name'], column.get('value', None)) value_format_func = column.get('formatter', column.get('value_formatter', None)) item_format_func = column.get('item_formatter', None) @@ -41,10 +72,18 @@ def _get_column_value(self, column, item): value = item_format_func(item) return value + class VerboseStrategy(PrinterStrategy): def print_items(self, heading, columns, items): """ + Print list of items + @type heading: string + @param heading: Title for the list of items + @type columns: list of dicts + @param columns: definition of columns + @type items: list of dicts + @param items: data to be printed, list of items """ self._print_header(heading) for item in items: @@ -63,6 +102,11 @@ def _print_header(self, heading): def _print_item(self, item, columns): """ + Print one record. + @type item: hash + @param item: data to print + @type columns: list of dicts + @param columns: columns definition """ print for column in columns: @@ -72,15 +116,19 @@ def _print_item(self, item, columns): continue if not column.get('multiline', False): - col_width = self._column_width(columns) + col_width = self._max_label_width(columns) print ("{0:<" + u_str(col_width + 1) + "} {1}").format(u_str(column['name'])+":", u_str(value)) # +1 to account for the : after the column name else: print column['name']+":" print indent_text(value, " ") - def _column_width(self, columns): + def _max_label_width(self, columns): """ + Returns maximum width of the column labels. + @type columns: list of dicts + @param columns: columns definition + @rtype: int """ width = 0 for column in columns: @@ -90,14 +138,27 @@ def _column_width(self, columns): class GrepStrategy(PrinterStrategy): + """ + Prints data into a grid that can be grepped easily. + String to divide the columns can be set optionally. + """ def __init__(self, delimiter=None): """ + @type delimiter: string + @param delimiter: delimiter for dividing the grid columns """ self.__delim = delimiter if delimiter else "" def print_items(self, heading, columns, items): """ + Print list of items + @type heading: string + @param heading: Title for the list of items + @type columns: list of dicts + @param columns: definition of columns + @type items: list of dicts + @param items: data to be printed, list of items """ column_widths = self._calc_column_widths(items, columns) self._print_header(heading, columns, column_widths) @@ -107,9 +168,13 @@ def print_items(self, heading, columns, items): def _print_header(self, heading, columns, column_widths): """ - Print a fancy header to stdout. + Print a fancy header with column labels to stdout. @type heading: string or list of strings @param heading: headers to be displayed + @type columns: list of dicts + @param columns: columns definition + @type column_widths: dict + @param column_widths: dictionary that holds maximal widths of columns {attr_name -> width} """ print_line() print center_text(heading) @@ -129,6 +194,10 @@ def _print_item(self, item, columns, column_widths): Print item of a list on single line @type item: hash @param item: data to print + @type columns: list of dicts + @param columns: columns definition + @type column_widths: + @param column_widths: """ print self.__delim, for column in columns: @@ -150,6 +219,13 @@ def _print_item(self, item, columns, column_widths): def _column_width(self, items, column): """ + Returns maximum width for the column to ensure that all the data + and the label fits in. + @type columns: list of dicts + @param columns: columns definition + @type column: dict + @param column: column definition + @rtype: int """ key = column['attr_name'] width = len(column['name'])+1 @@ -160,6 +236,12 @@ def _column_width(self, items, column): def _calc_column_widths(self, items, columns): """ + Counts and retunrs dictionary that holds maximal widths of all columns {attr_name -> width} + @type items: list of dicts + @param items: data to be printed + @type columns: list of dicts + @param columns: columns definition + @rtype: dict """ widths = {} for column in columns: @@ -169,38 +251,42 @@ def _calc_column_widths(self, items, columns): class Printer: """ - Class for unified printing of the CLI output. + Unified interface for printing data in CLI. """ def __init__(self, strategy=None): + """ + @type strategy: PrinterStrategy + @param strategy: strategy that is used for formatting the output. + """ self.__printer_strategy = strategy self.__columns = [] self.__heading = "" def set_header(self, heading): """ + Sets label for a fancy header that is printed above the data. + @type heading: string """ self.__heading = heading def set_strategy(self, strategy): """ + Sets formatting strategy + @type strategy: PrinterStrategy + @param strategy: strategy that is used for formatting the output. """ self.__printer_strategy = strategy def add_column(self, attr_name, name = None, **kwargs): """ - Add column to display + Add column of data thet will be displayed @type attr_name: string @param attr_name: key to data hash @type name: string - @param name: display name of the column. It is automatically transformed from display name - if not set. - @type multiline: bool - @param multiline: flag to mark multiline values - @type show_in_grep: bool - @param show_in_grep: flag to set whether the column should be displayed also in grep mode or not - @type value: string - @param value: value that should be used rather than accessing the data hash + @param name: label for the column. It is generated automatically attr_name if it's not set. + @type kwargs: dict + @param kwargs: other parameters that are passed to the printer strategy """ col = kwargs col['attr_name'] = attr_name @@ -209,6 +295,9 @@ def add_column(self, attr_name, name = None, **kwargs): def print_item(self, item): """ + Print one record + @type item: dict + @param item: data to be printed """ if not self.__printer_strategy: self.set_strategy(VerboseStrategy()) @@ -216,6 +305,9 @@ def print_item(self, item): def print_items(self, items): """ + Print list of records + @type items: list of dicts + @param items: data to be printed """ if not self.__printer_strategy: self.set_strategy(GrepStrategy()) @@ -227,11 +319,14 @@ def __attr_to_name(self, attr_name): oraganization_id -> Organization Id @type attr_name: string @param attr_name: attribute name + @rtype: string """ return " ".join([part[0].upper() + part[1:] for part in attr_name.split("_")]) def __filtered_columns(self): """ + @return: list of columns that can be printed with current strategy + @rtype: list of column definition dicts """ filtered = [] for column in self.__columns: @@ -241,10 +336,14 @@ def __filtered_columns(self): return filtered - -# indent block of text -------------------------------------------------------- def indent_text(text, indent="\t"): """ + Indents given text. + @type text: string or list of strings + @param text: text to be indented + @type indent: string + @param indent: value that is added at the beggining of each line of the text + @rtype: string """ if not text: text = u_str(None) @@ -256,9 +355,14 @@ def indent_text(text, indent="\t"): return indent_text(text.split("\n"), indent) -# converts block of text to one line ------------------------------------------ def text_to_line(text, glue=" "): """ + Squeezes a block of text to one line. + @type text: string + @param text: text to be processed + @type glue: string + @param glue: string used for joining lines of the text + @rtype: string """ if not text: text = u_str(None) @@ -271,6 +375,13 @@ def text_to_line(text, glue=" "): def center_text(text, width = None): """ + Centers block of text in given width. + @type text: string + @param text: text to be processed + @type width: int + @param width: width of space the text should be centered to. If no width is given, + full terminal size is used. + @rtype: string """ if not width: width = get_term_width() @@ -286,6 +397,10 @@ def center_text(text, width = None): def print_line(width = None): """ + Prints line of characters '-' to stdout + @type width: int + @param width: width of the line in characters. If no width is given, + full terminal size is used. """ if not width: width = get_term_width() @@ -295,6 +410,7 @@ def print_line(width = None): def get_term_width(): """ returns terminal width (tested only with Linux) + @rtype: int """ try: import fcntl