Blame boom/report.py

Packit Service f2d567
# Copyright (C) 2017 Red Hat, Inc., Bryn M. Reeves <bmr@redhat.com>
Packit Service f2d567
#
Packit Service f2d567
# boom/report.py - Text reporting
Packit Service f2d567
#
Packit Service f2d567
# This file is part of the boom project.
Packit Service f2d567
#
Packit Service f2d567
# This copyrighted material is made available to anyone wishing to use,
Packit Service f2d567
# modify, copy, or redistribute it subject to the terms and conditions
Packit Service f2d567
# of the GNU General Public License v.2.
Packit Service f2d567
#
Packit Service f2d567
# You should have received a copy of the GNU Lesser General Public License
Packit Service f2d567
# along with this program; if not, write to the Free Software Foundation,
Packit Service f2d567
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Packit Service f2d567
"""The Boom reporting module contains a set of classes for creating
Packit Service f2d567
simple text based tabular reports for a user-defined set of object
Packit Service f2d567
types and fields. No restrictions are placed on the types of object
Packit Service f2d567
that can be reported: users of the ``BoomReport`` classes may define
Packit Service f2d567
additional object types outside the ``boom`` package and include these
Packit Service f2d567
types in reports generated by the module.
Packit Service f2d567
Packit Service f2d567
The fields displayed in a specific report may be selected from the
Packit Service f2d567
available set of fields by specifying a simple comma-separated string
Packit Service f2d567
list of field names (in display order). In addition, custom multi-column
Packit Service f2d567
sorting is possible using a similar string notation.
Packit Service f2d567
Packit Service f2d567
The ``BoomReport`` module is closely based on the ``device-mapper``
Packit Service f2d567
reporting engine and shares many features and behaviours with device
Packit Service f2d567
mapper reports.
Packit Service f2d567
"""
Packit Service f2d567
from __future__ import print_function
Packit Service f2d567
Packit Service f2d567
from boom import find_minimum_sha_prefix, BOOM_DEBUG_REPORT
Packit Service f2d567
import logging
Packit Service f2d567
import sys
Packit Service f2d567
Packit Service f2d567
_log = logging.getLogger(__name__)
Packit Service f2d567
_log.set_debug_mask(BOOM_DEBUG_REPORT)
Packit Service f2d567
Packit Service f2d567
_log_debug = _log.debug
Packit Service f2d567
_log_debug_report = _log.debug_masked
Packit Service f2d567
_log_info = _log.info
Packit Service f2d567
_log_warn = _log.warning
Packit Service f2d567
_log_error = _log.error
Packit Service f2d567
Packit Service f2d567
_default_columns = 80
Packit Service f2d567
Packit Service f2d567
REP_NUM = "num"
Packit Service f2d567
REP_STR = "str"
Packit Service f2d567
REP_SHA = "sha"
Packit Service f2d567
Packit Service f2d567
_dtypes = [REP_NUM, REP_STR, REP_SHA]
Packit Service f2d567
Packit Service f2d567
Packit Service f2d567
_default_width = 8
Packit Service f2d567
Packit Service f2d567
ALIGN_LEFT = "left"
Packit Service f2d567
ALIGN_RIGHT = "right"
Packit Service f2d567
Packit Service f2d567
_align_types = [ALIGN_LEFT, ALIGN_RIGHT]
Packit Service f2d567
Packit Service f2d567
ASCENDING = "ascending"
Packit Service f2d567
DESCENDING = "descending"
Packit Service f2d567
Packit Service f2d567
STANDARD_QUOTE = "'"
Packit Service f2d567
STANDARD_PAIR = "="
Packit Service f2d567
Packit Service f2d567
MIN_SHA_WIDTH = 7
Packit Service f2d567
Packit Service f2d567
Packit Service f2d567
# Python2 vs. Python2 string types
Packit Service f2d567
try:
Packit Service f2d567
    # Py2
Packit Service f2d567
    string_types = (str, unicode)
Packit Service f2d567
except NameError:
Packit Service f2d567
    # Py3
Packit Service f2d567
    string_types = (str)
Packit Service f2d567
Packit Service f2d567
num_types = (int, float)
Packit Service f2d567
Packit Service f2d567
Packit Service f2d567
class BoomReportOpts(object):
Packit Service f2d567
    """BoomReportOpts()
Packit Service f2d567
        Options controlling the formatting and output of a boom report.
Packit Service f2d567
    """
Packit Service f2d567
    columns = 0
Packit Service f2d567
    headings = True
Packit Service f2d567
    buffered = True
Packit Service f2d567
    separator = None
Packit Service f2d567
    field_name_prefix = None
Packit Service f2d567
    unquoted = True
Packit Service f2d567
    aligned = True
Packit Service f2d567
    columns_as_rows = False
Packit Service f2d567
    report_file = None
Packit Service f2d567
Packit Service f2d567
    def __init__(self, columns=_default_columns, headings=True, buffered=True,
Packit Service f2d567
                 separator=" ", field_name_prefix="", unquoted=True,
Packit Service f2d567
                 aligned=True, report_file=sys.stdout):
Packit Service f2d567
        """Initialise BoomReportOpts object.
Packit Service f2d567
Packit Service f2d567
            Initialise a ``BoomReportOpts`` object to control output
Packit Service f2d567
            of a ``BoomReport``.
Packit Service f2d567
Packit Service f2d567
            :param columns: the number of columns to use for output.
Packit Service f2d567
            :param headings: a boolean indicating whether to output
Packit Service f2d567
                             column headings for this report.
Packit Service f2d567
            :param buffered: a boolean indicating whether to buffer
Packit Service f2d567
                             output from this report.
Packit Service f2d567
            :param report_file: a file to which output will be sent.
Packit Service f2d567
            :returns: a new ``BoomReportOpts`` object.
Packit Service f2d567
            :rtype: ``<class BoomReportOpts>``
Packit Service f2d567
        """
Packit Service f2d567
        self.columns = columns
Packit Service f2d567
        self.headings = headings
Packit Service f2d567
        self.buffered = buffered
Packit Service f2d567
        self.separator = separator
Packit Service f2d567
        self.field_name_prefix = field_name_prefix
Packit Service f2d567
        self.unquoted = unquoted
Packit Service f2d567
        self.aligned = aligned
Packit Service f2d567
        self.report_file = report_file
Packit Service f2d567
Packit Service f2d567
Packit Service f2d567
class BoomReportObjType(object):
Packit Service f2d567
    """BoomReportObjType()
Packit Service f2d567
        Class representing a type of objecct to be reported on.
Packit Service f2d567
        Instances of ``BoomReportObjType`` must specify an identifier,
Packit Service f2d567
        a description, and a data function that will return the correct
Packit Service f2d567
        type of object from a compound object containing data objects
Packit Service f2d567
        of different types. For reports that use only a single object
Packit Service f2d567
        type the ``data_fn`` member may be simply ``lambda x: x``.
Packit Service f2d567
    """
Packit Service f2d567
Packit Service f2d567
    objtype = -1
Packit Service f2d567
    desc = ""
