Source code for lmi.shell.LMIFormatter
# Copyright (C) 2012-2014 Peter Hatina <phatina@redhat.com>
#
# 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, see <http://www.gnu.org/licenses/>.
import os
import abc
import sys
import tempfile
import subprocess
from textwrap import TextWrapper
from lmi.shell.LMIUtil import lmi_raise_or_dump_exception
from lmi.shell.LMIExceptions import LMINoPagerError
def _is_executable(fpath):
"""
:param string fpath: path of executable
:returns: True, if provided path is executable, False otherwise
"""
return os.path.exists(fpath) and os.access(fpath, os.X_OK)
def _which(program):
"""
:param string program: executable name
:returns: full path of a selected executable
:rtype: string
"""
fpath, fname = os.path.split(program)
if fpath:
if _is_executable(program):
return program
else:
for path in os.environ["PATH"].split(os.pathsep):
exe_file = os.path.join(path, program)
if _is_executable(exe_file):
return exe_file
return None
def _get_pager_with_params():
"""
:returns: list containing a executable with its CLI arguments
"""
if "PAGER" in os.environ:
path = _which(os.environ["PAGER"])
if path:
return [path]
for p in ["less", "more"]:
path = _which(p)
if not path:
continue
elif p == "less":
return [path, "-S"]
return [path]
lmi_raise_or_dump_exception(LMINoPagerError("No default pager found"))
return []
[docs]class LMITextFormatter(object):
"""
Text formatter class. Used when printing a block of text to output stream.
:param string text: text to be formatted
"""
SUB_INDENT = 4
def __init__(self, text):
self._text = text
[docs] def format(self, indent=0, width=80, f=sys.stdout,
separator=True):
"""
Formats a block of text and prints it to the output stream.
:param int indent: number of spaces to indent the text block
:param int width: total text block width
:param f: output stream
:param dictionary kwargs: supported keyword arguments
:param bool separator: if True, there will be a new line appended after
the formatted text; default value is True
"""
if indent > width:
return # NOTE: this is wrong!
wrapper = TextWrapper()
wrapper.width = width - indent
wrapper.subsequent_indent = " " * LMITextFormatter.SUB_INDENT
for l in wrapper.wrap(self._text):
f.write("%s%s\n" % (" " * indent, l))
if separator:
f.write("\n")
[docs]class LMIFormatter(object):
"""
Abstract class for derived subclasses.
"""
__metaclass__ = abc.ABCMeta
def __init__(self):
stty_dim = os.popen("stty size 2> /dev/null", "r").read().split()
self._width = int(stty_dim[1]) if stty_dim else 80
@abc.abstractmethod
[docs] def format(self, indent=0, width=80, f=sys.stdout):
"""
Formats a block of text and prints it to the output stream.
:param int indent: number of spaces to indent the text block
:param int width: total text block width
:param f: output stream
"""
pass
[docs] def fancy_format(self, interactive):
"""
Formats a block of text. If the LMIShell is running in interactive
mode, pager will be used, otherwise the output will be written to
standard output.
:param bool interactive: defines, if to use pager
"""
if interactive:
tmpfile = tempfile.mkstemp()
f = os.fdopen(tmpfile[0], "w")
self.format(0, self._width, f)
f.close()
subprocess_params = _get_pager_with_params()
if not subprocess_params:
return
subprocess_params.append(tmpfile[1])
subprocess.call(subprocess_params)
os.remove(tmpfile[1])
else:
self.format(0, self._width)
[docs]class LMIMethodFormatter(LMIFormatter):
"""
Method formatter is used to print out :py:class:`wbem.CIMMethod`
representation.
"""
def __init__(self, cim_method):
super(LMIMethodFormatter, self).__init__()
self._cim_method = cim_method
[docs] def format_qualifier(self, qualif, indent, width, f):
"""
Prints out a parameter of :py:class:`wbem.CIMMethod`.
:param int indent: number of spaces to indent the text block
:param int width: total text block width
:param f: output stream
"""
val = "[qualifier] %s %s" % (qualif.type, qualif.name)
if qualif.value:
if isinstance(qualif.value, list):
val += ": { " + ", ".join(
["'" + str(v) + "'" for v in qualif.value]) + " }"
else:
val += ": '%s'" % str(qualif.value)
LMITextFormatter(val).format(indent, width, f)
[docs] def format_parameter(self, param, indent, width, f):
"""
Prints out a parameter of :py:class:`wbem.CIMMethod`.
:param int indent: number of spaces to indent the text block
:param int width: total text block width
:param f: output stream
"""
ref_classname = ""
if param.type == "reference":
ref_classname = " (%s)" % param.reference_class
formatter = LMITextFormatter("[param] %s%s %s" % (
param.type, ref_classname, param.name))
formatter.format(indent, width, f)
[docs] def format_method(self, method, indent, width, f):
"""
Prints out a method of :py:class:`wbem.CIMClass`.
:param int indent: number of spaces to indent the text block
:param int width: total text block width
:param f: output stream
"""
has_args = "..." if method.parameters else ""
formatter = LMITextFormatter(
"[method] %s %s(%s)" % (method.return_type, method.name, has_args))
formatter.format(indent, width, f)
for q in method.qualifiers.itervalues():
self.format_qualifier(q, indent+4, width, f)
for p in method.parameters.itervalues():
self.format_parameter(p, indent+4, width, f)
[docs] def format(self, indent=0, width=80, f=sys.stdout):
"""
Prints out :py:class`CIMMethod` object.
:param int indent: number of spaces to indent the text block
:param int width: total text block width
:param f: output stream
"""
self.format_method(self._cim_method, indent, width, f)
[docs]class LMIClassFormatter(LMIMethodFormatter):
"""
Class formatter is used to print out :py:class:`wbem.CIMClass`
representation.
:param CIMClass cim_class: object to print out
"""
def __init__(self, cim_class):
# Call LMIFormatter ctor directly and use inly LMIMethodFormatter
# methods for LMIClass documentation formatting.
LMIFormatter.__init__(self)
self._cim_class = cim_class
[docs] def format_property(self, prop, indent, width, f):
"""
Prints out a property of :py:class:`wbem.CIMClass`.
:param int indent: number of spaces to indent the text block
:param int width: total text block width
:param f: output stream
"""
is_array_str = " array" if prop.is_array else ""
val = "[property%s] %s %s" % (is_array_str, prop.type, prop.name)
LMITextFormatter(val).format(indent, width, f)
for q in prop.qualifiers.itervalues():
self.format_qualifier(q, indent+4, width, f)
[docs] def format(self, indent=0, width=80, f=sys.stdout):
"""
Formats out :py:class:`wbem.CIMClass` object.
:param int indent: number of spaces to indent the text block
:param int width: total text block width
:param f: output stream
"""
formatter = LMITextFormatter("Class: %s (%s)" % (
self._cim_class.classname,
self._cim_class.superclass))
formatter.format(indent, width, f)
for q in self._cim_class.qualifiers.itervalues():
self.format_qualifier(q, indent+4, width, f)
for p in self._cim_class.properties.itervalues():
self.format_property(p, indent+4, width, f)
for m in self._cim_class.methods.itervalues():
self.format_method(m, indent+4, width, f)
[docs]class LMIInstanceFormatter(LMIFormatter):
"""
Instance formatter is used to print out :py:class:`wbem.CIMInstance`
representation.
:param CIMInstance cim_instance: object to print out
"""
def __init__(self, cim_instance):
super(LMIInstanceFormatter, self).__init__()
self._cim_instance = cim_instance
[docs] def format_property(self, prop, indent, width, f):
"""
Prints out a property of :py:class:`wbem.CIMInstance`.
:param int indent: number of spaces to indent the text block
:param int width: total text block width
:param f: output stream
"""
is_array_str = " array" if prop.is_array else ""
val = "[property%s] %s %s" % (is_array_str, prop.type, prop.name)
if prop.value:
if isinstance(prop.value, list):
val += " = { " + ", ".join(
["'" + str(v) + "'" for v in prop.value]) + " }"
else:
val += " = '%s'" % str(prop.value)
LMITextFormatter(val).format(indent, width, f)
for q in prop.qualifiers.itervalues():
self.format_qualifier(q, indent+4, width, f)
[docs] def format(self, indent=0, width=80, f=sys.stdout):
"""
Prints out :py:class`CIMInstance` object.
:param int indent: number of spaces to indent the text block
:param int width: total text block width
:param f: output stream
"""
formatter = LMITextFormatter(
"Instance of %s" % self._cim_instance.classname)
formatter.format(indent, width, f)
for p in self._cim_instance.properties.itervalues():
self.format_property(p, indent+4, width, f)
[docs]class LMIMofFormatter(LMIFormatter):
"""
MOF formatter is used to print out MOF representation of a CIM object.
:param obj: object, which has :py:meth:`tomof` method
"""
def __init__(self, obj):
super(LMIMofFormatter, self).__init__()
if not hasattr(obj, "tomof") or not callable(obj.tomof):
raise TypeError("object needs provide tomof() method")
self._obj = obj
[docs] def format(self, indent=0, width=80, f=sys.stdout):
"""
Formats a MOF object and prints it to the output stream.
:param int indent: number of spaces to indent the text block
:param int width: total text block width
:param f: output stream
"""
f.write(self._obj.tomof())