"""
Bindings data classes
"""
__author__ = 'VMware, Inc.'
__copyright__ = 'Copyright (c) 2015 VMware, Inc. All rights reserved.'
import decimal
import six
import sys
try:
import simplejson as json
except ImportError:
import json
from vmware.vapi.bindings.common import raise_core_exception
from vmware.vapi.data.serializers import cleanjson
# TODO: Split this into static and dynamic structures.
[docs]class VapiStruct(object):
"""
Representation of IDL Structure in python language bindings
"""
_validator_list = None
def __init__(self, mappings=None, struct_value=None):
"""
Initialize VapiStruct
:type mappings: :class:`dict` or :class:`None`
:param mappings: A mapping for all field names whose IDL name does
not match PEP8 standard name
:type struct_value: :class:`vmware.vapi.data.value.StructValue`
:param struct_value: StructValue to be used for VapiStruct or :class:`None`
"""
self._mappings = mappings
# fields will either be in native form or in unknown
# fields
self._unexpected_fields = None
self._struct_value = struct_value
[docs] def get_field(self, attr):
"""
Returns the struct field value
:type attr: :class:`str`
:param attr: Field name as defined in IDL
:rtype: :class:`object`
:return: Field value
"""
if self._mappings and attr in self._mappings:
return getattr(self, self._mappings[attr])
else:
return getattr(self, attr)
@classmethod
[docs] def validate_struct_value(cls, struct_value):
"""
Validate if the given struct value satisfies all
the constraints of this VapiStruct.
:type struct_value: :class:`vmware.vapi.data.value.StructValue`
:param struct_value: StructValue to be validated
:type validators: :class:`list` of :class:`vmware.vapi.data.validator.Validator`
:param validators: List of validators
:raise :class:`vmware.vapi.exception.CoreException` if a constraint is
not satisfied
"""
if cls._validator_list:
for validator in cls._validator_list:
msg_list = validator.validate(struct_value, None)
raise_core_exception(msg_list)
[docs] def validate_constraints(self):
"""
Validate if the current VapiStruct instance satisfies all the
constraints of this VapiStruct type.
:raise :class:`vmware.vapi.exception.CoreException` if a constraint is
not satisfied
"""
struct_value = self.get_struct_value()
self.validate_struct_value(struct_value)
@classmethod
[docs] def get_binding_type(cls):
"""
Returns the corresponding BindingType for the VapiStruct class
:rtype: :class:`vmware.vapi.bindings.type.BindingType`
:return: BindingType for this VapiStruct
"""
return getattr(cls, '_binding_type', None)
@classmethod
def _set_binding_type(cls, binding_type):
"""
Set the underlying BindingType for this VapiStruct.
:type binding_type: :class:`vmware.vapi.bindings.type.BindingType`
:param binding_type: BindingType for this VapiStruct
"""
cls._binding_type = binding_type
[docs] def get_struct_value(self):
"""
Returns the corresponding StructValue for the VapiStruct class
:rtype: :class:`vmware.vapi.data.value.StructValue`
:return: StructValue for this VapiStruct
"""
# For dynamic structures
if self._struct_value:
return self._struct_value
else:
# For static structures
from vmware.vapi.bindings.converter import TypeConverter
struct_value = TypeConverter.convert_to_vapi(self, self._binding_type)
unexpected_fields = self._unexpected_fields or {}
for k, v in six.iteritems(unexpected_fields):
struct_value.set_field(k, v)
return struct_value
def _set_unexpected_fields(self, unexpected_fields=None):
"""
Set the underlying StructValue for this VapiStruct. This is
an internal method and should only be used by vAPI runtime.
:type unexpected_fields: :class:`dict` of :class:`str` and
:class:`vmware.vapi.data.value.DataValue` or :class:`None`
:param struct_value: StructValue for this VapiStruct
"""
self._unexpected_fields = unexpected_fields
[docs] def convert_to(self, cls):
"""
Convert the underlying StructValue to an instance of the provided class
if possible. Conversion will be possible if the StructValue contains
all the fields expected by the provided class and the type of the value
in each fields matches the type of the field expected by the provided
class.
:type cls: :class:`vmware.vapi.data.value.StructValue`
:param cls: The type to convert to
:rtype: :class:'vmware.vapi.bindings.struct.VapiStruct'
:return: The converted value
"""
from vmware.vapi.bindings.converter import TypeConverter
return TypeConverter.convert_to_python(self.get_struct_value(),
cls.get_binding_type())
[docs] def to_json(self):
"""
Convert the object into a json string.
:rtype: :class:`str`
:return: JSON string representation of this object
"""
struct_value = self.get_struct_value()
return cleanjson.DataValueConverter.convert_to_json(struct_value)
[docs] def to_dict(self):
"""
Convert the object into a python dictionary. Even the nested types
are converted to dictionaries.
:rtype: :class:`dict`
:return: Dictionary representation of this object
"""
# TODO: Implement native converter from DataValue -> Dictionary
# to improve performance if it is used heavily
return json.loads(self.to_json(), parse_float=decimal.Decimal)
def _get_attrs(self):
"""
Returns the attributes of the vAPI structure object
:rtype: :class:`list` of :class:`str`
:return: List of attributes of this object
"""
# Using getmembers in inspect to return all the attributes
# of this object. And later filter those to get only the
# public data attributes
return [k for k in six.iterkeys(vars(self))
if not k.startswith('_')]
def __eq__(self, other):
if other is None:
return False
for attr in self._get_attrs():
if getattr(self, attr) != getattr(other, attr):
return False
return True
def __ne__(self, other):
return not (self == other)
def __repr__(self):
class_name = self.__class__.__name__
attrs = self._get_attrs()
result = ', '.join(
['%s=%s' % (attr, repr(getattr(self, attr)))
for attr in attrs])
return '%s(%s)' % (class_name, result)
def __str__(self):
attrs = self._get_attrs()
result = ', '.join(
['%s : %s' % (attr, str(getattr(self, attr)))
for attr in attrs])
return '{%s}' % result
def __hash__(self):
return str(self).__hash__()
[docs]class PrettyPrinter(object):
"""
Helper class to pretty print Python native values (with special support
for VapiStruct objects).
"""
def __init__(self, stream=sys.stdout, indent=2):
"""
Initialize PrettyPrinter
:type stream: :class:`object`
:param stream: A stream object that implements File protocol's
write operation
:type indent: :class:`int`
:param indent: Indentation to be used for new lines
"""
self._stream = stream
self._indent = indent
[docs] def pprint(self, value, level=0):
"""
Print a Python native value
:type value: :class:`vmware.vapi.bindings.struct.VapiStruct`
:param value: VapiStruct to be pretty printed
:type level: :class:`int`
:param level: Indentation level
"""
self._process_value(value, level)
def _print_level(self, value, level, newline=True):
"""
Print data at a given identation level
:type value: :class:`str`
:param value: String to be printed
:type level: :class:`int`
:param level: Indentation level
:type newline: :class:`bool`
:param newline: If true, prints a new line after the data. If false,
only prints the data
"""
if level:
self._stream.write(' ' * level + value)
else:
self._stream.write(value)
if newline:
self._stream.write('\n')
def _process_value(self, value, level=0):
"""
Process a value
:type value: :class:`object`
:param value: Value to be processed
:type level: :class:`int`
:param level: Indentation level
"""
if isinstance(value, VapiStruct):
self._pprint_struct(value, level + self._indent)
elif isinstance(value, dict):
self._pprint_dict(value, level + self._indent)
elif isinstance(value, list):
self._pprint_list(value, level + self._indent)
elif isinstance(value, six.string_types):
self._print_level("'%s'," % value, 0)
elif isinstance(value, six.integer_types):
self._print_level('%s,' % value, 0)
elif value is None:
self._print_level('None,', 0)
else:
self._print_level('%s,' % value, level)
def _pprint_struct(self, value, level=0):
"""
Pretty print a struct
:type value: :class:`vmware.vapi.bindings.struct.VapiStruct`
:param value: Value to be processed
:type level: :class:`int`
:param level: Indentation level
"""
class_name = value.__class__.__name__
self._print_level(class_name + '(', 0)
for k in sorted(value._get_attrs()): # pylint: disable=W0212
v = getattr(value, k)
self._print_level('%s=' % k, level, False)
self._process_value(v, level)
self._print_level('),', level - self._indent)
def _pprint_dict(self, value, level=0):
"""
Pretty print a dictionary
:type value: :class:`dict`
:param value: Value to be processed
:type level: :class:`int`
:param level: Indentation level
"""
if not value:
self._print_level('{},', 0)
return
self._print_level('{', 0)
for k in sorted(value.keys()):
self._print_level("'%s':" % k, level, False)
self._process_value(value[k], level)
self._print_level('},', level - self._indent)
def _pprint_list(self, value, level=0):
"""
Pretty print a list
:type value: :class:`list`
:param value: Value to be processed
:type level: :class:`int`
:param level: Indentation level
"""
if not value:
self._print_level('[],', 0)
return
self._print_level('[', 0)
for v in value:
self._print_level('', level, False)
self._process_value(v, level)
self._print_level('],', level - self._indent)