Packit Service f2d567
    prefix = ""
Packit Service f2d567
    data_fn = None
Packit Service f2d567
Packit Service f2d567
    def __init__(self, objtype, desc, prefix, data_fn):
Packit Service f2d567
        """Initialise BoomReportObjType.
Packit Service f2d567
Packit Service f2d567
            Initialise a new ``BoomReportObjType`` object with the
Packit Service f2d567
            specified ``objtype``, ``desc``, optional ``prefix`` and
Packit Service f2d567
            ``data_fn``. The ``objtype`` must be an integer power of two
Packit Service f2d567
            that is unique within a given report. The ``data_fn`` should
Packit Service f2d567
            accept an object as its only argument and return an object
Packit Service f2d567
            of the requested type.
Packit Service f2d567
        """
Packit Service f2d567
        if not objtype or objtype < 0:
Packit Service f2d567
            raise ValueError("BoomReportObjType objtype cannot be <= 0.")
Packit Service f2d567
Packit Service f2d567
        if not desc:
Packit Service f2d567
            raise ValueError("BoomReportObjType desc cannot be empty.")
Packit Service f2d567
Packit Service f2d567
        if not data_fn:
Packit Service f2d567
            raise ValueError("BoomReportObjType requires data_fn.")
Packit Service f2d567
Packit Service f2d567
        self.objtype = objtype
Packit Service f2d567
        self.desc = desc
Packit Service f2d567
        self.prefix = prefix
Packit Service f2d567
        self.data_fn = data_fn
Packit Service f2d567
Packit Service f2d567
Packit Service f2d567
class BoomFieldType(object):
Packit Service f2d567
    """BoomFieldType()
Packit Service f2d567
        The ``BoomFieldType`` class describes the properties of a field
Packit Service f2d567
        available in a ``BoomReport`` instance.
Packit Service f2d567
    """
Packit Service f2d567
    objtype = -1
Packit Service f2d567
    name = None
Packit Service f2d567
    head = None
Packit Service f2d567
    desc = None
Packit Service f2d567
    width = _default_width
Packit Service f2d567
    align = None
Packit Service f2d567
    dtype = None
Packit Service f2d567
    report_fn = None
Packit Service f2d567
Packit Service f2d567
    def __init__(self, objtype, name, head, desc, width, dtype, report_fn,
Packit Service f2d567
                 align=None):
Packit Service f2d567
        """Initialise new BoomFieldType object.
Packit Service f2d567
Packit Service f2d567
            Initialise a new ``BoomFieldType`` object with the specified
Packit Service f2d567
            properties.
Packit Service f2d567
Packit Service f2d567
            :param objtype: The numeric object type ID (power of two)
Packit Service f2d567
            :param name: The field name used to select display fields
Packit Service f2d567
            :param desc: A human-readable description of the field
Packit Service f2d567
            :param width: The default (initial) field width
Packit Service f2d567
            :param dtype: The BoomReport data type of the field
Packit Service f2d567
            :param report_fn: The field reporting function
Packit Service f2d567
            :param align: The field alignment value
Packit Service f2d567
            :returns: A new BoomReportFieldType object
Packit Service f2d567
            :rtype: BoomReportFieldType
Packit Service f2d567
        """
Packit Service f2d567
        if not objtype:
Packit Service f2d567
            raise ValueError("'objtype' must be non-zero")
Packit Service f2d567
        if not name:
Packit Service f2d567
            raise ValueError("'name' is required")
Packit Service f2d567
        self.objtype = objtype
Packit Service f2d567
        self.name = name
Packit Service f2d567
        self.head = head
Packit Service f2d567
        self.desc = desc
Packit Service f2d567
Packit Service f2d567
        if dtype not in _dtypes:
Packit Service f2d567
            raise ValueError("Invalid field dtype: %s " % dtype)
Packit Service f2d567
Packit Service f2d567
        if align and align not in _align_types:
Packit Service f2d567
            raise ValueError("Invalid field alignment: %s" % align)
Packit Service f2d567
Packit Service f2d567
        self.dtype = dtype
Packit Service f2d567
        self.report_fn = report_fn
Packit Service f2d567
Packit Service f2d567
        if not align:
Packit Service f2d567
            if dtype == REP_STR or dtype == REP_SHA:
Packit Service f2d567
                self.align = ALIGN_LEFT
Packit Service f2d567
            if dtype == REP_NUM:
Packit Service f2d567
                self.align = ALIGN_RIGHT
Packit Service f2d567
        else:
Packit Service f2d567
            self.align = align
Packit Service f2d567
Packit Service f2d567
        if width < 0:
Packit Service f2d567
            raise ValueError("Field width cannot be < 0")
Packit Service f2d567
        self.width = width if width else _default_width
Packit Service f2d567
Packit Service f2d567
Packit Service f2d567
class BoomFieldProperties(object):
Packit Service f2d567
    field_num = None
Packit Service f2d567
    # sort_posn
Packit Service f2d567
    initial_width = 0
Packit Service f2d567
    width = 0
Packit Service f2d567
    objtype = None
Packit Service f2d567
    dtype = None
Packit Service f2d567
    align = None
Packit Service f2d567
    #
Packit Service f2d567
    # Field flags
Packit Service f2d567
    #
Packit Service f2d567
    hidden = False
Packit Service f2d567
    implicit = False
Packit Service f2d567
    sort_key = False
Packit Service f2d567
    sort_dir = None
Packit Service f2d567
    compact_one = False  # used for implicit fields
Packit Service f2d567
    compacted = False
Packit Service f2d567
    sort_posn = None
Packit Service f2d567
Packit Service f2d567
Packit Service f2d567
class BoomField(object):
Packit Service f2d567
    """BoomField()
Packit Service f2d567
        A ``BoomField`` represents an instance of a ``BoomFieldType``
Packit Service f2d567
        including its associated data values.
Packit Service f2d567
    """
Packit Service f2d567
    #: reference to the containing BoomReport
Packit Service f2d567
    _report = None
Packit Service f2d567
    #: reference to the BoomFieldProperties describing this field
Packit Service f2d567
    _props = None
Packit Service f2d567
    #: The formatted string to be reported for this field.
Packit Service f2d567
    report_string = None
Packit Service f2d567
    #: The raw value of this field. Used for sorting.
Packit Service f2d567
    sort_value = None
Packit Service f2d567
Packit Service f2d567
    def __init__(self, report, props):
Packit Service f2d567
        """Initialise a new BoomField object.
Packit Service f2d567
Packit Service f2d567
            Initialise a BoomField object and configure the supplied
Packit Service f2d567
            ``report`` and ``props`` attributes.
Packit Service f2d567
Packit Service f2d567
            :param report: The BoomReport that owns this field
Packit Service f2d567
            :param props: The BoomFieldProperties object for this field
Packit Service f2d567
        """
Packit Service f2d567
        self._report = report
Packit Service f2d567
        self._props = props
Packit Service f2d567
Packit Service f2d567
    def report_str(self, value):
