#
# (C) Copyright 2003-2007 Hewlett-Packard Development Company, L.P.
# (C) Copyright 2006-2007 Novell, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# Author: Tim Potter <tpot@hp.com>
# Author: Martin Pool <mbp@hp.com>
# Author: Bart Whiteley <bwhiteley@suse.de>
# Author: Ross Peoples <ross.peoples@gmail.com>
#
# pylint: disable=line-too-long
"""
CIM objects are local representations of CIM instances, classes, properties,
etc., as Python objects. They are used as input to and output from WBEM
operations:
========================================== ==========================================================================
CIM object Purpose
========================================== ==========================================================================
:class:`~pywbem.CIMInstanceName` Instance path of a CIM instance
:class:`~pywbem.CIMInstance` CIM instance
:class:`~pywbem.CIMClassName` Name of a CIM class, optionally with class path
:class:`~pywbem.CIMClass` CIM class
:class:`~pywbem.CIMProperty` CIM property, both as property value in a CIM instance and as property
declaration in a CIM class
:class:`~pywbem.CIMMethod` CIM method declaration in a CIM class
:class:`~pywbem.CIMParameter` CIM parameter in a CIM method declaration in a CIM class
:class:`~pywbem.CIMQualifier` CIM qualifier value
:class:`~pywbem.CIMQualifierDeclaration` CIM qualifier type/declaration
========================================== ==========================================================================
.. _`NocaseDict`:
NocaseDict
----------
:class:`~pywbem.NocaseDict` is a dictionary implementation with
case-insensitive but case-preserving keys. It is used for sets of named
CIM elements (e.g. CIM properties in an instance or class, or CIM parameters
in a method).
Except for the case-insensitivity of its keys, it behaves like the built-in
:class:`py:dict`. Therefore, :class:`~pywbem.NocaseDict` is not described
in detail in this documentation.
Deprecated: In v0.9.0, support for comparing two NocaseDict instances with the
``>``, ``>``, ``<=``, ``>=`` operators has been deprecated.
"""
# pylint: enable=line-too-long
# Note: When used before module docstrings, Pylint scopes the disable statement
# to the whole rest of the file, so we need an enable statement.
# This module is meant to be safe for 'import *'.
from __future__ import print_function, absolute_import
from datetime import datetime, timedelta
import warnings
import six
if six.PY2:
# pylint: disable=wrong-import-order
from __builtin__ import type as builtin_type
else:
# pylint: disable=wrong-import-order
from builtins import type as builtin_type # pylint: disable=import-error
from . import cim_xml
from .cim_types import _CIMComparisonMixin, type_from_name, \
cimtype, atomic_to_cim_xml, CIMType, \
CIMDateTime, Uint8, Sint8, Uint16, Sint16, Uint32, \
Sint32, Uint64, Sint64, Real32, Real64
__all__ = ['CIMClassName', 'CIMProperty', 'CIMInstanceName', 'CIMInstance',
'CIMClass', 'CIMMethod', 'CIMParameter', 'CIMQualifier',
'CIMQualifierDeclaration', 'tocimxml', 'tocimxmlstr', 'tocimobj']
# Constants for MOF formatting output
MOF_INDENT = 4
MAX_MOF_LINE = 79 # use 79 because comma separator sometimes runs over
# pylint: disable=too-many-lines
class NocaseDict(object):
"""
Yet another implementation of a case-insensitive dictionary.
Whenever keys are looked up, that is done case-insensitively. Whenever
keys are returned, they are returned with the lexical case that was
originally specified.
In addition to the methods listed, the dictionary supports:
* Retrieval of values based on key: `val = d[key]`
* Assigning values for a key: `d[key] = val`
* Deleting a key/value pair: `del d[key]`
* Equality comparison (`==`, `!=`)
* Ordering comparison (`<`, `<=`, `>=`, `>`)
* Containment test: `key in d`
* For loops: `for key in d`
* Determining length: `len(d)`
"""
def __init__(self, *args, **kwargs):
"""
Initialize the new dictionary from at most one positional argument and
optionally from additional keyword arguments.
Initialization happens in two steps, first from the positional
argument:
* If no positional argument is provided, or if one argument with the
value None is provided, the new dictionary will be left empty in
this step.
* If one positional argument of tuple or list type is provided, the
items in that iterable must be tuples of key and value,
respectively. The key/value pairs will be put into the new
dictionary (without copying them).
* If one positional argument of dictionary (mapping) or `NocaseDict`_
type is provided, its key/value pairs are put into the new
dictionary (without copying them).
* Otherwise, `TypeError` is raised.
After that, any provided keyword arguments are put into the so
initialized dictionary as key/value pairs (without copying them).
"""
self._data = {}
# Step 1: Initialize from at most one positional argument
if len(args) == 1:
if isinstance(args[0], (list, tuple)):
# Initialize from iterable of tuple(key,value)
for item in args[0]:
self[item[0]] = item[1]
elif isinstance(args[0], dict):
# Initialize from dict/mapping object
self.update(args[0])
elif isinstance(args[0], NocaseDict):
# Initialize from another NocaseDict object
self._data = args[0]._data.copy() # pylint: disable=protected-access
elif args[0] is None:
# Leave empty
pass
else:
raise TypeError(
"Invalid type for NocaseDict initialization: %s (%s)" % \
(args[0].__class__.__name__, type(args[0])))
elif len(args) > 1:
raise TypeError(
"Too many positional arguments for NocaseDict initialization: "\
"%s (1 allowed)" % len(args))
# Step 2: Add any keyword arguments
self.update(kwargs)
# Basic accessor and settor methods
def __getitem__(self, key):
"""
Invoked when retrieving the value for a key, using `val = d[key]`.
The key is looked up case-insensitively. Raises `KeyError` if the
specified key does not exist. Note that __setitem__() ensures that
only string typed keys will exist, so the key type is not tested here
and specifying non-string typed keys will simply lead to a KeyError.
"""
k = key
if isinstance(key, six.string_types):
k = k.lower()
try:
return self._data[k][1]
except KeyError:
raise KeyError('Key %r not found' % key)
def __setitem__(self, key, value):
"""
Invoked when assigning a value for a key using `d[key] = val`.
The key is looked up case-insensitively. If the key does not exist,
it is added with the new value. Otherwise, its value is overwritten
with the new value.
Raises `TypeError` if the specified key does not have string type.
"""
if not isinstance(key, six.string_types):
raise TypeError('NocaseDict key %s must be string type, ' \
'but is %s' % (key, builtin_type(key)))
k = key.lower()
self._data[k] = (key, value)
def __delitem__(self, key):
"""
Invoked when deleting a key/value pair using `del d[key]`.
The key is looked up case-insensitively. Raises `KeyError` if the
specified key does not exist. Note that __setitem__() ensures that
only string typed keys will exist, so the key type is not tested here
and specifying non-string typed keys will simply lead to a KeyError.
"""
k = key
if isinstance(key, six.string_types):
k = k.lower()
try:
del self._data[k]
except KeyError:
raise KeyError('Key %r not found' % key)
def __len__(self):
"""
Invoked when determining the number of key/value pairs in the
dictionary using `len(d)`.
"""
return len(self._data)
def __contains__(self, key):
"""
Invoked when determining whether a specific key is in the dictionary
using `key in d`.
The key is looked up case-insensitively.
"""
k = key
if isinstance(key, six.string_types):
k = k.lower()
return k in self._data
def get(self, key, default=None):
"""
Get the value for a specific key, or the specified default value if
the key does not exist.
The key is looked up case-insensitively.
"""
try:
return self[key]
except KeyError:
return default
def setdefault(self, key, default):
"""
Assign the specified default value for a specific key if the key did
not exist and return the value for the key.
The key is looked up case-insensitively.
"""
if not key in self:
self[key] = default
return self[key]
# Other accessor expressed in terms of iterators
def keys(self):
"""
Return a copied list of the dictionary keys, in their original case.
"""
return list(self.iterkeys())
def values(self):
"""
Return a copied list of the dictionary values.
"""
return list(self.itervalues())
def items(self):
"""
Return a copied list of the dictionary items, where each item is a
tuple of its original key and its value.
"""
return list(self.iteritems())
# Iterators
def iterkeys(self):
"""
Return an iterator through the dictionary keys in their original
case.
"""
for item in six.iteritems(self._data):
yield item[1][0]
def itervalues(self):
"""
Return an iterator through the dictionary values.
"""
for item in six.iteritems(self._data):
yield item[1][1]
def iteritems(self):
"""
Return an iterator through the dictionary items, where each item is a
tuple of its original key and its value.
"""
for item in six.iteritems(self._data):
yield item[1]
def __iter__(self):
"""
Invoked when iterating through the dictionary using `for key in d`.
The returned keys have their original case.
"""
return six.iterkeys(self._data)
# Other stuff
def __repr__(self):
"""
Return a string representation of the `NocaseDict`_ object that is
suitable for debugging.
"""
items = ', '.join([('%r: %r' % (key, value))
for key, value in sorted(self.iteritems())])
return 'NocaseDict({%s})' % items
def update(self, *args, **kwargs):
"""
Update the dictionary from sequences of key/value pairs provided in any
positional arguments, and from key/value pairs provided in any keyword
arguments. The key/value pairs are not copied.
Each positional argument can be:
* an object with a method `items()` that returns an
:term:`py:iterable` of tuples containing key and value.
* an object without such a method, that is an :term:`py:iterable` of
tuples containing key and value.
Each keyword argument is a key/value pair.
"""
for mapping in args:
if hasattr(mapping, 'items'):
for key, value in mapping.items():
self[key] = value
else:
for key, value in mapping:
self[key] = value
for key, value in kwargs.items():
self[key] = value
def clear(self):
"""
Remove all items from the dictionary.
"""
self._data.clear()
def popitem(self):
"""
This function does nothing.
In a standard mapping implementation, it would remove and return an
arbitrary item from the dictionary.
TODO: Why does popitem() do nothing; was it simply not implemented?
"""
pass
def copy(self):
"""
Return a shallow copy of the dictionary (i.e. the keys and values are
not copied).
"""
result = NocaseDict()
result._data = self._data.copy() # pylint: disable=protected-access
return result
def __eq__(self, other):
"""
Invoked when two dictionaries are compared with the `==` operator.
The comparison is based on matching key/value pairs.
The keys are looked up case-insensitively.
"""
for key, self_value in self.iteritems():
if not key in other:
return False
other_value = other[key]
try:
if not self_value == other_value:
return False
except TypeError:
return False # not comparable -> considered not equal
return len(self) == len(other)
def __ne__(self, other):
"""
Invoked when two dictionaries are compared with the `!=` operator.
Implemented by delegating to the `==` operator.
"""
return not self == other
@staticmethod
def __ordering_deprecated():
"""Function to issue deprecation warning for ordered comparisons
"""
warnings.warn(
"Ordering comparisons for pywbem.NocaseDict are deprecated",
DeprecationWarning)
def __lt__(self, other):
self.__ordering_deprecated()
# Delegate to the underlying standard dictionary. This will result in
# a case sensitive comparison, but that will be better than the faulty
# algorithm that was used before. It will raise TypeError "unorderable
# types" in Python 3.
return self._data < other._data
def __gt__(self, other):
"""
Invoked when two dictionaries are compared with the `>` operator.
Implemented by delegating to the `<` operator.
"""
self.__ordering_deprecated()
return other < self
def __ge__(self, other):
"""
Invoked when two dictionaries are compared with the `>=` operator.
Implemented by delegating to the `>` and `==` operators.
"""
self.__ordering_deprecated()
return (self > other) or (self == other)
def __le__(self, other):
"""
Invoked when two dictionaries are compared with the `<=` operator.
Implemented by delegating to the `<` and `==` operators.
"""
self.__ordering_deprecated()
return (self < other) or (self == other)
def _intended_value(intended, unspecified, actual, name, msg):
"""
Return the intended value if the actual value is unspecified or has
the intended value already, and otherwise raise a ValueError with the
specified error message.
Arguments:
* `intended`: The intended value, or sequence of values. The first
item in the sequence will act as the intended value, the others
are also valid values.
* `unspecified`: A value indicating 'unspecified' (usually `None`).
* `actual`: The actual value.
* `name`: The name of the attribute that this is about, for use in the
exception message.
* `msg`: A context setting message, for use in the exception message.
"""
if isinstance(intended, (tuple, list)):
if actual == unspecified:
return intended[0] # the default
elif actual in intended:
return actual
else:
raise ValueError(msg + ", but specifies %s=%r (must be one of %r)"\
% (name, actual, intended))
else:
if actual == unspecified:
return intended
elif actual == intended:
return actual
else:
raise ValueError(msg + ", but specifies %s=%r (must be %r)"\
% (name, actual, intended))
def cmpname(name1, name2):
"""
Compare two CIM names, case-insensitively.
One or both of the items may be `None`, and `None` is considered the lowest
possible value.
The implementation delegates to the '==' and '<' operators of the
name datatypes.
If name1 == name2, 0 is returned.
If name1 < name2, -1 is returned.
Otherwise, +1 is returned.
"""
if name1 is None and name2 is None:
return 0
if name1 is None:
return -1
if name2 is None:
return 1
lower_name1 = name1.lower()
lower_name2 = name2.lower()
if lower_name1 == lower_name2:
return 0
if lower_name1 < lower_name2:
return -1
else:
return 1
def cmpitem(item1, item2):
"""
Compare two items (CIM values, CIM objects, or NocaseDict objects) for
unequality.
Note: Support for comparing the order of the items has been removed
in pywbem v0.9.0.
One or both of the items may be `None`.
The implementation uses the '==' operator of the item datatypes.
If value1 == value2, 0 is returned.
If value1 != value2, 1 is returned.
"""
if item1 is None and item2 is None:
return 0
if item1 is None or item2 is None:
return 1
if item1 == item2:
return 0
return 1
def _convert_unicode(obj):
"""
Convert the input object into a Unicode string (`unicode`for Python 2,
and `str` for Python 3).
If the input object already is a Unicode string, it is simply returned.
If the input object is a byte string, it is decoded using UTF-8.
Otherwise, the input object is translated into its string representation.
"""
if isinstance(obj, six.text_type):
return obj
if isinstance(obj, six.binary_type):
return obj.decode("utf-8")
return six.text_type(obj)
def _ensure_unicode(obj):
"""
Return the input object, ensuring that a byte string is decoded into a
Unicode string (`unicode`for Python 2, and `str` for Python 3).
If the input object is a byte string, it is decoded using UTF-8.
Otherwise, the input object is simply returned.
"""
if isinstance(obj, six.binary_type):
return obj.decode("utf-8")
return obj
def _convert_bytes(obj):
"""
Convert the input object into a byte string (`str`for Python 2, and `bytes`
for Python 3).
If the input object already is a byte string, it is simply returned.
If the input object is a Unicode string, it is encoded using UTF-8.
Otherwise, the input object is translated into its string representation.
"""
if isinstance(obj, six.binary_type):
return obj
if isinstance(obj, six.text_type):
return obj.encode("utf-8")
return six.binary_type(obj)
def _ensure_bytes(obj):
"""
Return the input object, ensuring that a Unicode string is decoded into a
byte string (`str`for Python 2, and `bytes` for Python 3).
If the input object is a Unicode string, it is encoded using UTF-8.
Otherwise, the input object is simply returned.
"""
if isinstance(obj, six.text_type):
return obj.encode("utf-8")
return obj
def _makequalifiers(qualifiers, indent):
"""
Return a MOF fragment for a NocaseDict of qualifiers indented the
number of spaces defined by indent. Return empty string if no qualifiers.
Normally multiline output and may fold qualifiers into multiple lines.
Parameters:
qualifiers (list): List of qualifiers to format.
indent (:term:`integer): Indent level for this set of qualifiers.
"""
if len(qualifiers) == 0:
return ''
return '%s[%s]' % (_indent_str(indent), ',\n '.ljust(indent+2).\
join([q.tomof(indent+2) for q in sorted(qualifiers.values())]))
def _indent_str(indent):
"""
Return a MOF indent pad string from the indent integer variable
that defines number of spaces to indent. Used to format MOF output
"""
return ' '.ljust(indent, ' ')
def mofstr(strvalue, indent=MOF_INDENT, maxline=MAX_MOF_LINE):
# Note: This is a raw docstring because it shows many backslashes, and
# that avoids having to double them.
r"""
Convert the string in `strvalue` into a MOF string constant
(i.e. a string literal), including the surrounding double quotes, and
return that result.
The input string must be a Python unicode string object, and the
returned MOF string constant is also a Python unicode string object.
This function handles MOF escaping and breaking the string into multiple
lines according to the `maxline` and `indent` parameters.
`DSP0004` defines that the character repertoire for MOF string constants
is the entire repertoire for the CIM string datatype. That is, the entire
Unicode character repertoire except for U+0000.
The only character for which DSP0004 requires the use of a MOF escape
sequence in a MOF string constant, is the double quote (because a MOF
string constant is enclosed in double quotes).
DSP0004 defines MOF escape sequences for several more characters, but it
does not require their use in MOF. For example, it is valid for a MOF
string constant to contain the characters U+000D (newline) or U+0009
(horizontal tab).
Now that may not be supported by MOF related tools, and therefore this
function plays it safe and uses MOF escape sequences for all characters
that have short MOF escape sequences defined (except for single quote), and
for all remaining characters in the so called "control range"
U+0001..U+001F, it uses generic MOF escape sequences (e.g. U+0001 becomes
"\x0001")
The following table shows the MOF escape sequences defined in `DSP0004`:
============ =============================================================
MOF escape Character
sequence
============ =============================================================
\b U+0008: Backspace
\t U+0009: Horizontal tab
\n U+000A: Line feed
\f U+000C: Form feed
\r U+000D: Carriage return
\" U+0022: Double quote (") (required to be used)
\' U+0027: Single quote (')
\\ U+005C: Backslash (\)
\x<hex> U+<hex>: Any UCS-2 character, where <hex> is one to four hex
digits, representing its UCS code position (this form is
limited to the UCS-2 character repertoire)
\X<hex> U+<hex>: Any UCS-2 character, where <hex> is one to four hex
digits, representing its UCS code position (this form is
limited to the UCS-2 character repertoire)
============ =============================================================
This function does not tolerate that the input string already contains
MOF escape sequences (it did so before v0.9, but that created more
problems than it solved).
After escaping, the string is broken into multiple lines, for better
readability. The maximum line size is specified via the `maxline`
parameter. The indentation for any spilled over lines (i.e. not the first
line) is specified via the `indent` parameter.
"""
escaped_str = strvalue
# Escape backslash (\)
escaped_str = escaped_str.replace('\\', '\\\\')
# Escape \b, \t, \n, \f, \r
# Note, the Python escape sequences happen to be the same as in MOF
escaped_str = escaped_str.replace('\b', '\\b').\
replace('\t', '\\t').\
replace('\n', '\\n').\
replace('\f', '\\f').\
replace('\r', '\\r')
# Escape remaining control characters (U+0001...U+001F), skipping
# U+0008, U+0009, U+000A, U+000C, U+000D that are already handled.
# We hard code it to be faster, plus we can easily skip already handled
# chars.
# The generic code would be (not skipping already handled chars):
# for cp in range(1, 32):
# c = six.unichr(cp)
# esc = '\\x%04X' % cp
# escaped_str = escaped_str.replace(c, esc)
escaped_str = escaped_str.replace(u'\u0001', '\\x0001').\
replace(u'\u0002', '\\x0002').\
replace(u'\u0003', '\\x0003').\
replace(u'\u0004', '\\x0004').\
replace(u'\u0005', '\\x0005').\
replace(u'\u0006', '\\x0006').\
replace(u'\u0007', '\\x0007').\
replace(u'\u000B', '\\x000B').\
replace(u'\u000E', '\\x000E').\
replace(u'\u000F', '\\x000F').\
replace(u'\u0010', '\\x0010').\
replace(u'\u0011', '\\x0011').\
replace(u'\u0012', '\\x0012').\
replace(u'\u0013', '\\x0013').\
replace(u'\u0014', '\\x0014').\
replace(u'\u0015', '\\x0015').\
replace(u'\u0016', '\\x0016').\
replace(u'\u0017', '\\x0017').\
replace(u'\u0018', '\\x0018').\
replace(u'\u0019', '\\x0019').\
replace(u'\u001A', '\\x001A').\
replace(u'\u001B', '\\x001B').\
replace(u'\u001C', '\\x001C').\
replace(u'\u001D', '\\x001D').\
replace(u'\u001E', '\\x001E').\
replace(u'\u001F', '\\x001F')
# Escape single and double quote
escaped_str = escaped_str.replace('"', '\\"')
escaped_str = escaped_str.replace("'", "\\'")
# Break into multiple strings for better readability
blankfind = maxline - indent - 2
_is = _indent_str(indent)
ret_str_list = list()
# TODO does not account for the extra char that may be appended
# to line (ex. comma). Improvement would to be to test last line
# for one char less than max line length and adjust. To get
# around this, we set max line length default to 79 ks Mar 2016
if escaped_str == '':
ret_str_list.append('""')
else:
while escaped_str != '':
if len(escaped_str) <= blankfind:
ret_str_list.append('"' + escaped_str + '"')
escaped_str = ''
else:
splitpos = escaped_str.rfind(' ', 0, blankfind)
if splitpos < 0:
splitpos = blankfind-1
ret_str_list.append('"' + escaped_str[0:splitpos+1] + '"')
escaped_str = escaped_str[splitpos+1:]
ret_str = ('\n'+_is).join(ret_str_list)
return ret_str
def moftype(cim_type, refclass):
"""
Converts a CIM data type name to MOF syntax.
"""
return (refclass + ' REF') if cim_type == 'reference' else cim_type
[docs]class CIMInstanceName(_CIMComparisonMixin):
"""
A CIM instance path (aka *instance name*).
A CIM instance path references a CIM instance in a namespace in a WBEM
server. Namespace and WBEM server may be unspecified.
This object can be used like a dictionary of the keybindings of the
instance path, with the names of the keybindings (= key properties) as
dictionary keys, and their property values as dictionary values.
Attributes:
classname (:term:`unicode string`):
Name of the creation class of the referenced instance.
This variable will never be `None`.
keybindings (`NocaseDict`_):
Keybindings of the instance path (the key property values of the
referenced instance).
Each dictionary item specifies one key property value, with:
* key (:term:`string`): Key property name
* value (:term:`CIM data type`): Key property value
This variable will never be `None`.
namespace (:term:`unicode string`):
Name of the CIM namespace containing the referenced instance.
`None` means that the namespace is unspecified.
host (:term:`unicode string`):
Host and optionally port of the WBEM server containing the CIM
namespace of the referenced instance, in the format:
``host[:port]``
The host can be specified in any of the usual formats:
* a short or fully qualified DNS hostname
* a literal (= dotted) IPv4 address
* a literal IPv6 address, formatted as defined in :term:`RFC3986`
with the extensions for zone identifiers as defined in
:term:`RFC6874`, supporting ``-`` (minus) for the delimiter
before the zone ID string, as an additional choice to ``%25``.
If no port is specified, the default port depends on the context in
which the object is used.
`None` means that the WBEM server is unspecified.
"""
def __init__(self, classname, keybindings=None, host=None, namespace=None):
"""
Parameters:
classname (:term:`string`):
Name of the creation class of the referenced instance.
Must not be `None`.
keybindings (:class:`py:dict` or `NocaseDict`_):
Keybindings for the instance path (the key property values
of the referenced instance).
For details about the dictionary items, see the corresponding
attribute.
`None` is interpreted as an empty dictionary.
host (:term:`string`):
Host and optionally port of the WBEM server containing the CIM
namespace of the referenced instance.
For details about the string format, see the corresponding instance
variable.
`None` means that the WBEM server is unspecified.
namespace (:term:`string`):
Name of the CIM namespace containing the referenced instance.
`None` means that the namespace is unspecified.
"""
# Make sure we process Unicode strings
classname = _ensure_unicode(classname)
host = _ensure_unicode(host)
namespace = _ensure_unicode(namespace)
if classname is None:
raise ValueError('Instance path must have a class name')
self.classname = classname
self.keybindings = NocaseDict(keybindings)
self.host = host
self.namespace = namespace
def _cmp(self, other):
"""
Comparator function for two :class:`~pywbem.CIMInstanceName` objects.
The comparison is based on the `host`, `namespace`, `classname`,
and `keybindings`, instance attributes, in descending precedence.
The `host` and `namespace` and `classname` attributes are compared
case-insensitively.
Raises `TypeError', if the `other` object is not a
:class:`~pywbem.CIMInstanceName` object.
"""
if self is other:
return 0
if not isinstance(other, CIMInstanceName):
raise TypeError("other must be CIMInstanceName, but is: %s" %\
type(other))
return (cmpname(self.host, other.host) or
cmpname(self.namespace, other.namespace) or
cmpname(self.classname, other.classname) or
cmpitem(self.keybindings, other.keybindings))
[docs] def __str__(self):
"""
Return the untyped WBEM URI of the CIM instance path represented
by the :class:`~pywbem.CIMInstanceName` object.
The returned WBEM URI is consistent with :term:`DSP0207`.
"""
ret_str = ''
if self.host is not None:
ret_str += '//%s/' % self.host
if self.namespace is not None:
ret_str += '%s:' % self.namespace
ret_str += '%s.' % self.classname
for key, value in self.keybindings.items():
ret_str += '%s=' % key
if isinstance(value, (six.integer_types, bool, float)):
ret_str += str(value)
elif isinstance(value, CIMInstanceName):
ret_str += '"%s"' % str(value).replace('\\', '\\\\').replace(
'"', '\\"')
else:
ret_str += '"%s"' % value
ret_str += ','
return ret_str[:-1]
[docs] def __repr__(self):
"""
Return a string representation of the
:class:`~pywbem.CIMInstanceName` object that is suitable for
debugging.
"""
return '%s(classname=%r, keybindings=%r, ' \
'namespace=%r, host=%r)' % \
(self.__class__.__name__, self.classname, self.keybindings,
self.namespace, self.host)
def __contains__(self, key):
return key in self.keybindings
def __getitem__(self, key):
return self.keybindings[key]
def __setitem__(self, key, value):
self.keybindings[key] = value
def __delitem__(self, key):
del self.keybindings[key]
def __len__(self):
return len(self.keybindings)
def __iter__(self):
return six.iterkeys(self.keybindings)
[docs] def copy(self):
"""
Return a copy of the :class:`~pywbem.CIMInstanceName` object.
"""
result = CIMInstanceName(self.classname)
result.keybindings = self.keybindings.copy()
result.host = self.host
result.namespace = self.namespace
return result
[docs] def update(self, *args, **kwargs):
"""
Add the positional arguments and keyword arguments to the keybindings,
updating the values of those that already exist.
"""
self.keybindings.update(*args, **kwargs)
[docs] def has_key(self, key):
"""
Return a boolean indicating whether the instance path has a
keybinding with name `key`.
"""
return key in self.keybindings
[docs] def get(self, key, default=None):
"""
Return the value of the keybinding with name `key`, or a default
value if a keybinding with that name does not exist.
"""
return self.keybindings.get(key, default)
[docs] def keys(self):
"""
Return a copied list of the keybinding names (in their original
lexical case).
"""
return self.keybindings.keys()
[docs] def values(self):
"""
Return a copied list of the keybinding values.
"""
return self.keybindings.values()
[docs] def items(self):
"""
Return a copied list of the keybindings, where each item is a tuple
of its keybinding name (in the original lexical case) and its value.
"""
return self.keybindings.items()
[docs] def iterkeys(self):
"""
Iterate through the keybinding names (in their original lexical case).i
"""
return self.keybindings.iterkeys()
[docs] def itervalues(self):
"""
Iterate through the keybinding values.
"""
return self.keybindings.itervalues()
[docs] def iteritems(self):
"""
Iterate through the keybindings, where each item is a tuple of the
keybinding name (in the original lexical case) and the keybinding
value.
"""
return self.keybindings.iteritems()
# pylint: disable=too-many-branches
[docs] def tocimxml(self):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMInstanceName` object,
as an instance of an appropriate subclass of :term:`Element`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
"""
if isinstance(self.keybindings, str):
# This cannot happen; self.keybindings is always a NocaseDict:
raise TypeError("Unexpected: keybindings has string type: %s" % \
repr(self.keybindings))
# TODO: Remove this old code after verifying that it works.
# #Class with single key string property
# instancename_xml = cim_xml.INSTANCENAME(
# self.classname,
# cim_xml.KEYVALUE(self.keybindings, 'string'))
# Note: The CIM data types are derived from the built-in types,
# so we cannot use isinstance() for this test.
# pylint: disable=unidiomatic-typecheck
elif builtin_type(self.keybindings) in six.integer_types + (float,):
# This cannot happen; self.keybindings is always a NocaseDict:
raise TypeError("Unexpected: keybindings has numeric type: %s" % \
repr(self.keybindings))
# TODO: Remove this old code after verifying that it works.
# # Class with single key numeric property
# instancename_xml = cim_xml.INSTANCENAME(
# self.classname,
# cim_xml.KEYVALUE(str(self.keybindings), 'numeric'))
elif isinstance(self.keybindings, NocaseDict):
kbs = []
for key_bind in self.keybindings.items():
# Keybindings can be integers, booleans, strings or
# value references.
if hasattr(key_bind[1], 'tocimxml'):
kbs.append(cim_xml.KEYBINDING(
key_bind[0],
cim_xml.VALUE_REFERENCE(key_bind[1].tocimxml())))
continue
if isinstance(key_bind[1], bool):
type_ = 'boolean'
if key_bind[1]:
value = 'TRUE'
else:
value = 'FALSE'
elif isinstance(key_bind[1], six.integer_types + (float,)):
# Numeric CIM data types derive from int, long or float.
# Note: int is a subtype of bool, but bool is already
# tested further up.
type_ = 'numeric'
value = str(key_bind[1])
elif isinstance(key_bind[1], six.string_types):
type_ = 'string'
value = _ensure_unicode(key_bind[1])
else:
raise TypeError('Invalid keybinding type for keybinding '\
'%s: %s' % (key_bind[0], builtin_type(key_bind[1])))
kbs.append(cim_xml.KEYBINDING(
key_bind[0],
cim_xml.KEYVALUE(value, type_)))
instancename_xml = cim_xml.INSTANCENAME(self.classname, kbs)
else:
# This cannot happen; self.keybindings is always a NocaseDict:
raise TypeError("Unexpected: keybindings has type: %s" % \
repr(self.keybindings))
# TODO: Remove this old code after verifying that it works.
# # Value reference
# instancename_xml = cim_xml.INSTANCENAME(
# self.classname,
# cim_xml.VALUE_REFERENCE(self.keybindings.tocimxml()))
# Instance name plus namespace = LOCALINSTANCEPATH
if self.host is None and self.namespace is not None:
return cim_xml.LOCALINSTANCEPATH(
cim_xml.LOCALNAMESPACEPATH(
[cim_xml.NAMESPACE(ns)
for ns in self.namespace.split('/')]),
instancename_xml)
# Instance name plus host and namespace = INSTANCEPATH
if self.host is not None and self.namespace is not None:
return cim_xml.INSTANCEPATH(
cim_xml.NAMESPACEPATH(
cim_xml.HOST(self.host),
cim_xml.LOCALNAMESPACEPATH([
cim_xml.NAMESPACE(ns)
for ns in self.namespace.split('/')])),
instancename_xml)
# Just a regular INSTANCENAME
return instancename_xml
[docs] def tocimxmlstr(self, indent=None):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMInstanceName` object, as a :term:`unicode string`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
Parameters:
indent (:term:`string` or :term:`integer`):
`None` indicates that a single-line version of the XML should be
returned, without any whitespace between the XML elements.
Other values indicate that a prettified, multi-line version of the
XML should be returned. A string value specifies the indentation
string to be used for each level of nested XML elements. An integer
value specifies an indentation string of so many blanks.
Returns:
The CIM-XML representation of the value, as a
:term:`unicode string`.
"""
return tocimxmlstr(self, indent)
[docs]class CIMInstance(_CIMComparisonMixin):
"""
A CIM instance, optionally including its instance path.
This object can be used like a dictionary of its properties, with the
names of the properties as dictionary keys, and their values as dictionary
values.
Attributes:
classname (:term:`unicode string`):
Name of the creation class of the instance.
This variable will never be `None`.
properties (`NocaseDict`_):
Properties for the instance.
Each dictionary item specifies one property value, with:
* key (:term:`string`): Property name
* value (:class:`~pywbem.CIMProperty`): Property value
This variable will never be `None`.
qualifiers (`NocaseDict`_):
Qualifiers for the instance.
Each dictionary item specifies one qualifier value, with:
* key (:term:`string`): Qualifier name
* value (:class:`~pywbem.CIMQualifier`): Qualifier value
Note that :term:`DSP0200` has deprecated the presence of qualifier
values on CIM instances.
This variable will never be `None`.
path (CIMInstanceName):
Instance path of the instance.
`None` means that the instance path is unspecified.
property_list (:class:`py:list` of :term:`unicode string`):
List of property names for use as a filter by some operations on
the instance.
`None` means that the properties are not filtered.
"""
# pylint: disable=too-many-arguments
def __init__(self, classname, properties=None, qualifiers=None,
path=None, property_list=None):
# pylint: disable=line-too-long
"""
Parameters:
classname (:term:`string`):
Name of the creation class of the instance.
Must not be `None`.
properties (:class:`py:dict` or `NocaseDict`_):
Properties for the instance.
For details about the dictionary items, see the corresponding
attribute.
`None` is interpreted as an empty dictionary.
qualifiers (:class:`py:dict` or `NocaseDict`_):
Qualifiers for the instance.
For details about the dictionary items, see the corresponding
attribute.
`None` is interpreted as an empty dictionary.
Note that :term:`DSP0200` has deprecated the presence of qualifier
values on CIM instances.
path (CIMInstanceName):
Instance path for the instance.
`None` means that the instance path is unspecified.
property_list (:term:`py:iterable` of :term:`string`):
List of property names for use as a filter by some operations on
the instance.
`None` means that the properties are not filtered.
"""
self.classname = _ensure_unicode(classname)
self.qualifiers = NocaseDict(qualifiers)
# TODO: Add support for accepting qualifiers as plain dict
self.path = path
if property_list is not None:
self.property_list = [_ensure_unicode(x).lower() \
for x in property_list]
else:
self.property_list = None
# Assign initialised property values and run through
# __setitem__ to enforce CIM data types for each property.
self.properties = NocaseDict()
if properties:
for key, value in properties.items():
self.__setitem__(key, value)
def _cmp(self, other):
"""
Comparator function for two :class:`~pywbem.CIMInstance` objects.
The comparison is based on the `classname`, `path`, `properties`,
and `qualifiers` instance attributes, in descending precedence.
The `classname` attribute is compared case-insensitively.
"""
if self is other:
return 0
if not isinstance(other, CIMInstance):
raise TypeError("other must be CIMInstance, but is: %s" %\
type(other))
return (cmpname(self.classname, other.classname) or
cmpitem(self.path, other.path) or
cmpitem(self.properties, other.properties) or
cmpitem(self.qualifiers, other.qualifiers))
[docs] def __str__(self):
"""
Return a short string representation of the
:class:`~pywbem.CIMInstance` object for human consumption.
"""
return '%s(classname=%r, path=%r, ...)' % \
(self.__class__.__name__, self.classname, self.path)
[docs] def __repr__(self):
"""
Return a string representation of the :class:`~pywbem.CIMInstance`
object that is suitable for debugging.
"""
return '%s(classname=%r, path=%r, ' \
'properties=%r, property_list=%r, ' \
'qualifiers=%r)' % \
(self.__class__.__name__, self.classname, self.path,
self.properties, self.property_list,
self.qualifiers)
def __contains__(self, key):
return key in self.properties
def __getitem__(self, key):
return self.properties[key].value
def __setitem__(self, key, value):
# Don't let anyone set integer or float values. You must use
# a subclass from the cim_type module.
# Note: The CIM data types are derived from the built-in types,
# so we cannot use isinstance() for this test.
# pylint: disable=unidiomatic-typecheck
if builtin_type(value) in six.integer_types + (float,):
raise TypeError(
"Type of numeric value for a property must be a "\
"CIM data type, but is %s" % builtin_type(value))
if self.property_list is not None and key.lower() not in \
self.property_list:
if self.path is not None and key not in self.path.keybindings:
return
# Convert value to appropriate pywbem type
if isinstance(value, CIMProperty):
val = value
else:
val = CIMProperty(key, value)
self.properties[key] = val
if self.path is not None and key in self.path.keybindings:
self.path[key] = val.value
def __delitem__(self, key):
del self.properties[key]
def __len__(self):
return len(self.properties)
def __iter__(self):
return six.iterkeys(self.properties)
[docs] def copy(self):
"""
Return copy of the :class:`~pywbem.CIMInstance` object.
"""
result = CIMInstance(self.classname)
result.properties = self.properties.copy()
result.qualifiers = self.qualifiers.copy()
result.path = (self.path is not None and \
[self.path.copy()] or [None])[0]
return result
[docs] def update(self, *args, **kwargs):
"""
Add the positional arguments and keyword arguments to the properties,
updating the values of those that already exist.
"""
for mapping in args:
if hasattr(mapping, 'items'):
for key, value in mapping.items():
self[key] = value
else:
for (key, value) in mapping:
self[key] = value
for key, value in kwargs.items():
self[key] = value
[docs] def update_existing(self, *args, **kwargs):
"""
Update the values of already existing properties from the positional
arguments and keyword arguments.
"""
for mapping in args:
if hasattr(mapping, 'items'):
for key, value in mapping.items():
try:
prop = self.properties[key]
except KeyError:
continue
prop.value = tocimobj(prop.type, value)
else:
for (key, value) in mapping:
try:
prop = self.properties[key]
except KeyError:
continue
prop.value = tocimobj(prop.type, value)
for key, value in kwargs.items():
try:
prop = self.properties[key]
except KeyError:
continue
prop.value = tocimobj(prop.type, value)
[docs] def has_key(self, key):
"""
Return a boolean indicating whether the instance has a property with
name `key`.
"""
return key in self.properties
[docs] def get(self, key, default=None):
"""
Return the value of the property with name `key`, or a default value if
a property with that name does not exist.
"""
prop = self.properties.get(key, None)
return default if prop is None else prop.value
[docs] def keys(self):
"""
Return a copied list of the property names (in their original lexical
case).
"""
return self.properties.keys()
[docs] def values(self):
"""
Return a copied list of the property values.
"""
return [v.value for v in self.properties.values()]
[docs] def items(self):
"""
Return a copied list of the properties, where each item is a tuple
of the property name (in the original lexical case) and the property
value.
"""
return [(key, v.value) for key, v in self.properties.items()]
[docs] def iterkeys(self):
"""
Iterate through the property names (in their original lexical
case).
"""
return self.properties.iterkeys()
[docs] def itervalues(self):
"""
Iterate through the property values.
"""
for _, val in self.properties.iteritems():
yield val.value
[docs] def iteritems(self):
"""
Iterate through the property names (in their original lexical case).
"""
for key, val in self.properties.iteritems():
yield (key, val.value)
[docs] def tocimxml(self):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMInstance` object,
as an instance of an appropriate subclass of :term:`Element`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
"""
props = []
for key, value in self.properties.items():
# Value has already been converted into a CIM object
# property type (e.g for creating null property values).
if isinstance(value, CIMProperty):
props.append(value)
continue
props.append(CIMProperty(key, value))
instance_xml = cim_xml.INSTANCE(
self.classname,
properties=[p.tocimxml() for p in props],
qualifiers=[q.tocimxml() for q in self.qualifiers.values()])
if self.path is None:
return instance_xml
return cim_xml.VALUE_NAMEDINSTANCE(self.path.tocimxml(),
instance_xml)
[docs] def tocimxmlstr(self, indent=None):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMInstance` object, as a :term:`unicode string`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
Parameters:
indent (:term:`string` or :term:`integer`):
`None` indicates that a single-line version of the XML should be
returned, without any whitespace between the XML elements.
Other values indicate that a prettified, multi-line version of the
XML should be returned. A string value specifies the indentation
string to be used for each level of nested XML elements. An integer
value specifies an indentation string of so many blanks.
Returns:
The CIM-XML representation of the object, as a
:term:`unicode string`.
"""
return tocimxmlstr(self, indent)
[docs] def tomof(self, indent=0):
"""
Return a :term:`unicode string` that is a MOF fragment with the
instance specification represented by the :class:`~pywbem.CIMInstance`
object.
Parameters:
indent (:term:`integer`):
Number of spaces the initial line of the output is indented.
"""
ret_str = 'instance of %s {\n' % self.classname
for prop in self.properties.values():
ret_str += prop.tomof(True, (indent+MOF_INDENT))
ret_str += '};\n'
return ret_str
[docs]class CIMClassName(_CIMComparisonMixin):
"""
A CIM class path.
A CIM class path references a CIM class in a namespace in a WBEM server.
Namespace and WBEM server may be unspecified.
Attributes:
classname (:term:`unicode string`):
Name of the referenced class.
This variable will never be `None`.
namespace (:term:`unicode string`):
Name of the CIM namespace containing the referenced class.
`None` means that the namespace is unspecified.
host (:term:`unicode string`):
Host and optionally port of the WBEM server containing the CIM
namespace of the referenced class, in the format:
``host[:port]``
The host can be specified in any of the usual formats:
* a short or fully qualified DNS hostname
* a literal (= dotted) IPv4 address
* a literal IPv6 address, formatted as defined in :term:`RFC3986`
with the extensions for zone identifiers as defined in
:term:`RFC6874`, supporting ``-`` (minus) for the delimiter
before the zone ID string, as an additional choice to ``%25``.
If no port is specified, the default port depends on the context in
which the object is used.
`None` means that the WBEM server is unspecified.
"""
def __init__(self, classname, host=None, namespace=None):
"""
Parameters:
classname (:term:`string`):
Name of the referenced class.
Must not be `None`.
host (:term:`string`):
Host and optionally port of the WBEM server containing the CIM
namespace of the referenced class.
For details about the string format, see the corresponding instance
variable.
`None` means that the WBEM server is unspecified.
namespace (:term:`string`):
Name of the CIM namespace containing the referenced class.
`None` means that the namespace is unspecified.
"""
# Make sure we process Unicode strings
classname = _ensure_unicode(classname)
host = _ensure_unicode(host)
namespace = _ensure_unicode(namespace)
if not isinstance(classname, six.string_types):
raise TypeError(
"classname argument has an invalid type: %s "\
"(expected string)" % builtin_type(classname))
# TODO: There are some odd restrictions on what a CIM
# classname can look like (i.e must start with a
# non-underscore and only one underscore per classname).
self.classname = classname
self.host = host
self.namespace = namespace
[docs] def copy(self):
"""
Return a copy the :class:`~pywbem.CIMClassName` object.
"""
return CIMClassName(self.classname, host=self.host,
namespace=self.namespace)
def _cmp(self, other):
"""
Comparator function for two :class:`~pywbem.CIMClassName` objects.
The comparison is based on the `host`, `namespace`, and `classname`
attributes of the :class:`~pywbem.CIMClassName` objects, in descending
precedence.
All of them are compared case-insensitively.
"""
if self is other:
return 0
if not isinstance(other, CIMClassName):
raise TypeError("other must be CIMClassName, but is: %s" %\
type(other))
return (cmpname(self.host, other.host) or
cmpname(self.namespace, other.namespace) or
cmpname(self.classname, other.classname))
[docs] def __str__(self):
"""
Return the untyped WBEM URI of the CIM class path represented by the
:class:`~pywbem.CIMClassName` object.
The returned WBEM URI is consistent with :term:`DSP0207`.
"""
ret_str = ''
if self.host is not None:
ret_str += '//%s/' % self.host
if self.namespace is not None:
ret_str += '%s:' % self.namespace
ret_str += self.classname
return ret_str
[docs] def __repr__(self):
"""
Return a string representation of the :class:`~pywbem.CIMClassName`
object that is suitable for debugging.
"""
return '%s(classname=%r, namespace=%r, ' \
'host=%r)' % \
(self.__class__.__name__, self.classname, self.namespace,
self.host)
[docs] def tocimxml(self):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMClassName` object,
as an instance of an appropriate subclass of :term:`Element`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
"""
classname = cim_xml.CLASSNAME(self.classname)
if self.namespace is not None:
localnsp = cim_xml.LOCALNAMESPACEPATH(
[cim_xml.NAMESPACE(ns)
for ns in self.namespace.split('/')])
if self.host is not None:
# Classname + namespace + host = CLASSPATH
return cim_xml.CLASSPATH(
cim_xml.NAMESPACEPATH(cim_xml.HOST(self.host), localnsp),
classname)
# Classname + namespace = LOCALCLASSPATH
return cim_xml.LOCALCLASSPATH(localnsp, classname)
# Just classname = CLASSNAME
return cim_xml.CLASSNAME(self.classname)
[docs] def tocimxmlstr(self, indent=None):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMClassName` object, as a :term:`unicode string`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
Parameters:
indent (:term:`string` or :term:`integer`):
`None` indicates that a single-line version of the XML should be
returned, without any whitespace between the XML elements.
Other values indicate that a prettified, multi-line version of the
XML should be returned. A string value specifies the indentation
string to be used for each level of nested XML elements. An integer
value specifies an indentation string of so many blanks.
Returns:
The CIM-XML representation of the object, as a
:term:`unicode string`.
"""
return tocimxmlstr(self, indent)
[docs]class CIMClass(_CIMComparisonMixin):
"""
A CIM class.
Attributes:
classname (:term:`unicode string`):
Name of the class.
This variable will never be `None`.
superclass (:term:`unicode string`):
Name of the superclass of the class.
`None` means that the class is a top-level class.
properties (`NocaseDict`_):
Property declarations of the class.
Each dictionary item specifies one property declaration, with:
* key (:term:`string`): Property name
* value (:class:`~pywbem.CIMProperty`): Property declaration
This variable will never be `None`.
methods (`NocaseDict`_):
Method declarations of the class.
Each dictionary item specifies one method declaration, with:
* key (:term:`string`): Nethod name
* value (:class:`~pywbem.CIMMethod`): Method declaration
This variable will never be `None`.
qualifiers (`NocaseDict`_):
Qualifier values of the class.
Each dictionary item specifies one qualifier value, with:
* key (:term:`string`): Qualifier name
* value (:class:`~pywbem.CIMQualifier`): Qualifier value
This variable will never be `None`.
"""
# pylint: disable=too-many-arguments
def __init__(self, classname, properties=None, methods=None,
superclass=None, qualifiers=None):
"""
Parameters:
classname (:term:`string`):
Name for the class.
Must not be `None`.
properties (:class:`py:dict` or `NocaseDict`_):
Property declarations for the class.
For details about the dictionary items, see the corresponding
attribute.
`None` is interpreted as an empty dictionary.
methods (:class:`py:dict` or `NocaseDict`_):
Method declarations for the class.
For details about the dictionary items, see the corresponding
attribute.
`None` is interpreted as an empty dictionary.
superclass (:term:`string`):
Name of the superclass for the class.
If `None`, the class will be a top-level class.
qualifiers (:class:`py:dict` or `NocaseDict`_):
Qualifier values for the class.
For details about the dictionary items, see the corresponding
attribute.
If `None`, the class will have no qualifiers.
"""
self.classname = _ensure_unicode(classname)
self.properties = NocaseDict(properties)
self.methods = NocaseDict(methods)
self.superclass = _ensure_unicode(superclass)
self.qualifiers = NocaseDict(qualifiers)
def _cmp(self, other):
"""
Comparator function for two :class:`~pywbem.CIMClass` objects.
The comparison is based on the `classname`, `superclass`, `qualifiers`,
`properties` and `methods` instance attributes, in descending
precedence.
The `classname` and `superclass` attributes are compared
case-insensitively.
"""
if self is other:
return 0
if not isinstance(other, CIMClass):
raise TypeError("other must be CIMClass, but is: %s" %\
type(other))
return (cmpname(self.classname, other.classname) or
cmpname(self.superclass, other.superclass) or
cmpitem(self.qualifiers, other.qualifiers) or
cmpitem(self.properties, other.properties) or
cmpitem(self.methods, other.methods))
[docs] def __str__(self):
"""
Return a short string representation of the
:class:`~pywbem.CIMClass` object for human consumption.
"""
return '%s(classname=%r, ...)' % \
(self.__class__.__name__, self.classname)
[docs] def __repr__(self):
"""
Return a string representation of the :class:`~pywbem.CIMClass`
object that is suitable for debugging.
"""
return '%s(classname=%r, superclass=%r, ' \
'properties=%r, methods=%r, qualifiers=%r)' % \
(self.__class__.__name__, self.classname, self.superclass,
self.properties, self.methods, self.qualifiers)
[docs] def copy(self):
"""
Return a copy of the :class:`~pywbem.CIMClass` object.
"""
result = CIMClass(self.classname)
result.properties = self.properties.copy()
result.methods = self.methods.copy()
result.superclass = self.superclass
result.qualifiers = self.qualifiers.copy()
return result
[docs] def tocimxml(self):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMClass` object,
as an instance of an appropriate subclass of :term:`Element`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
"""
return cim_xml.CLASS(
self.classname,
properties=[p.tocimxml() for p in self.properties.values()],
methods=[m.tocimxml() for m in self.methods.values()],
qualifiers=[q.tocimxml() for q in self.qualifiers.values()],
superclass=self.superclass)
[docs] def tocimxmlstr(self, indent=None):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMClass` object, as a :term:`unicode string`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
Parameters:
indent (:term:`string` or :term:`integer`):
`None` indicates that a single-line version of the XML should be
returned, without any whitespace between the XML elements.
Other values indicate that a prettified, multi-line version of the
XML should be returned. A string value specifies the indentation
string to be used for each level of nested XML elements. An integer
value specifies an indentation string of so many blanks.
Returns:
The CIM-XML representation of the object, as a
:term:`unicode string`.
"""
return tocimxmlstr(self, indent)
[docs] def tomof(self):
"""
Return a :term:`unicode string` that is a MOF fragment with the
class definition represented by the :class:`~pywbem.CIMClass`
object.
"""
indent = MOF_INDENT
# Qualifiers definition or empty line
ret_str = '%s\n' % (_makequalifiers(self.qualifiers,
indent))
ret_str += 'class %s ' % self.classname
# Superclass
if self.superclass is not None:
ret_str += ': %s ' % self.superclass
ret_str += '{\n'
# Properties; indent one level from class definition
for prop_val in self.properties.values():
ret_str += prop_val.tomof(False, indent)
# Methods, indent one level from class definition
for method in self.methods.values():
ret_str += '\n%s' % method.tomof(indent)
ret_str += '};\n'
return ret_str
# pylint: disable=too-many-statements,too-many-instance-attributes
[docs]class CIMProperty(_CIMComparisonMixin):
"""
A CIM property.
This object can be used in a :class:`~pywbem.CIMInstance` object for
representing a property value, or in a :class:`~pywbem.CIMClass` object
for representing a property declaration.
For property values in CIM instances:
* The `value` attribute is the actual value of the property.
* Qualifiers are not allowed.
For property declarations in CIM classes:
* The `value` attribute is the default value of the property
declaration.
* Qualifiers are allowed.
Scalar (=non-array) properties may have a value of NULL (= `None`), any
primitive CIM data type, reference type, and string type with embedded
instance or embedded object.
Array properties may be Null or may have elements with a value of NULL, any
primitive CIM data type, and string type with embedded instance or embedded
object. Reference types are not allowed in property arrays in CIM, as per
:term:`DSP0004`.
Attributes:
name (:term:`unicode string`):
Name of the property.
This variable will never be `None`.
value (:term:`CIM data type`):
Value of the property (interpreted as actual value when
representing a property value, and as default value for property
declarations).
`None` means that the value is Null.
For CIM data types string and char16, this attribute will be a
:term:`unicode string`, even when specified as a :term:`byte string`
in the constructor.
type (:term:`unicode string`):
Name of the CIM data type of the property (e.g. ``"uint8"``).
This variable will never be `None`.
reference_class (:term:`unicode string`):
The name of the referenced class, for reference properties.
`None`, for non-reference properties.
embedded_object (:term:`unicode string`):
A string value indicating the kind of embedded object represented
by the property value.
The following values are defined for this parameter:
* ``"instance"``: The property is declared with the
``EmbeddedInstance`` qualifier, indicating that the property
value is an embedded instance of the class specified as the value
of the ``EmbeddedInstance`` qualifier.
The property value must be a :class:`~pywbem.CIMInstance` object,
or `None`.
* ``"object"``: The property is declared with the
``EmbeddedObject`` qualifier, indicating that the property
value is an embedded object (instance or class) of which the
class name is not known.
The property value must be a :class:`~pywbem.CIMInstance` or
:class:`~pywbem.CIMClass` object, or `None`.
* `None`, for properties not representing embedded objects.
is_array (:class:`py:bool`):
A boolean indicating whether the property is an array (`True`) or a
scalar (`False`).
This variable will never be `None`.
array_size (:term:`integer`):
The size of the array property, for fixed-size arrays.
`None` means that the array property has variable size, or that it is
not an array.
class_origin (:term:`unicode string`):
The CIM class origin of the property (the name
of the most derived class that defines or overrides the property in
the class hierarchy of the class owning the property).
`None` means that class origin information is not available.
propagated (:class:`py:bool`):
If not `None`, indicates whether the property declaration has been
propagated from a superclass to this class, or the property value
has been propagated from the creation class to this instance (the
latter is not really used).
`None` means that propagation information is not available.
qualifiers (`NocaseDict`_):
Qualifier values for the property declaration.
Each dictionary item specifies one qualifier value, with:
* key (:term:`string`): Qualifier name
* value (:class:`~pywbem.CIMQualifier`): Qualifier value
This variable will never be `None`.
"""
# pylint: disable=too-many-statements
def __init__(self, name, value, type=None,
class_origin=None, array_size=None, propagated=None,
is_array=None, reference_class=None, qualifiers=None,
embedded_object=None):
# pylint: disable=redefined-builtin,too-many-arguments,too-many-branches
# pylint: disable=too-many-statements,too-many-instance-attributes
# pylint: disable=too-many-instance-attributes
"""
The constructor infers optional parameters that are not specified (for
example, it infers `type` from the Python type of `value` and other
information). If the specified parameters are inconsistent, an
exception is raised. If an optional parameter is needed for some reason,
an exception is raised.
Parameters:
name (:term:`string`):
Name of the property.
Must not be `None`.
value (:term:`CIM data type`):
Value of the property (interpreted as actual value when
representing a property value, and as default value for property
declarations).
`None` means that the property is Null.
type (:term:`string`):
Name of the CIM data type of the property (e.g. ``"uint8"``).
`None` means that the parameter is unspecified, causing the
corresponding attribute to be inferred. An exception is
raised if it cannot be inferred.
class_origin (:term:`string`):
The CIM class origin of the property (the name
of the most derived class that defines or overrides the property in
the class hierarchy of the class owning the property).
`None` means that class origin information is not available.
array_size (:term:`integer`):
The size of the array property, for fixed-size arrays.
`None` means that the array property has variable size.
propagated (:class:`py:bool`):
If not `None`, indicates whether the property declaration has been
propagated from a superclass to this class, or the property value
has been propagated from the creation class to this instance (the
latter is not really used).
`None` means that propagation information is not available.
is_array (:class:`py:bool`):
A boolean indicating whether the property is an array (`True`) or a
scalar (`False`).
`None` means that the parameter is unspecified, causing the
corresponding attribute to be inferred from the `value`
parameter, and if that is `None` it defaults to `False` (scalar).
reference_class (:term:`string`):
The name of the referenced class, for reference properties.
`None` means that the parameter is unspecified, causing the
corresponding attribute to be inferred. An exception is
raised if it cannot be inferred.
qualifiers (:class:`py:dict` or `NocaseDict`_):
Qualifier values for the property declaration.
For details about the dictionary items, see the corresponding
attribute.
`None` is interpreted as an empty dictionary.
embedded_object (:term:`string`):
A string value indicating the kind of embedded object represented
by the property value. Has no meaning for property declarations.
For details about the possible values, see the corresponding
attribute.
In addition, `None` means that the value is unspecified, causing
the corresponding attribute to be inferred. An exception is
raised if it cannot be inferred.
Examples:
::
# a string property:
CIMProperty("MyString", "abc")
# a uint8 property:
CIMProperty("MyNum", 42, "uint8")
# a uint8 property:
CIMProperty("MyNum", Uint8(42))
# a uint8 array property:
CIMProperty("MyNumArray", [1,2,3], "uint8")
# a reference property:
CIMProperty("MyRef", CIMInstanceName(...))
# an embedded object property containing a class:
CIMProperty("MyEmbObj", CIMClass(...))
# an embedded object property containing an instance:
CIMProperty("MyEmbObj", CIMInstance(...), embedded_object="object")
# an embedded instance property:
CIMProperty("MyEmbInst", CIMInstance(...))
# a string property that is Null:
CIMProperty("MyString", None, "string")
# a uint8 property that is Null:
CIMProperty("MyNum", None, "uint8")
# a reference property that is Null:
CIMProperty("MyRef", None, reference_class="MyClass")
# an embedded object property that is Null:
CIMProperty("MyEmbObj", None, embedded_object="object")
# an embedded instance property that is Null:
CIMProperty("MyEmbInst", None, embedded_object="instance")
"""
type_ = type # Minimize usage of the builtin 'type'
# Make sure we process Unicode strings
name = _ensure_unicode(name)
type_ = _ensure_unicode(type_)
value = _ensure_unicode(value)
class_origin = _ensure_unicode(class_origin)
propagated = _ensure_unicode(propagated)
reference_class = _ensure_unicode(reference_class)
embedded_object = _ensure_unicode(embedded_object)
# Check `name`
if name is None:
raise ValueError('Property must have a name')
# General checks:
if embedded_object not in (None, 'instance', 'object'):
raise ValueError('Property %r specifies an invalid ' \
'embedded_object=%r' % (name, embedded_object))
if is_array not in (None, True, False):
raise ValueError('Property %r specifies an invalid ' \
'is_array=%r' % (name, is_array))
# Set up is_array
if isinstance(value, (list, tuple)):
is_array = _intended_value(
True, None, is_array, 'is_array',
'Property %r has a value that is an array (%s)' % \
(name, builtin_type(value)))
elif value is not None: # Scalar value
is_array = _intended_value(
False, None, is_array, 'is_array',
'Property %r has a value that is a scalar (%s)' % \
(name, builtin_type(value)))
else: # Null value
if is_array is None:
is_array = False # For compatibility with old default
if not is_array and array_size is not None:
raise ValueError('Scalar property %r specifies array_size=%r ' \
'(must be None)' % (name, array_size))
# Determine type, embedded_object, and reference_class attributes.
# Make sure value is CIM-typed.
if is_array: # Array property
if reference_class is not None:
raise ValueError(
'Array property %r cannot specify reference_class' % name)
elif value is None or len(value) == 0 or value[0] is None:
# Cannot infer from value, look at embedded_object and type
if embedded_object == 'instance':
msg = 'Array property %r contains embedded instances' % name
type_ = _intended_value('string', None, type_, 'type', msg)
elif embedded_object == 'object':
msg = 'Array property %r contains embedded objects' % name
type_ = _intended_value('string', None, type_, 'type', msg)
elif type_ is not None:
# Leave type as specified, but check it for validity
dummy_type_obj = type_from_name(type_)
else:
raise ValueError(
'Cannot infer type of array property %r that is ' \
'Null, empty, or has Null as its first element' % \
name)
elif isinstance(value[0], CIMInstance):
msg = 'Array property %r contains CIMInstance values' % name
type_ = _intended_value('string', None, type_, 'type', msg)
embedded_object = _intended_value(
('instance', 'object'), None, embedded_object,
'embedded_object', msg)
elif isinstance(value[0], CIMClass):
msg = 'Array property %r contains CIMClass values' % name
type_ = _intended_value('string', None, type_, 'type', msg)
embedded_object = _intended_value(
'object', None, embedded_object, 'embedded_object', msg)
elif isinstance(value[0], (datetime, timedelta)):
value = [CIMDateTime(val) if val is not None
else val for val in value]
msg = 'Array property %r contains datetime or timedelta ' \
'values' % name
type_ = _intended_value('datetime', None, type_, 'type', msg)
embedded_object = _intended_value(
None, None, embedded_object, 'embedded_object', msg)
elif type_ == 'datetime':
value = [CIMDateTime(val) if val is not None
and not isinstance(val, CIMDateTime)
else val for val in value]
msg = 'Array property %r specifies CIM data type %r' % \
(name, type_)
embedded_object = _intended_value(
None, None, embedded_object, 'embedded_object', msg)
elif type_ is None:
# Determine simple type from (non-Null) value
type_ = cimtype(value[0])
msg = 'Array property %r contains simple typed values ' \
'with no CIM data type specified' % name
embedded_object = _intended_value(
None, None, embedded_object, 'embedded_object', msg)
else: # type is specified and value (= entire array) is not Null
# Make sure the array elements are of the corresponding Python
# type.
value = [type_from_name(type_)(val) if val is not None
else val for val in value]
msg = 'Array property %r contains simple typed values ' \
'and specifies CIM data type %r' % (name, type_)
embedded_object = _intended_value(
None, None, embedded_object, 'embedded_object', msg)
else: # Scalar property
if value is None:
# Try to infer from embedded_object, reference_class, and type
if embedded_object == 'instance':
msg = 'Property %r contains embedded instance' % name
type_ = _intended_value('string', None, type_, 'type', msg)
reference_class = _intended_value(
None, None, reference_class, 'reference_class', msg)
elif embedded_object == 'object':
msg = 'Property %r contains embedded object' % name
type_ = _intended_value('string', None, type_, 'type', msg)
reference_class = _intended_value(
None, None, reference_class, 'reference_class', msg)
elif reference_class is not None:
msg = 'Property %r is a reference' % name
embedded_object = _intended_value(
None, None, embedded_object, 'embedded_object', msg)
type_ = _intended_value(
'reference', None, type_, 'type', msg)
elif type_ is not None:
# Leave type as specified, but check it for validity
dummy_type_obj = type_from_name(type_)
else:
raise ValueError('Cannot infer type of simple ' \
'property %r that is Null' % name)
elif isinstance(value, CIMInstanceName):
msg = 'Property %r has a CIMInstanceName value with ' \
'classname=%r' % (name, value.classname)
reference_class = _intended_value(
value.classname, None, reference_class, 'reference_class',
msg)
type_ = _intended_value('reference', None, type_, 'type', msg)
embedded_object = _intended_value(
None, None, embedded_object, 'embedded_object', msg)
elif isinstance(value, CIMInstance):
msg = 'Property %r has a CIMInstance value' % name
type_ = _intended_value('string', None, type_, 'type', msg)
embedded_object = _intended_value(
('instance', 'object'), None, embedded_object,
'embedded_object', msg)
reference_class = _intended_value(
None, None, reference_class, 'reference_class', msg)
elif isinstance(value, CIMClass):
msg = 'Property %r has a CIMClass value' % name
type_ = _intended_value('string', None, type_, 'type', msg)
embedded_object = _intended_value(
'object', None, embedded_object, 'embedded_object', msg)
reference_class = _intended_value(
None, None, reference_class, 'reference_class', msg)
elif isinstance(value, (datetime, timedelta)):
value = CIMDateTime(value)
msg = 'Property %r has a datetime or timedelta value' % name
type_ = _intended_value('datetime', None, type_, 'type', msg)
embedded_object = _intended_value(
None, None, embedded_object, 'embedded_object', msg)
reference_class = _intended_value(
None, None, reference_class, 'reference_class', msg)
elif type_ == 'datetime':
if not isinstance(value, CIMDateTime):
value = CIMDateTime(value)
msg = 'Property %r specifies CIM data type %r' % (name, type_)
embedded_object = _intended_value(
None, None, embedded_object, 'embedded_object', msg)
reference_class = _intended_value(
None, None, reference_class, 'reference_class', msg)
elif type_ is None:
# Determine simple type from (non-Null) value
type_ = cimtype(value)
msg = 'Property %r has a simple typed value ' \
'with no CIM data type specified' % name
embedded_object = _intended_value(
None, None, embedded_object, 'embedded_object', msg)
reference_class = _intended_value(
None, None, reference_class, 'reference_class', msg)
else: # type is specified and value is not Null
# Make sure the value is of the corresponding Python type.
_type_obj = type_from_name(type_)
value = _type_obj(value) # pylint: disable=redefined-variable-type
msg = 'Property %r has a simple typed value ' \
'and specifies CIM data type %r' % (name, type_)
embedded_object = _intended_value(
None, None, embedded_object, 'embedded_object', msg)
reference_class = _intended_value(
None, None, reference_class, 'reference_class', msg)
# Initialize members
self.name = name
self.value = value
self.type = type_
self.class_origin = class_origin
self.array_size = array_size
self.propagated = propagated
self.is_array = is_array
self.reference_class = reference_class
self.qualifiers = NocaseDict(qualifiers)
self.embedded_object = embedded_object
[docs] def copy(self):
"""
Return a copy of the :class:`~pywbem.CIMProperty` object.
"""
return CIMProperty(self.name,
self.value,
type=self.type,
class_origin=self.class_origin,
array_size=self.array_size,
propagated=self.propagated,
is_array=self.is_array,
reference_class=self.reference_class,
qualifiers=self.qualifiers.copy())
[docs] def __str__(self):
"""
Return a short string representation of the
:class:`~pywbem.CIMProperty` object for human consumption.
"""
return '%s(name=%r, value=%r, type=%r, ' \
'reference_class=%r, embedded_object=%r, ' \
'is_array=%r, ...)' % \
(self.__class__.__name__, self.name, self.value, self.type,
self.reference_class, self.embedded_object,
self.is_array)
[docs] def __repr__(self):
"""
Return a string representation of the :class:`~pywbem.CIMProperty`
object that is suitable for debugging.
"""
return '%s(name=%r, value=%r, type=%r, ' \
'reference_class=%r, embedded_object=%r, ' \
'is_array=%r, array_size=%r, ' \
'class_origin=%r, propagated=%r, ' \
'qualifiers=%r)' % \
(self.__class__.__name__, self.name, self.value, self.type,
self.reference_class, self.embedded_object,
self.is_array, self.array_size,
self.class_origin, self.propagated,
self.qualifiers)
[docs] def tocimxml(self):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMProperty` object,
as an instance of an appropriate subclass of :term:`Element`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
"""
if self.is_array:
value = self.value
if value is not None:
if value:
if self.embedded_object is not None:
value = [v.tocimxml().toxml() for v in value]
value = cim_xml.VALUE_ARRAY(
[cim_xml.VALUE(
atomic_to_cim_xml(v)) for v in value])
return cim_xml.PROPERTY_ARRAY(
self.name,
self.type,
value,
self.array_size,
self.class_origin,
self.propagated,
qualifiers=[q.tocimxml() for q in self.qualifiers.values()],
embedded_object=self.embedded_object)
elif self.type == 'reference':
value_reference = None
if self.value is not None:
value_reference = cim_xml.VALUE_REFERENCE(self.value.tocimxml())
return cim_xml.PROPERTY_REFERENCE(
self.name,
value_reference,
reference_class=self.reference_class,
class_origin=self.class_origin,
propagated=self.propagated,
qualifiers=[q.tocimxml() for q in self.qualifiers.values()])
else:
value = self.value
if value is not None:
if self.embedded_object is not None:
value = value.tocimxml().toxml()
else:
value = atomic_to_cim_xml(value)
value = cim_xml.VALUE(value) # pylint: disable=redefined-variable-type
return cim_xml.PROPERTY(
self.name,
self.type,
value,
class_origin=self.class_origin,
propagated=self.propagated,
qualifiers=[q.tocimxml() for q in self.qualifiers.values()],
embedded_object=self.embedded_object)
[docs] def tocimxmlstr(self, indent=None):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMProperty` object, as a :term:`unicode string`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
Parameters:
indent (:term:`string` or :term:`integer`):
`None` indicates that a single-line version of the XML should be
returned, without any whitespace between the XML elements.
Other values indicate that a prettified, multi-line version of the
XML should be returned. A string value specifies the indentation
string to be used for each level of nested XML elements. An integer
value specifies an indentation string of so many blanks.
Returns:
The CIM-XML representation of the object, as a
:term:`unicode string`.
"""
return tocimxmlstr(self, indent)
def _scalar_value2mof(self, value_, indent):
"""
Private function to map provided value to string for MOF output.
Used by :meth:`tomof`.
Parameters:
value_ (:term:`CIM data type`): Value to be mapped to string for MOF
output.
indent (:term:`integer`): Number of spaces to indent the initial
line of the generated MOF.
"""
if self.type == 'string':
if self.embedded_object is not None:
# TODO ks 8/16 do special formatting for this so output
# sort of looks like mof, not just a string with lfs
val_ = value_.tomof()
else:
val_ = value_
_mof = mofstr(val_, indent=indent)
elif self.type == 'datetime':
_mof = '"%s"' % str(value_)
else:
_mof = str(value_)
return _mof
def _array_val2mof(self, indent, fold):
"""
Output array of values either on single line or one line per value.
Used by :meth:`tomof`.
Parameters:
indent (:term:`integer`): Number of spaces to indent the initiali
line of the generated MOF.
fold (bool): If True, format as instance MOF. Else, format as class
MOF.
"""
mof_ = ''
sep = ', ' if not fold else ',\n' + _indent_str(indent)
for i, val_ in enumerate(self.value):
if i > 0:
mof_ += sep
mof_ += self._scalar_value2mof(val_, indent)
return mof_
[docs] def tomof(self, is_instance=True, indent=0):
"""
Return a string representing the MOF definition of a single property.
Parameters:
is_instance (bool): If True, format as instance MOF. Else, format as
class MOF.
indent (:term:`integer`): Number of spaces to indent the initial
line of the generated MOF.
"""
if is_instance:
# is an instance; set name
mof = '%s%s = ' % (_indent_str(indent), self.name)
else: # is a class; set type, name, array info
if self.is_array:
if self.array_size is not None:
array_str = "[%s]" % self.array_size
else:
array_str = "[]"
else:
array_str = ''
mof = '\n'
if len(self.qualifiers) != 0:
mof += '%s\n' % ((_makequalifiers(self.qualifiers,
(indent + MOF_INDENT))))
mof += '%s%s %s%s' % (_indent_str(indent),
moftype(self.type,
self.reference_class),
self.name, array_str)
# set the value into the mof
if self.value is None:
if is_instance:
mof += 'NULL'
elif self.is_array:
mof += ' = {'
# output as single line if within width limits
arr_str = self._array_val2mof(indent, False)
# If too large, redo with on array element per line
if len(arr_str) > (MAX_MOF_LINE - indent):
arr_str = '\n' + _indent_str(indent+MOF_INDENT)
arr_str += self._array_val2mof((indent+MOF_INDENT), True)
mof += arr_str + '}'
else:
if not is_instance:
mof += ' = '
mof += self._scalar_value2mof(self.value, indent)
mof += ';\n'
return mof
def _cmp(self, other):
"""
Comparator function for two :class:`~pywbem.CIMProperty` objects.
The comparison is based on the `name`, `value`, `type`,
`reference_class`, `is_array`, `array_size`, `propagated`,
`class_origin`, and `qualifiers` instance attributes, in descending
precedence.
The `name` and `reference_class` attributes are compared
case-insensitively.
Raises `TypeError', if the `other` object is not a
:class:`~pywbem.CIMProperty` object.
"""
if self is other:
return 0
if not isinstance(other, CIMProperty):
raise TypeError("other must be CIMProperty, but is: %s" %\
type(other))
return (cmpname(self.name, other.name) or
cmpitem(self.value, other.value) or
cmpitem(self.type, other.type) or
cmpname(self.reference_class, other.reference_class) or
cmpitem(self.is_array, other.is_array) or
cmpitem(self.array_size, other.array_size) or
cmpitem(self.propagated, other.propagated) or
cmpitem(self.class_origin, other.class_origin) or
cmpitem(self.qualifiers, other.qualifiers))
[docs]class CIMMethod(_CIMComparisonMixin):
"""
A method declaration in a CIM class.
Attributes:
name (:term:`unicode string`):
Name of the method.
This variable will never be `None`.
return_type (:term:`unicode string`):
Name of the CIM data type of the method return type (e.g. ``"uint32"``).
This variable will never be `None`. Note that void return types of
methods are not supported in CIM.
class_origin (:term:`unicode string`):
The CIM class origin of the method (the name
of the most derived class that defines or overrides the method in
the class hierarchy of the class owning the method).
`None` means that class origin information is not available.
propagated (:class:`py:bool`):
If not `None`, indicates whether the method has been propagated
from a superclass to this class.
`None` means that propagation information is not available.
parameters (`NocaseDict`_):
Parameter declarations for the method declaration.
Each dictionary item specifies one parameter declaration, with:
* key (:term:`string`): Parameter name
* value (:class:`~pywbem.CIMParameter`): Parameter declaration
This variable will never be `None`.
qualifiers (`NocaseDict`_):
Qualifier values for the method declaration.
Each dictionary item specifies one qualifier value, with:
* key (:term:`string`): Qualifier name
* value (:class:`~pywbem.CIMQualifier`): Qualifier value
This variable will never be `None`.
"""
# pylint: disable=too-many-arguments
def __init__(self, name=None, return_type=None, parameters=None,
class_origin=None, propagated=False, qualifiers=None,
methodname=None):
"""
The constructor stores the input parameters as-is and does
not infer unspecified parameters from the others
(like :class:`~pywbem.CIMProperty` does).
Parameters:
name (:term:`string`):
Name of the method (just the method name, without class name
or parenthesis).
Must not be `None`.
Deprecated: This argument has been named `methodname` before
v0.9.0. Using `methodname` as a named argument still works,
but has been deprecated in v0.9.0.
return_type (:term:`string`):
Name of the CIM data type of the method return type
(e.g. ``"uint32"``).
Must not be `None`
parameters (:class:`py:dict` or `NocaseDict`_):
Parameter declarations for the method declaration.
For details about the dictionary items, see the corresponding
attribute.
If `None`, the method will have no parameters, and the dictionary
will be empty.
class_origin (:term:`string`):
The CIM class origin of the method (the name
of the most derived class that defines or overrides the method in
the class hierarchy of the class owning the method).
`None` means that class origin information is not available.
propagated (:class:`py:bool`):
If not `None`, indicates whether the method has been propagated
from a superclass to this class.
`None` means that propagation information is not available.
qualifiers (:class:`py:dict` or `NocaseDict`_):
Qualifier values for the method declaration.
For details about the dictionary items, see the corresponding
attribute.
`None` is interpreted as an empty dictionary.
"""
if methodname is not None:
warnings.warn("methodname is deprecated; use name instead",
DeprecationWarning)
if name is not None:
raise TypeError("name and methodname cannot be specified both")
name = methodname
if name is None:
raise TypeError("name must not be None")
self.name = _ensure_unicode(name)
self.return_type = _ensure_unicode(return_type)
self.parameters = NocaseDict(parameters)
self.class_origin = _ensure_unicode(class_origin)
# TODO: Propagated is bool; _ensure_unicode() is unnecessary
self.propagated = _ensure_unicode(propagated)
self.qualifiers = NocaseDict(qualifiers)
# Check valid `return_type`
if return_type is None:
raise ValueError('return_type must not be None')
def _cmp(self, other):
"""
Comparator function for two :class:`~pywbem.CIMMethod` objects.
The comparison is based on the `name`, `qualifiers`, `parameters`,
`return_type`, `class_origin` and `propagated` instance attributes,
in descending precedence.
The `name` attribute is compared case-insensitively.
"""
if self is other:
return 0
if not isinstance(other, CIMMethod):
raise TypeError("other must be CIMMethod, but is: %s" %\
type(other))
return (cmpname(self.name, other.name) or
cmpitem(self.qualifiers, other.qualifiers) or
cmpitem(self.parameters, other.parameters) or
cmpitem(self.return_type, other.return_type) or
cmpitem(self.class_origin, other.class_origin) or
cmpitem(self.propagated, other.propagated))
[docs] def __str__(self):
"""
Return a short string representation of the
:class:`~pywbem.CIMMethod` object for human consumption.
"""
return '%s(name=%r, return_type=%r, ...)' % \
(self.__class__.__name__, self.name, self.return_type)
[docs] def __repr__(self):
"""
Return a string representation of the :class:`~pywbem.CIMMethod`
object that is suitable for debugging.
"""
return '%s(name=%r, return_type=%r, ' \
'class_origin=%r, propagated=%r, ' \
'parameters=%r, qualifiers=%r)' % \
(self.__class__.__name__, self.name, self.return_type,
self.class_origin, self.propagated,
self.parameters, self.qualifiers)
[docs] def copy(self):
"""
Return a copy of the :class:`~pywbem.CIMMethod` object.
"""
result = CIMMethod(self.name,
return_type=self.return_type,
class_origin=self.class_origin,
propagated=self.propagated)
result.parameters = self.parameters.copy()
result.qualifiers = self.qualifiers.copy()
return result
[docs] def tocimxml(self):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMMethod` object,
as an instance of an appropriate subclass of :term:`Element`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
"""
return cim_xml.METHOD(
self.name,
parameters=[p.tocimxml() for p in self.parameters.values()],
return_type=self.return_type,
class_origin=self.class_origin,
propagated=self.propagated,
qualifiers=[q.tocimxml() for q in self.qualifiers.values()])
[docs] def tocimxmlstr(self, indent=None):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMMethod` object, as a :term:`unicode string`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
Parameters:
indent (:term:`string` or :term:`integer`):
`None` indicates that a single-line version of the XML should be
returned, without any whitespace between the XML elements.
Other values indicate that a prettified, multi-line version of the
XML should be returned. A string value specifies the indentation
string to be used for each level of nested XML elements. An integer
value specifies an indentation string of so many blanks.
Returns:
The CIM-XML representation of the object, as a
:term:`unicode string`.
"""
return tocimxmlstr(self, indent)
[docs] def tomof(self, indent):
"""
Return a :term:`unicode string` that is a MOF fragment with the
method definition represented by the :class:`~pywbem.CIMMethod`
object.
"""
ret_str = ''
if len(self.qualifiers) != 0:
ret_str += '%s\n' % (_makequalifiers(self.qualifiers,
(indent+MOF_INDENT)))
# TODO is None allowed for return type.
ret_str += _indent_str(indent)
if self.return_type is not None:
ret_str += '%s ' % moftype(self.return_type, None)
# TODO CIM-XML does not support methods returning reference
# types(the CIM architecture does).
if len(self.parameters.values()) != 0:
ret_str += '%s(\n' % (self.name)
ret_str += ',\n'.join(
[p.tomof(indent+MOF_INDENT) for p in self.parameters.values()])
ret_str += ');\n'
else:
ret_str += '%s();\n' % (self.name)
return ret_str
[docs]class CIMParameter(_CIMComparisonMixin):
"""
A CIM parameter for a method declaration.
Attributes:
name (:term:`unicode string`):
Name of the parameter.
This variable will never be `None`.
type (:term:`unicode string`):
Name of the CIM data type of the parameter (e.g. ``"uint8"``).
This variable will never be `None`.
reference_class (:term:`unicode string`):
The name of the referenced class, for reference parameters.
`None`, for non-reference parameters.
is_array (:class:`py:bool`):
A boolean indicating whether the parameter is an array (`True`) or a
scalar (`False`).
This variable will never be `None`.
array_size (:term:`integer`):
The size of the array parameter, for fixed-size arrays.
`None` means that the array parameter has variable size, or that it is
not an array.
qualifiers (`NocaseDict`_):
Qualifier values of the parameter declaration.
Each dictionary item specifies one qualifier value, with:
* key (:term:`string`): Qualifier name
* value (:class:`~pywbem.CIMQualifier`): Qualifier value
This variable will never be `None`.
value:
Deprecated: Because the object represents a parameter declaration,
this attribute does not make any sense. Accessing it will issue
a :term:`DeprecationWarning`.
"""
# pylint: disable=too-many-arguments
def __init__(self, name, type, reference_class=None, is_array=None,
array_size=None, qualifiers=None, value=None):
# pylint: disable=redefined-builtin
"""
The constructor stores the input parameters as-is and does
not infer unspecified parameters from the others
(like :class:`~pywbem.CIMProperty` does).
Parameters:
name (:term:`string`):
Name of the parameter.
Must not be `None`.
type (:term:`string`):
Name of the CIM data type of the parameter (e.g. ``"uint8"``).
Must not be `None`.
reference_class (:term:`string`):
Name of the referenced class, for reference parameters.
`None` is stored as-is.
is_array (:class:`py:bool`):
A boolean indicating whether the parameter is an array (`True`) or a
scalar (`False`).
`None` is stored as-is.
array_size (:term:`integer`):
The size of the array parameter, for fixed-size arrays.
`None` means that the array parameter has variable size.
qualifiers (:class:`py:dict` or `NocaseDict`_):
Qualifier values of the parameter declaration.
For details about the dictionary items, see the corresponding
attribute.
`None` is interpreted as an empty dictionary.
value:
Deprecated: Because the object represents a parameter declaration,
this parameter does not make any sense. Specifying a value other
than `None` will issue a :term:`DeprecationWarning`.
"""
type_ = type # Minimize usage of the builtin 'type'
self.name = _ensure_unicode(name)
self.type = _ensure_unicode(type_)
self.reference_class = _ensure_unicode(reference_class)
self.is_array = is_array
self.array_size = array_size
self.qualifiers = NocaseDict(qualifiers)
if value is not None:
warnings.warn(
"The value parameter of CIMParameter is deprecated",
DeprecationWarning)
self._value = value
@property
def value(self):
"""Return `value` attribute (Deprecated)."""
warnings.warn(
"The value attribute of CIMParameter is deprecated",
DeprecationWarning)
return self._value
@value.setter
def value(self, value):
"""Set `value` attribute (Deprecated)."""
warnings.warn(
"The value attribute of CIMParameter is deprecated",
DeprecationWarning)
self._value = value
def _cmp(self, other):
"""
Comparator function for two :class:`~pywbem.CIMParameter` objects.
The comparison is based on the `name`, `type`, `reference_class`,
`is_array`, `array_size`, `qualifiers` and `value` instance attributes,
in descending precedence.
The `name` attribute is compared case-insensitively.
Raises `TypeError', if the `other` object is not a
:class:`~pywbem.CIMParameter` object.
"""
if self is other:
return 0
if not isinstance(other, CIMParameter):
raise TypeError("other must be CIMParameter, but is: %s" %\
type(other))
return (cmpname(self.name, other.name) or
cmpitem(self.type, other.type) or
cmpname(self.reference_class, other.reference_class) or
cmpitem(self.is_array, other.is_array) or
cmpitem(self.array_size, other.array_size) or
cmpitem(self.qualifiers, other.qualifiers) or
cmpitem(self.value, other.value))
[docs] def __str__(self):
"""
Return a short string representation of the
:class:`~pywbem.CIMParameter` object for human consumption.
"""
return '%s(name=%r, type=%r, ' \
'reference_class=%r, ' \
'is_array=%r, ...)' % \
(self.__class__.__name__, self.name, self.type,
self.reference_class,
self.is_array)
[docs] def __repr__(self):
"""
Return a string representation of the :class:`~pywbem.CIMParameter`
object that is suitable for debugging.
"""
return '%s(name=%r, type=%r, ' \
'reference_class=%r, ' \
'is_array=%r, array_size=%r, ' \
'qualifiers=%r)' % \
(self.__class__.__name__, self.name, self.type,
self.reference_class,
self.is_array, self.array_size,
self.qualifiers)
[docs] def copy(self):
"""
Return a copy of the :class:`~pywbem.CIMParameter` object.
"""
result = CIMParameter(self.name,
self.type,
reference_class=self.reference_class,
is_array=self.is_array,
array_size=self.array_size,
value=self.value)
result.qualifiers = self.qualifiers.copy()
return result
[docs] def tocimxml(self):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMParameter` object,
as an instance of an appropriate subclass of :term:`Element`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
"""
if self.type == 'reference':
if self.is_array:
array_size = None
if self.array_size is not None:
array_size = str(self.array_size)
return cim_xml.PARAMETER_REFARRAY(
self.name,
self.reference_class,
array_size,
qualifiers=[q.tocimxml()
for q in self.qualifiers.values()])
else:
return cim_xml.PARAMETER_REFERENCE(
self.name,
self.reference_class,
qualifiers=[q.tocimxml()
for q in self.qualifiers.values()])
elif self.is_array:
array_size = None
if self.array_size is not None:
array_size = str(self.array_size)
return cim_xml.PARAMETER_ARRAY(
self.name,
self.type,
array_size,
qualifiers=[q.tocimxml() for q in self.qualifiers.values()])
else:
return cim_xml.PARAMETER(
self.name,
self.type,
qualifiers=[q.tocimxml() for q in self.qualifiers.values()])
[docs] def tocimxmlstr(self, indent=None):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMParameter` object, as a :term:`unicode string`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
Parameters:
indent (:term:`string` or :term:`integer`):
`None` indicates that a single-line version of the XML should be
returned, without any whitespace between the XML elements.
Other values indicate that a prettified, multi-line version of the
XML should be returned. A string value specifies the indentation
string to be used for each level of nested XML elements. An integer
value specifies an indentation string of so many blanks.
Returns:
The CIM-XML representation of the object, as a
:term:`unicode string`.
"""
return tocimxmlstr(self, indent)
[docs] def tomof(self, indent):
"""
Return a :term:`unicode string` that is a MOF fragment with the
parameter definition represented by the :class:`~pywbem.CIMParameter`
object.
Parameters:
indent (:term:`integer`): Number of spaces to indent each parameter
"""
if self.is_array:
if self.array_size is not None:
array_str = "[%s]" % self.array_size
else:
array_str = "[]"
else:
array_str = ''
rtn_str = ''
if len(self.qualifiers) != 0:
rtn_str = '%s\n' % (_makequalifiers(self.qualifiers,
indent + 2))
rtn_str += '%s%s %s%s' % (_indent_str(indent),
moftype(self.type, self.reference_class),
self.name, array_str)
return rtn_str
# pylint: disable=too-many-instance-attributes
[docs]class CIMQualifier(_CIMComparisonMixin):
"""
A CIM qualifier value.
A qualifier represents metadata on a class, method, property, etc., and
specifies information such as a documentation string or whether a property
is a key.
:class:`~pywbem.CIMQualifier` objects can be used to represent the qualifier
values that are specified on a CIM element (e.g. on a CIM class). In that
case, the :attr:`propagated` property is always `False`, and the effective
values of applicable but unspecified qualifiers need to be determined by
users, by considering the default value of the corresponding qualifier type,
the propagation and override flavors of the qualifier, and the qualifier
values that have been specified in the class ancestry of the CIM element in
question.
:class:`~pywbem.CIMQualifier` objects can also be used to represent the
effective values of all applicable qualifiers on a CIM element, including
those that have not been specified, e.g. in the MOF declaration of the CIM
element. In this case, the :class:`CIMQualifier` objects for qualifier
values that are specified in MOF represent the specified values, and their
:attr:`propagated` property is `False`. The :class:`CIMQualifier` objects
for qualifier values that are not specified in MOF represent the effective
values, and their :attr:`propagated` property is `True`.
Whether a set of :class:`CIMQualifier` objects on a CIM object represents
just the specified qualifiers or all applicable qualifiers needs to be known
from the context.
:class:`~pywbem.CIMQualifier` has properties that represent qualifier
flavors (:attr:`tosubclass`, :attr:`toinstance`, :attr:`overridable`, and
:attr:`translatable`). If any of these flavor properties is not `None`, the
qualifier value represented by the :class:`~pywbem.CIMQualifier` object
implicitly defines a qualifier type. Implicitly defined qualifier types have
been deprecated in :term:`DSP0004`. The implicitly defined qualifier type is
conceptual and is not materialized as a
:class:`~pywbem.CIMQualifierDeclaration` object.
Attributes:
name (:term:`unicode string`):
Name of the qualifier.
This variable will never be `None`.
value (:term:`CIM data type`):
Value of the qualifier.
`None` means that the value is Null.
For CIM data types string and char16, this attribute will be a
:term:`unicode string`, even when specified as a :term:`byte string`
in the constructor.
type (:term:`unicode string`):
Name of the CIM data type of the qualifier (e.g. ``"uint8"``).
This variable will never be `None`.
propagated (:class:`py:bool`):
Indicates whether the qualifier value has been propagated from a
superclass to this class.
`None` means that this information is not available.
tosubclass (:class:`py:bool`):
If not `None`, causes an implicit qualifier type to be defined for this
qualifier that has the specified flavor.
If `True`, specifies the ToSubclass flavor (the qualifier value
propagates to subclasses); if `False` specifies the Restricted flavor
(the qualifier values does not propagate to subclasses).
`None` means that this information is not available.
toinstance (:class:`py:bool`):
If not `None`, causes an implicit qualifier type to be defined for this
qualifier that has the specified flavor.
If `True` specifies the ToInstance flavor(the qualifier value
propagates to instances. If `False`, specifies that qualifier values
do not propagate to instances. There is no flavor corresponding to
`toinstance=False`.
`None` means that this information is not available.
Note that :term:`DSP0200` has deprecated the presence of qualifier
values on CIM instances.
overridable (:class:`py:bool`):
If not `None`, causes an implicit qualifier type to be defined for this
qualifier that has the specified flavor.
If `True`, specifies the EnableOverride flavor(the qualifier value is
overridable in subclasses); if `False` specifies the DisableOverride
flavor(the qualifier value is not overridable in subclasses).
`None` means that this information is not available.
translatable (:class:`py:bool`):
If not `None`, causes an implicit qualifier type to be defined for this
qualifier that has the specified flavor.
If `True`, specifies the Translatable flavor (the qualifier is
translatable); if `False` specifies that the qualfier is
not translatable. There is no flavor corresponding to
translatable=False.
`None` means that this information is not available.
"""
#pylint: disable=too-many-arguments
def __init__(self, name, value, type=None, propagated=None,
overridable=None, tosubclass=None, toinstance=None,
translatable=None):
# pylint: disable=redefined-builtin
"""
The constructor infers optional parameters that are not specified (for
example, it infers `type` from the Python type of `value` and other
information). If the specified parameters are inconsistent, an
exception is raised. If an optional parameter is needed for some reason,
an exception is raised.
Parameters:
name (:term:`string`):
Name of the qualifier.
Must not be `None`.
value (:term:`CIM data type`):
Value of the qualifier.
`None` means that the qualifier is Null.
type (:term:`string`):
Name of the CIM data type of the qualifier (e.g. ``"uint8"``).
`None` means that the parameter is unspecified, causing the
corresponding attribute to be inferred. An exception is
raised if it cannot be inferred.
propagated (:class:`py:bool`):
If not `None`, specifies whether the qualifier value has been
propagated from a superclass to this class.
`None` means that this information is not available.
overridable (:class:`py:bool`):
If not `None`, specifies whether the qualifier value is overridable
in subclasses.
`None` means that this information is not available.
tosubclass (:class:`py:bool`):
If not `None`, specifies whether the qualifier value propagates
to subclasses.
`None` means that this information is not available.
toinstance (:class:`py:bool`):
If not `None`, specifies whether the qualifier value propagates
to instances.
`None` means that this information is not available.
Note that :term:`DSP0200` has deprecated the presence of qualifier
values on CIM instances.
translatable (:class:`py:bool`):
If not `None`, specifies whether the qualifier is translatable.
`None` means that this information is not available.
"""
type_ = type # Minimize usage of the builtin 'type'
self.name = _ensure_unicode(name)
self.type = _ensure_unicode(type_)
# TODO: Propagated is bool; _ensure_unicode() is unnecessary
self.propagated = _ensure_unicode(propagated)
self.overridable = overridable
self.tosubclass = tosubclass
self.toinstance = toinstance
self.translatable = translatable
# Determine type of value if not specified
if type is None:
# Can't work out what is going on if type and value are
# both not set.
if value is None:
raise TypeError('Null qualifier "%s" must have a type' % name)
if isinstance(value, list):
# Determine type for list value
if len(value) == 0:
raise TypeError(
'Empty qualifier array "%s" must have a type' % name)
self.type = cimtype(value[0])
else:
# Determine type for regular value
self.type = cimtype(value)
# Don't let anyone set integer or float values. You must use
# a subclass from the cim_type module.
# Note: The CIM data types are derived from the built-in types,
# so we cannot use isinstance() for this test.
# pylint: disable=unidiomatic-typecheck
if builtin_type(value) in six.integer_types + (float,):
raise TypeError(
"Type of numeric value for a qualifier must be a "\
"CIM data type, but is %s" % builtin_type(value))
self.value = value
def _cmp(self, other):
"""
Comparator function for two :class:`~pywbem.CIMQualifier` objects.
The comparison is based on the `name`, `type`, `value`, `propagated`,
`overridable`, `tosubclass`, `toinstance`, `translatable` instance
attributes, in descending precedence.
The `name` attribute is compared case-insensitively.
"""
if self is other:
return 0
if not isinstance(other, CIMQualifier):
raise TypeError("other must be CIMQualifier, but is: %s" %\
type(other))
return (cmpname(self.name, other.name) or
cmpitem(self.type, other.type) or
cmpitem(self.value, other.value) or
cmpitem(self.propagated, other.propagated) or
cmpitem(self.overridable, other.overridable) or
cmpitem(self.tosubclass, other.tosubclass) or
cmpitem(self.toinstance, other.toinstance) or
cmpitem(self.translatable, other.translatable))
[docs] def __str__(self):
"""
Return a short string representation of the
:class:`~pywbem.CIMQualifier` object for human consumption.
"""
return "%s(name=%r, value=%r, type=%r, ...)" % \
(self.__class__.__name__, self.name, self.value, self.type)
[docs] def __repr__(self):
"""
Return a string representation of the :class:`~pywbem.CIMQualifier`
object that is suitable for debugging.
"""
return '%s(name=%r, value=%r, type=%r, ' \
'tosubclass=%r, overridable=%r, translatable=%r, ' \
'toinstance=%r, propagated=%r)' % \
(self.__class__.__name__, self.name, self.value, self.type,
self.tosubclass, self.overridable, self.translatable,
self.toinstance, self.propagated)
[docs] def copy(self):
"""
Return a copy of the :class:`~pywbem.CIMQualifier` object.
"""
return CIMQualifier(self.name,
self.value,
type=self.type,
propagated=self.propagated,
overridable=self.overridable,
tosubclass=self.tosubclass,
toinstance=self.toinstance,
translatable=self.translatable)
[docs] def tocimxml(self):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMQualifier` object,
as an instance of an appropriate subclass of :term:`Element`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
"""
value = None
if isinstance(self.value, list):
value = cim_xml.VALUE_ARRAY([cim_xml.VALUE(v) for v in self.value])
elif self.value is not None:
# pylint: disable=redefined-variable-type
# used as VALUE.ARRAY and the as VALUE
value = cim_xml.VALUE(self.value)
return cim_xml.QUALIFIER(self.name,
self.type,
value,
propagated=self.propagated,
overridable=self.overridable,
tosubclass=self.tosubclass,
toinstance=self.toinstance,
translatable=self.translatable)
[docs] def tocimxmlstr(self, indent=None):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMQualifier` object, as a :term:`unicode string`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
Parameters:
indent (:term:`string` or :term:`integer`):
`None` indicates that a single-line version of the XML should be
returned, without any whitespace between the XML elements.
Other values indicate that a prettified, multi-line version of the
XML should be returned. A string value specifies the indentation
string to be used for each level of nested XML elements. An integer
value specifies an indentation string of so many blanks.
Returns:
The CIM-XML representation of the object, as a
:term:`unicode string`.
"""
return tocimxmlstr(self, indent)
[docs] def tomof(self, indent=MOF_INDENT):
"""
Return a :term:`unicode string` that is a MOF fragment with the
qualifier value represented by the :class:`~pywbem.CIMQualifier`
object.
Parameters:
indent (:term:`integer`): Number of spaces to indent the second and
subsequent lines of a multi-line result. The first line is not
indented.
"""
def valstr(value):
"""
Return a string that is the MOF literal representing a value.
"""
if isinstance(value, six.string_types):
return mofstr(value, indent)
return str(value)
if isinstance(self.value, list):
line_pos = indent + len(self.name) + 4
values = ''
for i, val in enumerate(self.value):
if i != 0:
values += ','
nextval = valstr(val)
if (line_pos + len(nextval) + 3) > MAX_MOF_LINE:
sep = '\n' + _indent_str(indent)
line_pos = len(_indent_str(indent)) + 4
else:
sep = ' '
line_pos += (len(nextval) + 2)
values += sep + nextval
mof = '%s {%s%s' % (self.name, values, '}')
else:
val = valstr(self.value)
if len(val) + indent + 4 >= MAX_MOF_LINE:
mof = '%s (\n%s%s)' % (self.name, _indent_str(indent),
val)
else:
mof = '%s (%s)' % (self.name, val)
return mof
#pylint: disable=too-many-instance-attributes
[docs]class CIMQualifierDeclaration(_CIMComparisonMixin):
"""
A CIM qualifier type is the declaration of a qualifier and defines the
attributes of qualifier name, qualifier type, value, scopes, and flavors
for the qualifier.
The scope of a qualifer determines the kinds of schema elements on which
it can be specified.
Value specifies the default value for the qualifier.
Flavors specify certain characteristics of the qualifier such as its
value propagation from the ancestry of the qualified element and its
translatability.
Flavors attributes must be specifically set on construction of the
:class:`CIMQualifierDeclaration` or they will be set to `None`. This
differs from the DMTF specification :term:`DSP0004` where default values
are defined as follows:
- Has the EnableOverride flavor; ``overridable = True``
- Has the ToSubClass flavor; ``tosubclass = True``
- Does not have theTranslatable flavor; ``translatable = False``
- Does not have ToInstance flavor; ``toinstance = False.``
Not defined in :term:`DSP0004` and deprecated in the DMTF protocol
specification :TERM:`DSP0200`
Because `None` is allowed as a value for the flavors attributes in
constructing a CIMQualifierDeclaration, the user must insure that any
flavor which has the value `None` is set to its default value if required
for subsequent processing.
The pywbem MOF compiler supplies all of the flavor values so that
those which were not specified in the MOF are set to the DMTF defined
default values.
Attributes:
name (:term:`unicode string`):
Name of the qualifier.
This variable will never be `None`.
type (:term:`unicode string`):
Name of the CIM data type of the qualifier (e.g. ``"uint8"``).
This variable will never be `None`.
value (:term:`CIM data type`):
Default value of the qualifier.
`None` means that the value is Null.
For CIM data types string and char16, this attribute will be a
:term:`unicode string`, even when specified as a :term:`byte string`
in the constructor.
is_array (:class:`py:bool`):
A boolean indicating whether the qualifier is an array (`True`) or a
scalar (`False`).
This variable will never be `None`.
array_size (:term:`integer`):
The size of the array qualifier, for fixed-size arrays.
`None` means that the array qualifier has variable size, or that it is
not an array.
scopes (`NocaseDict`_):
Scopes of the qualifier.
Each dictionary item specifies one scope value, with:
* key (:term:`string`): Scope name, in upper case.
* value (:class:`py:bool`): Scope value, specifying whether the
qualifier has that scope (i.e. can be applied to a CIM element of
that kind).
Valid scope names are "CLASS", "ASSOCIATION", "REFERENCE",
"PROPERTY", "METHOD", "PARAMETER", "INDICATION", and "ANY".
`None` is interpreted as an empty dictionary.
This variable will never be `None`.
tosubclass (:class:`py:bool`):
If `True` specifies the ToSubclass flavor(the qualifier value
propagates to subclasses); if `False` specifies the Restricted
flavor(the qualifier value does not propagate to subclasses).
`None` means that this information is not available.
toinstance (:class:`py:bool`):
If `True`, specifies the ToInstance flavor. This flavor specifies
that the qualifier value propagates to instances. If `False`,
specifies that qualifier values do not propagate to instances.
There is no flavor corresponding to `toinstance=False`.
`None` means that this information is not available.
Note that :term:`DSP0200` has deprecated the presence of qualifier
values on CIM instances.
overridable (:class:`py:bool`):
If `True`, specifies the EnableOverride flavor(the qualifier value is
overridable in subclasses); if `False` specifies the DisableOverride
flavor(the qualifier value is not overridable in subclasses).
`None` means that this information is not available.
translatable (:class:`py:bool`):
If `True`, specifies the Translatable flavor. This flavor specifies
that the qualifier is translatable. If `False`, specifies that the
qualfier is not translatable. There is no flavor corresponding to
`translatable=False`.
`None` means that this information is not available.
"""
# TODO: 8/16 ks Consider removing toinstance completely from object since
# it is not a viable part of specs any more.
# pylint: disable=too-many-arguments
def __init__(self, name, type, value=None, is_array=False,
array_size=None, scopes=None,
overridable=None, tosubclass=None, toinstance=None,
translatable=None):
# pylint: disable=redefined-builtin
"""
Parameters:
name (:term:`string`):
Name of the qualifier.
Must not be `None`.
type (:term:`string`):
Name of the CIM data type of the qualifier (e.g. ``"uint8"``).
Must not be `None`.
value (:term:`CIM data type`):
Default value of the qualifier.
`None` means a default value of Null.
is_array (:class:`py:bool`):
A boolean indicating whether the qualifier is an array (`True`) or
a scalar (`False`).
Must not be `None`.
array_size (:term:`integer`):
The size of the array qualifier, for fixed-size arrays.
`None` means that the array qualifier has variable size.
scopes (:class:`py:dict` or `NocaseDict`_):
Scopes of the qualifier.
For details about the dictionary items, see the corresponding
attribute.
overridable (:class:`py:bool`):
If not `None`, defines the flavor that defines whether the
qualifier value is overridable in subclasses.
`None` means that this information is not available.
tosubclass (:class:`py:bool`):
If not `None`, specifies the flavor that defines whether the
qualifier value propagates to subclasses.
`None` means that this information is not available.
toinstance (:class:`py:bool`):
If not `None`, specifies the flavor that defines whether the
qualifier value propagates to instances.
`None` means that this information is not available.
Note that :term:`DSP0200` has deprecated the presence of qualifier
values on CIM instances and this flavor is not defined in
:term:`DSP0004`
translatable (:class:`py:bool`):
If not `None`, specifies the flavor that defines whether the
qualifier is translatable.
`None` means that this information is not available.
"""
type_ = type # Minimize usage of the builtin 'type'
self.name = _ensure_unicode(name)
self.type = _ensure_unicode(type_)
self.value = _ensure_unicode(value)
self.is_array = is_array
self.array_size = array_size
self.scopes = NocaseDict(scopes)
self.overridable = overridable
self.tosubclass = tosubclass
self.toinstance = toinstance
self.translatable = translatable
def _cmp(self, other):
"""
Comparator function for two :class:`~pywbem.CIMQualifierDeclaration`
objects.
The comparison is based on the `name`, `type`, `value`, `is_array`,
`array_size`, `scopes`, `overridable`, `tosubclass`, `toinstance`,
`translatable` instance attributes, in descending precedence.
The `name` attribute is compared case-insensitively.
"""
if self is other:
return 0
if not isinstance(other, CIMQualifierDeclaration):
raise TypeError("other must be CIMQualifierDeclaration, "\
"but is: %s" % type(other))
return (cmpname(self.name, other.name) or
cmpitem(self.type, other.type) or
cmpitem(self.value, other.value) or
cmpitem(self.is_array, other.is_array) or
cmpitem(self.array_size, other.array_size) or
cmpitem(self.scopes, other.scopes) or
cmpitem(self.overridable, other.overridable) or
cmpitem(self.tosubclass, other.tosubclass) or
cmpitem(self.toinstance, other.toinstance) or
cmpitem(self.translatable, other.translatable))
[docs] def __str__(self):
"""
Return a short string representation of the
:class:`~pywbem.CIMQualifierDeclaration` object for human
consumption.
"""
return '%s(name=%r, value=%r, type=%r, ' \
'is_array=%r, ...)' % \
(self.__class__.__name__, self.name, self.value, self.type,
self.is_array)
[docs] def __repr__(self):
"""
Return a string representation of the
:class:`~pywbem.CIMQualifierDeclaration` object that is suitable for
debugging.
"""
return '%s(name=%r, value=%r, type=%r, ' \
'is_array=%r, array_size=%r, ' \
'scopes=%r, tosubclass=%r, overridable=%r, ' \
'translatable=%r, toinstance=%r)' % \
(self.__class__.__name__, self.name, self.value, self.type,
self.is_array, self.array_size,
self.scopes, self.tosubclass, self.overridable,
self.translatable, self.toinstance)
[docs] def copy(self):
"""
Return a copy the :class:`~pywbem.CIMQualifierDeclaration` object.
"""
return CIMQualifierDeclaration(self.name,
self.type,
value=self.value,
is_array=self.is_array,
array_size=self.array_size,
scopes=self.scopes,
overridable=self.overridable,
tosubclass=self.tosubclass,
toinstance=self.toinstance,
translatable=self.translatable)
[docs] def tocimxml(self):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMQualifierDeclaration` object,
as an instance of an appropriate subclass of :term:`Element`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
"""
return cim_xml.QUALIFIER_DECLARATION(self.name,
self.type,
self.value,
is_array=self.is_array,
array_size=self.array_size,
qualifier_scopes=self.scopes,
overridable=self.overridable,
tosubclass=self.tosubclass,
toinstance=self.toinstance,
translatable=self.translatable)
[docs] def tocimxmlstr(self, indent=None):
"""
Return the CIM-XML representation of the
:class:`~pywbem.CIMQualifierDeclaration` object, as a
:term:`unicode string`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
Parameters:
indent (:term:`string` or :term:`integer`):
`None` indicates that a single-line version of the XML should be
returned, without any whitespace between the XML elements.
Other values indicate that a prettified, multi-line version of the
XML should be returned. A string value specifies the indentation
string to be used for each level of nested XML elements. An integer
value specifies an indentation string of so many blanks.
Returns:
The CIM-XML representation of the object, as a
:term:`unicode string`.
"""
return tocimxmlstr(self, indent)
[docs] def tomof(self):
"""
Return a :term:`unicode string` that is a MOF fragment with the
qualifier type represented by the
:class:`~pywbem.CIMQualifierDeclaration` object.
"""
mof = 'Qualifier %s : %s' % (self.name, self.type)
if self.is_array:
mof += '['
if self.array_size is not None:
mof += str(self.array_size)
mof += ']'
if self.value is not None:
if isinstance(self.value, list):
mof += ' = {'
mof += ', '.join([atomic_to_cim_xml(
tocimobj(self.type, x)) for x in self.value])
mof += '}'
else:
mof += ' = %s' % atomic_to_cim_xml(
tocimobj(self.type, self.value))
mof += ',\n%sScope(' % _indent_str(MOF_INDENT)
mof += ', '.join([x.lower() for x, y in self.scopes.items() if y]) + ')'
# toinstance flavor not included here because not part of DSP0004
if not self.overridable and not self.tosubclass \
and not self.translatable:
mof += ';'
return mof
mof += ',\n%sFlavor(' % _indent_str(MOF_INDENT)
mof += self.overridable and 'EnableOverride' or 'DisableOverride'
mof += ', '
mof += self.tosubclass and 'ToSubclass' or 'Restricted'
if self.translatable:
mof += ', Translatable'
mof += ');'
return mof
[docs]def tocimxml(value):
"""
Return the CIM-XML representation of the CIM object or CIM data type,
as an :term:`Element` object.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
Parameters:
value (:term:`CIM object` or :term:`CIM data type`):
The value.
Returns:
The CIM-XML representation of the specified value,
as an instance of an appropriate subclass of :term:`Element`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
"""
# Python cim_obj object
if hasattr(value, 'tocimxml'):
return value.tocimxml()
# CIMType or builtin type
if isinstance(value, (CIMType, int, six.text_type)):
return cim_xml.VALUE(six.text_type(value))
if isinstance(value, six.binary_type):
return cim_xml.VALUE(_ensure_unicode(value))
# TODO: Verify whether this is a bug to have this test after the one for
# int. Bool is a subtype of int, so bool probably matches in the test
# above.
if isinstance(value, bool):
if value:
return cim_xml.VALUE('TRUE')
else:
return cim_xml.VALUE('FALSE')
# Iterable of values
try:
return cim_xml.VALUE_ARRAY([tocimxml(v) for v in value])
except TypeError:
raise ValueError("Can't convert %s (%s) to CIM XML" % \
(value, builtin_type(value)))
[docs]def tocimxmlstr(value, indent=None):
"""
Return the CIM-XML representation of the CIM object or CIM data type,
as a :term:`unicode string`.
The returned CIM-XML representation is consistent with :term:`DSP0201`.
Parameters:
value (:term:`CIM object` or :term:`CIM data type`):
The CIM object or CIM data type to be converted to CIM-XML.
indent (:term:`string` or :term:`integer`):
`None` indicates that a single-line version of the XML should be
returned, without any whitespace between the XML elements.
Other values indicate that a prettified, multi-line version of the XML
should be returned. A string value specifies the indentation string to
be used for each level of nested XML elements. An integer value
specifies an indentation string of so many blanks.
Returns:
The CIM-XML representation of the value, as a :term:`unicode string`.
"""
xml_elem = tocimxml(value)
if indent is None:
xml_str = xml_elem.toxml()
else:
if isinstance(indent, six.string_types):
pass # use indent, as specified
elif isinstance(indent, six.integer_types):
indent = ' '*indent
else:
raise TypeError("Type of indent must be string or integer, " \
"but is: %s" % type(indent))
xml_str = xml_elem.toprettyxml(indent=indent)
# xml_str is a unicode string if required based upon its content.
return _ensure_unicode(xml_str)
#pylint: disable=too-many-locals,too-many-return-statements,too-many-branches
[docs]def tocimobj(type_, value):
"""
Return a CIM object representing the specified value and
type.
Parameters:
`type_` (:term:`string`):
The CIM data type name for the CIM object. See :ref:`CIM data types`
for valid type names.
If `value` is a list, `type_` must specify the CIM data type name of
an item in the list.
value (:term:`CIM data type` and some others, see description):
The value to be represented as a CIM object.
In addition to the Python types listed in :ref:`CIM data types`, the
following Python types are supported for this parameter:
* `None`: The returned object will be `None`.
* If `type_` specifies one of the CIM integer data types:
- :term:`integer`
- :term:`string`. The string must represent a decimal number
* If `type_` specifies the CIM boolean data type:
- :term:`string`. The string must be ``'true'`` or ``'false'`` in any
lexical case
* If `type_` specifies the CIM datetime data type:
- All input types of :class:`~pywbem.CIMDateTime`
* If `type_` specifies the CIM reference data type:
- :term:`string`. The string must be an untyped WBEM URI representing
an instance path (see :term:`DSP0207`)
Returns:
A :term:`CIM data type` object, representing the specified value and
type.
"""
if value is None or type_ is None:
return None
if type_ != 'string' and isinstance(value, six.string_types) and not value:
return None
# Lists of values
if isinstance(value, list):
return [tocimobj(type_, x) for x in value]
# Boolean type
if type_ == 'boolean':
if isinstance(value, bool):
return value
elif isinstance(value, six.string_types):
if value.lower() == 'true':
return True
elif value.lower() == 'false':
return False
raise ValueError('Invalid boolean value: "%s"' % value)
# String type
if type_ == 'string':
return _ensure_unicode(value)
# Integer types
if type_ == 'uint8':
return Uint8(value)
if type_ == 'sint8':
return Sint8(value)
if type_ == 'uint16':
return Uint16(value)
if type_ == 'sint16':
return Sint16(value)
if type_ == 'uint32':
return Uint32(value)
if type_ == 'sint32':
return Sint32(value)
if type_ == 'uint64':
return Uint64(value)
if type_ == 'sint64':
return Sint64(value)
# Real types
if type_ == 'real32':
return Real32(value)
if type_ == 'real64':
return Real64(value)
# Char16
if type_ == 'char16':
return _ensure_unicode(value)
# Datetime
if type_ == 'datetime':
return CIMDateTime(value)
# REF
def partition(str_arg, seq):
"""
partition(str_arg, sep) -> (head, sep, tail)
Searches for the separator sep in str_arg, and returns the,
part before it the separator itself, and the part after it.
If the separator is not found, returns str_arg and two empty
strings.
"""
try:
return str_arg.partition(seq)
except AttributeError:
try:
idx = str_arg.index(seq)
except ValueError:
return (str_arg, '', '')
return (str_arg[:idx], seq, str_arg[idx+len(seq):])
if type_ == 'reference': # pylint: disable=too-many-nested-blocks
# pylint: disable=too-many-return-statements,too-many-branches
# TODO doesn't handle double-quoting, as in refs to refs. Example:
# r'ex_composedof.composer="ex_sampleClass.label1=9921,' +
# 'label2=\"SampleLabel\"",component="ex_sampleClass.label1=0121,' +
# 'label2=\"Component\""')
if isinstance(value, (CIMInstanceName, CIMClassName)):
return value
elif isinstance(value, six.string_types):
nm_space = host = None
head, sep, tail = partition(value, '//')
if sep and head.find('"') == -1:
# we have a namespace type
head, sep, tail = partition(tail, '/')
host = head
else:
tail = head
head, sep, tail = partition(tail, ':')
if sep:
nm_space = head
else:
tail = head
head, sep, tail = partition(tail, '.')
if not sep:
return CIMClassName(head, host=host, namespace=nm_space)
classname = head
key_bindings = {}
while tail:
head, sep, tail = partition(tail, ',')
if head.count('"') == 1: # quoted string contains comma
tmp, sep, tail = partition(tail, '"')
head = '%s,%s' % (head, tmp)
tail = partition(tail, ',')[2]
head = head.strip()
key, sep, val = partition(head, '=')
if sep:
cl_name, s, k = partition(key, '.')
if s:
if cl_name != classname:
raise ValueError('Invalid object path: "%s"' % \
value)
key = k
val = val.strip()
if val[0] == '"' and val[-1] == '"':
val = val.strip('"')
else:
if val.lower() in ('true', 'false'):
val = val.lower() == 'true'
elif val.isdigit():
val = int(val) # pylint: disable=R0204
else:
try:
val = float(val)
except ValueError:
try:
val = CIMDateTime(val)
except ValueError:
raise ValueError('Invalid key binding: %s'\
% val)
key_bindings[key] = val
return CIMInstanceName(classname, host=host, namespace=nm_space,
keybindings=key_bindings)
else:
raise ValueError('Invalid reference value: "%s"' % value)
raise ValueError('Invalid CIM data type name: "%s"' % type_)
def byname(nlist):
"""
Convert a list of named objects into a map indexed by name
"""
return dict([(x.name, x) for x in nlist])