Current File : //usr/lib/python2.7/site-packages/vdo/statistics/StatFormatter.py |
#
# Copyright (c) 2018 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
"""
DisplaySpec
$Id: //eng/vdo-releases/magnesium/src/python/vdo/statistics/StatFormatter.py#1 $
"""
import sys
class StatFormatter(object):
"""
An object which formats labeled values. Formatters are able to
represent all of the verbose formatting for vdoStats and vdoMonitor
(the df-style formatting in vdoStats is done by hand).
A formatter is specified by supplying a description of the format for each
level of the hierarchy of labeled values. If the values have more levels than
the formatter has specified, the final level specification will be applied
to all remaining value levels.
A level specification is specified as a hash with any of the following keys:
displayFilter: A function which takes a LabeledValue and return True if
that value (and its children) should be displayed. Defaults
to a tautology.
indent: The additional indentation to apply from this level down.
Defaults to no additional indentation.
joiner: The string to use for joining label-value pairs at the
current level. In YAML mode, defaults to a newline,
otherwise defaults to a space.
namer: The way of naming the current level. Can be any of these:
None: Don't name this level
list: A list of indexes into the value list at this
level, the name of the level will be the
concatenation of the values at those indexes.
If the first element of the list is a string, it
will be used as a prefix for the name.
'=': Use the label of the value at this level as the
name, use an equals sign to connect the name to the
values
'+': Use the label of the value at this level as the
name, regardless of the formatter's mode
string: Any other string will be used as the name
True: Any other true value will use the label as the name
if the mode indicates that the level should be named
and otherwise won't name the level
(non-hierarchical, multivalued levels don't get
named).
"""
def __init__(self, displayLevels, hierarchical=True, yaml=True):
"""
Create a new formatter.
:param displayLevels: An array of hashes describing how to format each
level of the labled values to be formatted.
:param hierarchical: If True, indentation will be increased at each level
otherwise, indentation will only be modified at
levels which specify the 'indent' parameter. Defaults
to True.
:param yaml: Whether to output as YAML or not. Defaults to True.
"""
self.hierarchical = hierarchical
self.yaml = yaml
self.spec = None
# build the DisplaySpecs from the bottom up to set child pointers.
displayLevels.reverse()
for level in displayLevels:
self.spec = DisplaySpec(self, self.spec, level)
# walk back down setting the indentation
s = self.spec
indent = ''
while (True):
indent = s.setIndentation(indent)
if s.child is None:
# Set the child of the bottom level to be itself
s.child = s
break
s = s.child
def format(self, lv):
"""
Format labeled values.
:param lv: The values to format
:return: The formatted value string
"""
return self.spec.format(lv)
def output(self, lv):
"""
Format labeled values and print the result.
:param lv: The values to format
"""
try:
print self.format(lv)
sys.stdout.flush()
except IOError:
# Someone must have closed stdout. Don't die.
pass
class DisplaySpec(object):
"""
An object which formats a single level of labeled values.
"""
def __init__(self, formatter, child, parameters):
"""
Create a new display specification.
:param formatter : The formatter which owns this specification.
:param child : The formatter for the next level down.
:param parameters : A dict which defines the format at this level. Valid
keys are documented in the header of this file.
"""
self.formatter = formatter
self.parent = None
self.child = child
self.displayFilter = parameters.get('displayFilter', lambda(lv): True)
self.indent = parameters.get('indent', '')
self.joiner = parameters.get('joiner',
"\n" if formatter.yaml else ' ')
self.nameJoiner = parameters.get('nameJoiner',
': ' if formatter.yaml else ' ')
self.namer = self._setNamer(parameters.get('namer'))
self.width = None
self.subWidth = None
if child:
child.parent = self
def _setNamer(self, nameType):
"""
Set up the namer for this level from the specification.
:param nameType: The type of namer to use
:return: The specified namer function
"""
if not nameType:
return lambda lv: None
if isinstance(nameType, list):
if isinstance(nameType[0], str):
name = [nameType[0], nameType[1:]]
else:
name = ['', nameType]
return lambda lv: (name[0]
+ ' '.join(str(lv.value[n].value) for n in name[1]))
if (isinstance(nameType, str)):
if (nameType == '='):
self.nameJoiner = '='
return lambda lv: None if lv.isMultiValued() else lv.label
if (nameType == '+'):
return lambda lv: lv.label
return lambda lv: nameType
if self.formatter.hierarchical:
return lambda lv: lv.label
return lambda lv: None if lv.isMultiValued() else lv.label
def getName(self, lv):
"""
Get the name for a labeled value.
:param lv: The value to name
:return: The name for the value or None if the value should not be named
"""
name = self.namer(lv)
if not name:
return None
if self.formatter.yaml:
nameWidth = max(self.width, len(name)) + 1
return "{0}{1:<{2}}{3}{4}".format(self.indent, name, nameWidth,
self.nameJoiner,
"\n" if lv.isMultiValued() else '')
return self.indent + name + self.nameJoiner
def setIndentation(self, parentIndent):
"""
Set the indentation for this level.
:param parentIndent: The indentation of the level above us
"""
self.indent = parentIndent + self.indent
return self.indent
def setWidth(self, lv):
"""
Set the width of the labels at this level.
:param lv: The value being formatted
"""
if self.width:
return
if self.formatter.hierarchical:
self.width = self.parent.subWidth if self.parent else lv.width()
self.subWidth = lv.subWidth(True)
return
if self.parent:
self.width = self.subWidth = self.parent.subWidth
return
self.width = lv.width()
self.subWidth = lv.subWidth(False)
def format(self, lv):
"""
Recursively format a labeled value.
:param lv: The value to format
:return: The formatted value string
"""
if not self.displayFilter(lv):
return None
self.setWidth(lv)
name = self.getName(lv)
value = lv.format(self.child, self.joiner)
return name + value if name else value