Packit Service f2d567
        """Report a string value for this BoomField object.
Packit Service f2d567
Packit Service f2d567
            Set the value for this field to the supplied ``value``.
Packit Service f2d567
Packit Service f2d567
            :param value: The string value to set
Packit Service f2d567
            :rtype: None
Packit Service f2d567
        """
Packit Service f2d567
        if not isinstance(value, string_types):
Packit Service f2d567
            raise TypeError("Value for report_str() must be a string type.")
Packit Service f2d567
        self.set_value(value, sort_value=value)
Packit Service f2d567
Packit Service f2d567
    def report_sha(self, value):
Packit Service f2d567
        """Report a SHA value for this BoomField object.
Packit Service f2d567
Packit Service f2d567
            Set the value for this field to the supplied ``value``.
Packit Service f2d567
Packit Service f2d567
            :param value: The SHA value to set
Packit Service f2d567
            :rtype: None
Packit Service f2d567
        """
Packit Service f2d567
        if not isinstance(value, string_types):
Packit Service f2d567
            raise TypeError("Value for report_sha() must be a string type.")
Packit Service f2d567
        self.set_value(value, sort_value=value)
Packit Service f2d567
Packit Service f2d567
    def report_num(self, value):
Packit Service f2d567
        """Report a numeric value for this BoomField object.
Packit Service f2d567
Packit Service f2d567
            Set the value for this field to the supplied ``value``.
Packit Service f2d567
Packit Service f2d567
            :param value: The numeric value to set
Packit Service f2d567
            :rtype: None
Packit Service f2d567
        """
Packit Service f2d567
        if value is not None and not isinstance(value, num_types):
Packit Service f2d567
            raise TypeError("Value for report_num() must be a numeric type.")
Packit Service f2d567
        report_string = str(value)
Packit Service f2d567
        sort_value = value if value is not None else -1
Packit Service f2d567
        self.set_value(report_string, sort_value=sort_value)
Packit Service f2d567
Packit Service f2d567
    def set_value(self, report_string, sort_value=None):
Packit Service f2d567
        """Report an arbitrary value for this BoomField object.
Packit Service f2d567
Packit Service f2d567
            Set the value for this field to the supplied ``value``,
Packit Service f2d567
            and set the field's ``sort_value`` to the supplied
Packit Service f2d567
            ``sort_value``.
Packit Service f2d567
Packit Service f2d567
            :param report_string: The string value to set
Packit Service f2d567
            :param sort_value: The sort value
Packit Service f2d567
            :rtype: None
Packit Service f2d567
        """
Packit Service f2d567
        if report_string is None:
Packit Service f2d567
            raise ValueError("No value assigned to field.")
Packit Service f2d567
        self.report_string = report_string
Packit Service f2d567
        self.sort_value = sort_value if sort_value else report_string
Packit Service f2d567
Packit Service f2d567
Packit Service f2d567
class BoomRow(object):
Packit Service f2d567
    """BoomRow()
Packit Service f2d567
        A class representing a single data row making up a report.
Packit Service f2d567
    """
Packit Service f2d567
    #: the report that this BoomRow belongs to
Packit Service f2d567
    _report = None
Packit Service f2d567
    #: the list of report fields in display order
Packit Service f2d567
    _fields = None
Packit Service f2d567
    #: fields in sort order
Packit Service f2d567
    _sort_fields = None
Packit Service f2d567
Packit Service f2d567
    def __init__(self, report):
Packit Service f2d567
        self._report = report
Packit Service f2d567
        self._fields = []
Packit Service f2d567
Packit Service f2d567
    def add_field(self, field):
Packit Service f2d567
        """Add a field to this BoomRow.
Packit Service f2d567
Packit Service f2d567
            :param field: The field to be added
Packit Service f2d567
            :rtype: None
Packit Service f2d567
        """
Packit Service f2d567
        self._fields.append(field)
Packit Service f2d567
Packit Service f2d567
Packit Service f2d567
def __none_returning_fn(obj):
Packit Service f2d567
    """Dummy data function for special report types.
Packit Service f2d567
Packit Service f2d567
        :returns: None
Packit Service f2d567
    """
Packit Service f2d567
    return None
Packit Service f2d567
Packit Service f2d567
Packit Service f2d567
# Implicit report fields and types
Packit Service f2d567
Packit Service f2d567
BR_SPECIAL = 0x80000000
Packit Service f2d567
_implicit_special_report_types = [
Packit Service f2d567
    BoomReportObjType(
Packit Service f2d567
        BR_SPECIAL, "Special", "special_", __none_returning_fn
Packit Service f2d567
    )
Packit Service f2d567
]
Packit Service f2d567
Packit Service f2d567
Packit Service f2d567
def __no_report_fn(f, d):
Packit Service f2d567
    """Dummy report function for special report types.
Packit Service f2d567
Packit Service f2d567
        :returns: None
Packit Service f2d567
    """
Packit Service f2d567
    return
Packit Service f2d567
Packit Service f2d567
Packit Service f2d567
_special_field_help_name = "help"
Packit Service f2d567
Packit Service f2d567
_implicit_special_report_fields = [
Packit Service f2d567
    BoomFieldType(
Packit Service f2d567
        BR_SPECIAL, _special_field_help_name, "Help", "Show help", 8,
Packit Service f2d567
        REP_STR, __no_report_fn)
Packit Service f2d567
]
Packit Service f2d567
Packit Service f2d567
Packit Service f2d567
# BoomReport class
Packit Service f2d567
Packit Service f2d567
class BoomReport(object):
Packit Service f2d567
    """BoomReport()
Packit Service f2d567
        A class representing a configurable text report with multiple
Packit Service f2d567
        caller-defined fields. An optional title may be provided and he
Packit Service f2d567
        ``fields`` argument must contain a list of ``BoomField`` objects
Packit Service f2d567
        describing the required report.
Packit Service f2d567
Packit Service f2d567
    """
Packit Service f2d567
    report_types = 0
Packit Service f2d567
Packit Service f2d567
    _fields = None
Packit Service f2d567
    _types = None
Packit Service f2d567
    _data = None
Packit Service f2d567
    _rows = None
Packit Service f2d567
    _keys_count = 0
Packit Service f2d567
    _field_properties = None
Packit Service f2d567
    _header_written = False
Packit Service f2d567
    _field_calc_needed = True
Packit Service f2d567
    _sort_required = False
Packit Service f2d567
    _already_reported = False
Packit Service f2d567
Packit Service f2d567
    # Implicit field support
Packit Service f2d567
    _implicit_types = _implicit_special_report_types
Packit Service f2d567
    _implicit_fields = _implicit_special_report_fields
Packit Service f2d567
Packit Service f2d567
    private = None
Packit Service f2d567
    opts = None
Packit Service f2d567
Packit Service f2d567
    def __help_requested(self):
Packit Service f2d567
        """Check for presence of 'help' fields in output selection.
Packit Service f2d567
Packit Service f2d567
            Check the fields making up this BoomReport and return True
Packit Service f2d567
            if any valid 'help' field synonym is present.
Packit Service f2d567
Packit Service f2d567
            :returns: True if help was requested or False otherwise
Packit Service f2d567
        """
Packit Service f2d567
        for fp in self._field_properties:
Packit Service f2d567
            if fp.implicit:
Packit Service f2d567
                name = self._implicit_fields[fp.field_num].name
Packit Service f2d567
                if name == _special_field_help_name:
Packit Service f2d567
                    return True
Packit Service f2d567
        return False
Packit Service f2d567
Packit Service f2d567
    def __get_longest_field_name_len(self, fields):
Packit Service f2d567
        """Find the longest field name length.
Packit Service f2d567
Packit Service f2d567
            :returns: the length of the longest configured field name
Packit Service f2d567
        """
Packit Service f2d567
        max_len = 0
Packit Service f2d567
        for f in fields:
Packit Service f2d567
            cur_len = len(f.name)
Packit Service f2d567
            max_len = cur_len if cur_len > max_len else max_len
Packit Service f2d567
        for t in self._types:
Packit Service f2d567
            cur_len = len(t.prefix) + 3
Packit Service f2d567
            max_len = cur_len if cur_len > max_len else max_len
Packit Service f2d567
        return max_len
Packit Service f2d567
Packit Service f2d567
    def __display_fields(self, display_field_types):
Packit Service f2d567
        """Display report fields help message.
Packit Service f2d567
Packit Service f2d567
            Display a list of valid fields for this ``BoomReport``.
Packit Service f2d567
Packit Service f2d567
            :param fields: The list of fields to display
Packit Service f2d567
            :param display_field_types: A boolean controling whether
Packit Service f2d567
                                        field types (str, SHA, num)
Packit Service f2d567
                                        are included in help output
Packit Service f2d567
        """
Packit Service f2d567
        fields = self._fields
Packit Service f2d567
        name_len = self.__get_longest_field_name_len(fields)
Packit Service f2d567
        last_desc = ""
Packit Service f2d567
        banner = "-" * 79
Packit Service f2d567
        for f in fields:
Packit Service f2d567
            t = self.__find_type(f.objtype)
Packit Service f2d567
            if t:
Packit Service f2d567
                desc = t.desc
Packit Service f2d567
            else:
Packit Service f2d567
                desc = ""
Packit Service f2d567
            if desc != last_desc:
Packit Service f2d567
                if len(last_desc):
Packit Service f2d567
                    print(" ")
Packit Service f2d567
                desc_len = len(desc) + 7
Packit Service f2d567
                print("%s Fields" % desc)
Packit Service f2d567
                print("%*.*s" % (desc_len, desc_len, banner))
Packit Service f2d567
            print("  %-*s - %s%s%s%s" %
Packit Service f2d567
                  (name_len, f.name, f.desc,
Packit Service f2d567
                   " [" if display_field_types else "",
Packit Service f2d567
                   f.dtype if display_field_types else "",
Packit Service f2d567
                   "]" if display_field_types else ""))
Packit Service f2d567
            last_desc = desc
Packit Service f2d567
Packit Service f2d567
    def __find_type(self, report_type):
Packit Service f2d567
        """Resolve numeric type to corresponding BoomReportObjType.
Packit Service f2d567
Packit Service f2d567
            :param report_type: The numeric report type to look up
Packit Service f2d567
            :returns: The requested BoomReportObjType.
Packit Service f2d567
            :raises: ValueError if no matching type was found.
Packit Service f2d567
        """
Packit Service f2d567
        for t in self._implicit_types:
Packit Service f2d567
            if t.objtype == report_type:
Packit Service f2d567
                return t
Packit Service f2d567
        for t in self._types:
Packit Service f2d567
            if t.objtype == report_type:
Packit Service f2d567
                return t
Packit Service f2d567
Packit Service f2d567
        raise ValueError("Unknown report object type: %d" % report_type)
Packit Service f2d567
Packit Service f2d567
    def __copy_field(self, field_num, implicit):
Packit Service f2d567
        """Copy field definition to BoomFieldProperties
Packit Service f2d567
Packit Service f2d567
            Copy values from a BoomFieldType to BoomFieldProperties.
Packit Service f2d567
Packit Service f2d567
            :param field_num: The number of this field (fields order)
Packit Service f2d567
            :param implicit: True if this field is implicit, else False
Packit Service f2d567
        """
Packit Service f2d567
        fp = BoomFieldProperties()
Packit Service f2d567
        fp.field_num = field_num
Packit Service f2d567
        fp.width = fp.initial_width = self._fields[field_num].width
Packit Service f2d567
        fp.implicit = implicit
Packit Service f2d567
        fp.objtype = self.__find_type(self._fields[field_num].objtype)
Packit Service f2d567
        fp.dtype = self._fields[field_num].dtype
Packit Service f2d567
        fp.align = self._fields[field_num].align
Packit Service f2d567
        return fp
Packit Service f2d567
Packit Service f2d567
    def __add_field(self, field_num, implicit):
Packit Service f2d567
        """Add a field to this BoomReport.
Packit Service f2d567
Packit Service f2d567
            Add the specified BoomFieldType to this BoomReport and
Packit Service f2d567
            configure BoomFieldProperties for it.
Packit Service f2d567
Packit Service f2d567
            :param field_num: The number of this field (fields order)
Packit Service f2d567
            :param implicit: True if this field is implicit, else False
Packit Service f2d567
        """
Packit Service f2d567
        fp = self.__copy_field(field_num, implicit)
Packit Service f2d567
        if fp.hidden:
Packit Service f2d567
            self._field_properties.insert(0, fp)
Packit Service f2d567
        else:
Packit Service f2d567
            self._field_properties.append(fp)
Packit Service f2d567
        return fp
Packit Service f2d567
Packit Service f2d567
    def __get_field(self, field_name):
Packit Service f2d567
        """Look up a field by name.
Packit Service f2d567
Packit Service f2d567
            Attempt to find the field named in ``field_name`` in this
Packit Service f2d567
            BoomReport's tables of implicit and user-defined fields,
Packit Service f2d567
            returning the a ``(field, implicit)`` tuple, where field
Packit Service f2d567
            contains the requested ``BoomFieldType``, and ``implicit``
Packit Service f2d567
            is a boolean indicating whether this field is implicit or
Packit Service f2d567
            not.
Packit Service f2d567
Packit Service f2d567
            :param field_num: The number of this field (fields order)
Packit Service f2d567
            :param implicit: True if this field is implicit, else False
Packit Service f2d567
        """
Packit Service f2d567
        # FIXME implicit fields
Packit Service f2d567
        for field in self._implicit_fields:
Packit Service f2d567
            if field.name == field_name:
Packit Service f2d567
                return (self._implicit_fields.index(field), True)
Packit Service f2d567
        for field in self._fields:
Packit Service f2d567
            if field.name == field_name:
Packit Service f2d567
                return (self._fields.index(field), False)
Packit Service f2d567
        raise ValueError("No matching field name: %s" % field_name)
Packit Service f2d567
Packit Service f2d567
    def __field_match(self, field_name, type_only):
Packit Service f2d567
        """Attempt to match a field and optionally update report type.
Packit Service f2d567
Packit Service f2d567
            Look up the named field and, if ``type_only`` is True,
Packit Service f2d567
            update this BoomReport's ``report_types`` mask to include
Packit Service f2d567
            the field's type identifier. If ``type_only`` is False the
Packit Service f2d567
            field is also added to this BoomReport's field list.
Packit Service f2d567
Packit Service f2d567
            :param field_name: A string identifying the field
Packit Service f2d567
            :param type_only: True if this call should only update types
Packit Service f2d567
        """
Packit Service f2d567
        try:
Packit Service f2d567
            (f, implicit) = self.__get_field(field_name)
Packit Service f2d567
            if (type_only):
Packit Service f2d567
                if implicit:
Packit Service f2d567
                    self.report_types |= self._implicit_fields[f].objtype
Packit Service f2d567
                else:
Packit Service f2d567
                    self.report_types |= self._fields[f].objtype
Packit Service f2d567
                return
Packit Service f2d567
            return self.__add_field(f, implicit)
Packit Service f2d567
        except ValueError as e:
Packit Service f2d567
            # FIXME handle '$PREFIX_all'
Packit Service f2d567
            # re-raise 'e' if it fails.
Packit Service f2d567
            raise e
Packit Service f2d567
Packit Service f2d567
    def __parse_fields(self, field_format, type_only):
Packit Service f2d567
        """Parse report field list.
Packit Service f2d567
Packit Service f2d567
            Parse ``field_format`` and attempt to match the names of
Packit Service f2d567
            field names found to registered BoomFieldType fields.
Packit Service f2d567
Packit Service f2d567
            If ``type_only`` is True only the ``report_types`` field
Packit Service f2d567
            is updated: otherwise the parsed fields are added to the
Packit Service f2d567
            BoomReport's field list.
Packit Service f2d567
Packit Service f2d567
            :param field_format: The list of fields to parse
Packit Service f2d567
            :param type_only: True if this call should only update types
Packit Service f2d567
        """
Packit Service f2d567
        for word in field_format.split(','):
Packit Service f2d567
            # Allow consecutive commas
Packit Service f2d567
            if not word:
Packit Service f2d567
                continue
Packit Service f2d567
            try:
Packit Service f2d567
                self.__field_match(word, type_only)
Packit Service f2d567
            except ValueError as e:
Packit Service f2d567
                self.__display_fields(True)
Packit Service f2d567
                print("Unrecognised field: %s" % word)
Packit Service f2d567
                raise e
Packit Service f2d567
Packit Service f2d567
    def __add_sort_key(self, field_num, sort, implicit, type_only):
Packit Service f2d567
        """Add a new sort key to this BoomReport
Packit Service f2d567
Packit Service f2d567
            Add the sort key identified by ``field_num`` to this list
Packit Service f2d567
            of sort keys for this BoomReport.
Packit Service f2d567
Packit Service f2d567
            :param field_num: The field number of the key to add
Packit Service f2d567
            :param sort: The sort direction for this key
Packit Service f2d567
            :param implicit: True if field_num is implicit, else False
Packit Service f2d567
            :param type_only: True if this call should only update types
Packit Service f2d567
        """
Packit Service f2d567
        fields = self._implicit_fields if implicit else self._fields
Packit Service f2d567
        found = None
Packit Service f2d567
Packit Service f2d567
        for fp in self._field_properties:
Packit Service f2d567
            if fp.implicit == implicit and fp.field_num == field_num:
Packit Service f2d567
                found = fp
Packit Service f2d567
Packit Service f2d567
        if not found:
Packit Service f2d567
            if type_only:
Packit Service f2d567
                self.report_types |= fields[field_num].objtype
Packit Service f2d567
                return
Packit Service f2d567
            else:
Packit Service f2d567
                found = self.__add_field(field_num, implicit)
Packit Service f2d567
Packit Service f2d567
        if found.sort_key:
Packit Service f2d567
            _log_info("Ignoring duplicate sort field: %s" %
Packit Service f2d567
                      fields[field_num].name)
Packit Service f2d567
        found.sort_key = True
Packit Service f2d567
        found.sort_dir = sort
Packit Service f2d567
        found.sort_posn = self._keys_count
Packit Service f2d567
        self._keys_count += 1
Packit Service f2d567
Packit Service f2d567
    def __key_match(self, key_name, type_only):
Packit Service f2d567
        """Attempt to match a sort key and update report type.
Packit Service f2d567
Packit Service f2d567
            Look up the named sort key and, if ``type_only`` is True,
Packit Service f2d567
            update this BoomReport's ``report_types`` mask to include
Packit Service f2d567
            the field's type identifier. If ``type_only`` is False the
Packit Service f2d567
            field is also added to this BoomReport's field list.
Packit Service f2d567
Packit Service f2d567
            :param field_name: A string identifying the sort key
Packit Service f2d567
            :param type_only: True if this call should only update types
Packit Service f2d567
        """
Packit Service f2d567
        sort_dir = None
Packit Service f2d567
Packit Service f2d567
        if not key_name:
Packit Service f2d567
            raise ValueError("Sort key name cannot be empty")
Packit Service f2d567
Packit Service f2d567
        if key_name.startswith('+'):
Packit Service f2d567
            sort_dir = ASCENDING
Packit Service f2d567
            key_name = key_name[1:]
Packit Service f2d567
        elif key_name.startswith('-'):
Packit Service f2d567
            sort_dir = DESCENDING
Packit Service f2d567
            key_name = key_name[1:]
Packit Service f2d567
        else:
Packit Service f2d567
            sort_dir = ASCENDING
Packit Service f2d567
Packit Service f2d567
        for field in self._implicit_fields:
Packit Service f2d567
            fields = self._implicit_fields
Packit Service f2d567
            if field.name == key_name:
Packit Service f2d567
                return self.__add_sort_key(fields.index(field), sort_dir,
Packit Service f2d567
                                           True, type_only)
Packit Service f2d567
        for field in self._fields:
Packit Service f2d567
            fields = self._fields
Packit Service f2d567
            if field.name == key_name:
Packit Service f2d567
                return self.__add_sort_key(fields.index(field), sort_dir,
Packit Service f2d567
                                           False, type_only)
Packit Service f2d567
Packit Service f2d567
        raise ValueError("Unknown sort key name: %s" % key_name)
Packit Service f2d567
Packit Service f2d567
    def __parse_keys(self, keys, type_only):
Packit Service f2d567
        """Parse report sort key list.
Packit Service f2d567
Packit Service f2d567
            Parse ``keys`` and attempt to match the names of
Packit Service f2d567
            sort keys found to registered BoomFieldType fields.
Packit Service f2d567
Packit Service f2d567
            If ``type_only`` is True only the ``report_types`` field
Packit Service f2d567
            is updated: otherwise the parsed fields are added to the
Packit Service f2d567
            BoomReport's sort key list.
Packit Service f2d567
Packit Service f2d567
            :param field_format: The list of fields to parse
Packit Service f2d567
            :param type_only: True if this call should only update types
Packit Service f2d567
        """
Packit Service f2d567
        if not keys:
Packit Service f2d567
            return
Packit Service f2d567
        for word in keys.split(','):
Packit Service f2d567
            # Allow consecutive commas
Packit Service f2d567
            if not word:
Packit Service f2d567
                continue
Packit Service f2d567
            try:
Packit Service f2d567
                self.__key_match(word, type_only)
Packit Service f2d567
            except ValueError as e:
Packit Service f2d567
                self.__display_fields(True)
Packit Service f2d567
                print("Unrecognised field: %s" % word)
Packit Service f2d567
                raise e
Packit Service f2d567
Packit Service f2d567
    def __init__(self, types, fields, output_fields, opts,
Packit Service f2d567
                 sort_keys, private):
Packit Service f2d567
        """Initialise BoomReport.
Packit Service f2d567
Packit Service f2d567
            Initialise a new ``BoomReport`` object with the specified fields
Packit Service f2d567
            and output control options.
Packit Service f2d567
Packit Service f2d567
            :param types: List of BoomReportObjType used in this report.
Packit Service f2d567
            :param fields: A list of ``BoomField`` field descriptions.
Packit Service f2d567
            :param output_fields: An optional list of output fields to
Packit Service f2d567
                                  be rendered by this report.
Packit Service f2d567
            :param opts: An instance of ``BoomReportOpts`` or None.
Packit Service f2d567
            :returns: A new report object.
Packit Service f2d567
            :rtype: ``BoomReport``.
Packit Service f2d567
        """
Packit Service f2d567
Packit Service f2d567
        self._fields = fields
Packit Service f2d567
        self._types = types
Packit Service f2d567
        self._private = private
Packit Service f2d567
Packit Service f2d567
        if opts.buffered:
Packit Service f2d567
            self._sort_required = True
Packit Service f2d567
Packit Service f2d567
        self.opts = opts if opts else BoomReportOpts()
Packit Service f2d567
Packit Service f2d567
        self._rows = []
Packit Service f2d567
        self._field_properties = []
Packit Service f2d567
Packit Service f2d567
        # set field_prefix from type
Packit Service f2d567
Packit Service f2d567
        # canonicalize_field_ids()
Packit Service f2d567
Packit Service f2d567
        if not output_fields:
Packit Service f2d567
            output_fields = ",".join([field.name for field in fields])
Packit Service f2d567
Packit Service f2d567
        # First pass: set up types
Packit Service f2d567
        self.__parse_fields(output_fields, 1)
Packit Service f2d567
        self.__parse_keys(sort_keys, 1)
Packit Service f2d567
Packit Service f2d567
        # Second pass: initialise fields
Packit Service f2d567
        self.__parse_fields(output_fields, 0)
Packit Service f2d567
        self.__parse_keys(sort_keys, 0)
Packit Service f2d567
Packit Service f2d567
        if self.__help_requested():
Packit Service f2d567
            self._already_reported = True
Packit Service f2d567
            self.__display_fields(display_field_types=True)
Packit Service f2d567
            print("")
Packit Service f2d567
Packit Service f2d567
    def __recalculate_sha_width(self):
Packit Service f2d567
        """Recalculate minimum SHA field widths.
Packit Service f2d567
Packit Service f2d567
            For each REP_SHA field present, recalculate the minimum
Packit Service f2d567
            field width required to ensure uniqueness of the displayed
Packit Service f2d567
            values.
Packit Service f2d567
Packit Service f2d567
            :rtype: None
Packit Service f2d567
        """
Packit Service f2d567
        shas = {}
Packit Service f2d567
        props_map = {}
Packit Service f2d567
        for row in self._rows:
Packit Service f2d567
            for field in row._fields:
Packit Service f2d567
                if self._fields[field._props.field_num].dtype == REP_SHA:
Packit Service f2d567
                    # Use field_num as index to apply check across rows
Packit Service f2d567
                    num = field._props.field_num
Packit Service f2d567
                    if num not in shas:
Packit Service f2d567
                        shas[num] = set()
Packit Service f2d567
                        props_map[num] = field._props
Packit Service f2d567
                    shas[num].add(field.report_string)
Packit Service f2d567
        for num in shas.keys():
Packit Service f2d567
            min_prefix = max(MIN_SHA_WIDTH, props_map[num].width)
Packit Service f2d567
            props_map[num].width = find_minimum_sha_prefix(shas[num],
Packit Service f2d567
                                                           min_prefix)
Packit Service f2d567
Packit Service f2d567
    def __recalculate_fields(self):
Packit Service f2d567
        """Recalculate field widths.
Packit Service f2d567
Packit Service f2d567
            For each field, recalculate the minimum field width by
Packit Service f2d567
            finding the longest ``report_string`` value for that field
Packit Service f2d567
            and updating the dynamic width stored in the corresponding
Packit Service f2d567
            ``BoomFieldProperties`` object.
Packit Service f2d567
Packit Service f2d567
            :rtype: None
Packit Service f2d567
        """
Packit Service f2d567
        for row in self._rows:
Packit Service f2d567
            for field in row._fields:
Packit Service f2d567
                if self._sort_required and field._props.sort_key:
Packit Service f2d567
                    row._sort_fields[field._props.sort_posn] = field
Packit Service f2d567
                if self._fields[field._props.field_num].dtype == REP_SHA:
Packit Service f2d567
                    continue
Packit Service f2d567
                field_len = len(field.report_string)
Packit Service f2d567
                if field_len > field._props.width:
Packit Service f2d567
                    field._props.width = field_len
Packit Service f2d567
Packit Service f2d567
    def __report_headings(self):
Packit Service f2d567
        """Output report headings.
Packit Service f2d567
Packit Service f2d567
            Output the column headings for this BoomReport.
Packit Service f2d567
Packit Service f2d567
            :rtype: None
Packit Service f2d567
        """
Packit Service f2d567
        self._header_written = True
Packit Service f2d567
        if not self.opts.headings:
Packit Service f2d567
            return
Packit Service f2d567
Packit Service f2d567
        line = ""
Packit Service f2d567
        props = self._field_properties
Packit Service f2d567
        for fp in props:
Packit Service f2d567
            if fp.hidden:
Packit Service f2d567
                continue
Packit Service f2d567
            fields = self._fields
Packit Service f2d567
            heading = fields[fp.field_num].head
Packit Service f2d567
            headertuple = (fp.width, fp.width, heading)
Packit Service f2d567
            if self.opts.aligned:
Packit Service f2d567
                heading = "%-*.*s" % headertuple
Packit Service f2d567
            line += heading
Packit Service f2d567
            if props.index(fp) != (len(props) - 1):
Packit Service f2d567
                line += self.opts.separator
Packit Service f2d567
        self.opts.report_file.write(line + "\n")
Packit Service f2d567
Packit Service f2d567
    def __row_key_fn(self):
Packit Service f2d567
        """Return a Python key function to compare report rows.
Packit Service f2d567
Packit Service f2d567
            The ``cmp`` argument of sorting functions has been removed
Packit Service f2d567
            in Python 3.x: to maintain similarity with the device-mapper
Packit Service f2d567
            report library we keep a traditional "cmp"-style function
Packit Service f2d567
            (that is structured identically to the version in the device
Packit Service f2d567
            mapper library), and dynamically wrap it in a ``__RowKey``
Packit Service f2d567
            object to conform to the Python sort key model.
Packit Service f2d567
Packit Service f2d567
            :returns: A __RowKey object wrapping _row_cmp()
Packit Service f2d567
            :rtype: __RowKey
Packit Service f2d567
        """
Packit Service f2d567
        def _row_cmp(row_a, row_b):
Packit Service f2d567
            """Compare two report rows for sorting.
Packit Service f2d567
Packit Service f2d567
                Compare the report rows ``row_a`` and ``row_b`` and
Packit Service f2d567
                return a "cmp"-style comparison value:
Packit Service f2d567
Packit Service f2d567
                    1 if row_a > row_b
Packit Service f2d567
                    0 if row_a == row_b
Packit Service f2d567
                   -1 if row_b < row_a
Packit Service f2d567
Packit Service f2d567
                Note that the actual comparison direction depends on the
Packit Service f2d567
                field definitions of the fields being compared, since
Packit Service f2d567
                each sort key defines its own sort order.
Packit Service f2d567
Packit Service f2d567
                :param row_a: The first row to compare
Packit Service f2d567
                :param row_b: The seconf row to compare
Packit Service f2d567
            """
Packit Service f2d567
            for cnt in range(0, row_a._report._keys_count):
Packit Service f2d567
                sfa = row_a._sort_fields[cnt]
Packit Service f2d567
                sfb = row_b._sort_fields[cnt]
Packit Service f2d567
                if sfa._props.dtype == REP_NUM:
Packit Service f2d567
                    num_a = sfa.sort_value
Packit Service f2d567
                    num_b = sfb.sort_value
Packit Service f2d567
                    if num_a == num_b:
Packit Service f2d567
                        continue
Packit Service f2d567
                    if sfa._props.sort_dir == ASCENDING:
Packit Service f2d567
                        return 1 if num_a > num_b else -1
Packit Service f2d567
                    else:
Packit Service f2d567
                        return 1 if num_a < num_b else -1
Packit Service f2d567
                else:
Packit Service f2d567
                    stra = sfa.sort_value
Packit Service f2d567
                    strb = sfb.sort_value
Packit Service f2d567
                    if stra == strb:
Packit Service f2d567
                        continue
Packit Service f2d567
                    if sfa._props.sort_dir == ASCENDING:
Packit Service f2d567
                        return 1 if stra > strb else -1
Packit Service f2d567
                    else:
Packit Service f2d567
                        return 1 if stra < strb else -1
Packit Service f2d567
            return 0
Packit Service f2d567
Packit Service f2d567
        class __RowKey(object):
Packit Service f2d567
            """__RowKey sort wrapper.
Packit Service f2d567
            """
Packit Service f2d567
            def __init__(self, obj, *args):
Packit Service f2d567
                """Initialise a new __RowKey object.
Packit Service f2d567
Packit Service f2d567
                    :param obj: The object to be compared
Packit Service f2d567
                    :returns: None
Packit Service f2d567
                """
Packit Service f2d567
                self.obj = obj
Packit Service f2d567
Packit Service f2d567
            def __lt__(self, other):
Packit Service f2d567
                """Test if less than.
Packit Service f2d567
Packit Service f2d567
                    :param other: The other object to be compared
Packit Service f2d567
                """
Packit Service f2d567
                return _row_cmp(self.obj, other.obj) < 0
Packit Service f2d567
Packit Service f2d567
            def __gt__(self, other):
Packit Service f2d567
                """Test if greater than.
Packit Service f2d567
Packit Service f2d567
                    :param other: The other object to be compared
Packit Service f2d567
                """
Packit Service f2d567
                return _row_cmp(self.obj, other.obj) > 0
Packit Service f2d567
Packit Service f2d567
            def __eq__(self, other):
Packit Service f2d567
                """Test if equal to.
Packit Service f2d567
Packit Service f2d567
                    :param other: The other object to be compared
Packit Service f2d567
                """
Packit Service f2d567
                return _row_cmp(self.obj, other.obj) == 0
Packit Service f2d567
Packit Service f2d567
            def __le__(self, other):
Packit Service f2d567
                """Test if less than or equal to.
Packit Service f2d567
Packit Service f2d567
                    :param other: The other object to be compared
Packit Service f2d567
                """
Packit Service f2d567
                return _row_cmp(self.obj, other.obj) <= 0
Packit Service f2d567
Packit Service f2d567
            def __ge__(self, other):
Packit Service f2d567
                """Test if greater than or equal to.
Packit Service f2d567
Packit Service f2d567
                    :param other: The other object to be compared
Packit Service f2d567
                """
Packit Service f2d567
                return _row_cmp(self.obj, other.obj) >= 0
Packit Service f2d567
Packit Service f2d567
            def __ne__(self, other):
Packit Service f2d567
                """Test if not equal to.
Packit Service f2d567
Packit Service f2d567
                    :param other: The other object to be compared
Packit Service f2d567
                """
Packit Service f2d567
                return _row_cmp(self.obj, other.obj) != 0
Packit Service f2d567
Packit Service f2d567
        return __RowKey
Packit Service f2d567
Packit Service f2d567
    def _sort_rows(self):
Packit Service f2d567
        """Sort the rows of this BoomReport.
Packit Service f2d567
Packit Service f2d567
            Sort this report's rows, according to the configured sort
Packit Service f2d567
            keys.
Packit Service f2d567
Packit Service f2d567
            :returns: None
Packit Service f2d567
        """
Packit Service f2d567
        self._rows.sort(key=self.__row_key_fn())
Packit Service f2d567
Packit Service f2d567
    def report_object(self, obj):
Packit Service f2d567
        """Report data for object.
Packit Service f2d567
Packit Service f2d567
            Add a row of data to this ``BoomReport``. The ``data``
Packit Service f2d567
            argument should be an object of the type understood by this
Packit Service f2d567
            report's fields. It will be passed in turn to each field to
Packit Service f2d567
            obtain data for the current row.
Packit Service f2d567
Packit Service f2d567
            :param obj: the object to report on for this row.
Packit Service f2d567
        """
Packit Service f2d567
        if obj is None:
Packit Service f2d567
            raise ValueError("Cannot report NoneType object.")
Packit Service f2d567
Packit Service f2d567
        if self._already_reported:
Packit Service f2d567
            return
Packit Service f2d567
Packit Service f2d567
        row = BoomRow(self)
Packit Service f2d567
        fields = self._fields
Packit Service f2d567
        if self._sort_required:
Packit Service f2d567
            row._sort_fields = [-1] * self._keys_count
Packit Service f2d567
        for fp in self._field_properties:
Packit Service f2d567
            field = BoomField(self, fp)
Packit Service f2d567
            data = fp.objtype.data_fn(obj)
Packit Service f2d567
Packit Service f2d567
            if data is None:
Packit Service f2d567
                raise ValueError("No data assigned to field %s" %
Packit Service f2d567
                                 fields[fp.field_num].name)
Packit Service f2d567
Packit Service f2d567
            try:
Packit Service f2d567
                fields[fp.field_num].report_fn(field, data)
Packit Service f2d567
            except ValueError:
Packit Service f2d567
                raise ValueError("No value assigned to field %s" %
Packit Service f2d567
                                 fields[fp.field_num].name)
Packit Service f2d567
            row.add_field(field)
Packit Service f2d567
        self._rows.append(row)
Packit Service f2d567
Packit Service f2d567
        if not self.opts.buffered:
Packit Service f2d567
            return self.report_output()
Packit Service f2d567
Packit Service f2d567
    def _output_field(self, field):
Packit Service f2d567
        """Output field data.
Packit Service f2d567
Packit Service f2d567
            Generate string data for one field in a report row.
Packit Service f2d567
Packit Service f2d567
            :field: The field to be output
Packit Service f2d567
            :returns: The output report string for this field
Packit Service f2d567
            :rtype: str
Packit Service f2d567
        """
Packit Service f2d567
        fields = self._fields
Packit Service f2d567
        prefix = self.opts.field_name_prefix
Packit Service f2d567
        quote = "" if self.opts.unquoted else STANDARD_QUOTE
Packit Service f2d567
Packit Service f2d567
        if prefix:
Packit Service f2d567
            field_name = fields[field._props.field_num].name
Packit Service f2d567
            prefix += "%s%s%s" % (field_name.upper(), STANDARD_PAIR,
Packit Service f2d567
                                  STANDARD_QUOTE)
Packit Service f2d567
Packit Service f2d567
        repstr = field.report_string
Packit Service f2d567
        width = field._props.width
Packit Service f2d567
        if self.opts.aligned:
Packit Service f2d567
            align = field._props.align
Packit Service f2d567
            if not align:
Packit Service f2d567
                if field._props.dtype == REP_NUM:
Packit Service f2d567
                    align = ALIGN_RIGHT
Packit Service f2d567
                else:
Packit Service f2d567
                    align = ALIGN_LEFT
Packit Service f2d567
            reptuple = (width, width, repstr)
Packit Service f2d567
            if align == ALIGN_LEFT:
Packit Service f2d567
                repstr = "%-*.*s" % reptuple
Packit Service f2d567
            else:
Packit Service f2d567
                repstr = "%*.*s" % reptuple
Packit Service f2d567
Packit Service f2d567
        suffix = quote
Packit Service f2d567
        return prefix + repstr + suffix
Packit Service f2d567
Packit Service f2d567
    def _output_as_rows(self):
Packit Service f2d567
        """Output this report in column format.
Packit Service f2d567
Packit Service f2d567
            Output the data contained in this ``BoomReport`` in column
Packit Service f2d567
            format, one row per line. If column headings have not been
Packit Service f2d567
            printed already they will be automatically displayed by this
Packit Service f2d567
            call.
Packit Service f2d567
Packit Service f2d567
            :returns: None
Packit Service f2d567
        """
Packit Service f2d567
        for fp in self._field_properties:
Packit Service f2d567
            if fp.hidden:
Packit Service f2d567
                for row in self._rows:
Packit Service f2d567
                    row._fields = row._fields[1:]
Packit Service f2d567
Packit Service f2d567
            fields = self._implicit_fields if fp.implicit else self._fields
Packit Service f2d567
            line = ""
Packit Service f2d567
Packit Service f2d567
            if self.opts.headings:
Packit Service f2d567
                line += fields[fp.field_num].head + self.opts.separator
Packit Service f2d567
Packit Service f2d567
            for row in self._rows:
Packit Service f2d567
                field = row._fields[0]
Packit Service f2d567
                line += self._output_field(field)
Packit Service f2d567
                line += self.opts.separator
Packit Service f2d567
                row._fields = row._fields[1:]
Packit Service f2d567
Packit Service f2d567
            self.opts.report_file.write(line + "\n")
Packit Service f2d567
Packit Service f2d567
    def _output_as_columns(self):
Packit Service f2d567
        """Output this report in column format.
Packit Service f2d567
Packit Service f2d567
            Output the data contained in this ``BoomReport`` in column
Packit Service f2d567
            format, one row per line. If column headings have not been
Packit Service f2d567
            printed already they will be automatically displayed by this
Packit Service f2d567
            call.
Packit Service f2d567
Packit Service f2d567
            :returns: None
Packit Service f2d567
        """
Packit Service f2d567
        if not self._header_written:
Packit Service f2d567
            self.__report_headings()
Packit Service f2d567
        for row in self._rows:
Packit Service f2d567
            do_field_delim = False
Packit Service f2d567
            line = ""
Packit Service f2d567
            for field in row._fields:
Packit Service f2d567
                if field._props.hidden:
Packit Service f2d567
                    continue
Packit Service f2d567
                if do_field_delim:
Packit Service f2d567
                    line += self.opts.separator
Packit Service f2d567
                else:
Packit Service f2d567
                    do_field_delim = True
Packit Service f2d567
                line += self._output_field(field)
Packit Service f2d567
            self.opts.report_file.write(line + "\n")
Packit Service f2d567
Packit Service f2d567
    def report_output(self):
Packit Service f2d567
        """Output report data.
Packit Service f2d567
Packit Service f2d567
            Output this report's data to the configured report file,
Packit Service f2d567
            using the configured output controls and fields.
Packit Service f2d567
Packit Service f2d567
            On success the number of rows output is returned. On
Packit Service f2d567
            error an exception is raised.
Packit Service f2d567
Packit Service f2d567
            :returns: the number of rows of output written.
Packit Service f2d567
            :rtype: ``int``
Packit Service f2d567
        """
Packit Service f2d567
        if self._already_reported:
Packit Service f2d567
            return
Packit Service f2d567
        if self._field_calc_needed:
Packit Service f2d567
            self.__recalculate_sha_width()
Packit Service f2d567
            self.__recalculate_fields()
Packit Service f2d567
        if self._sort_required:
Packit Service f2d567
            self._sort_rows()
Packit Service f2d567
        if self.opts.columns_as_rows:
Packit Service f2d567
            return self._output_as_rows()
Packit Service f2d567
        else:
Packit Service f2d567
            return self._output_as_columns()
Packit Service f2d567
Packit Service f2d567
Packit Service f2d567
__all__ = [
Packit Service f2d567
    # Module constants
Packit Service f2d567
Packit Service f2d567
    'REP_NUM', 'REP_STR', 'REP_SHA',
Packit Service f2d567
    'ALIGN_LEFT', 'ALIGN_RIGHT',
Packit Service f2d567
    'ASCENDING', 'DESCENDING',
Packit Service f2d567
Packit Service f2d567
    # Report objects
Packit Service f2d567
    'BoomReportOpts', 'BoomReportObjType', 'BoomField', 'BoomFieldType',
Packit Service f2d567
    'BoomFieldProperties', 'BoomReport'
Packit Service f2d567
]
Packit Service f2d567
Packit Service f2d567
# vim: set et ts=4 sw=4 :