# Copyright 2018 IBM Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
""":mod:`microprobe.property` module
"""
# Futures
from __future__ import absolute_import
# Built-in modules
import os
# Third party modules
import six
# Own modules
from microprobe.exceptions import MicroprobeArchitectureDefinitionError
from microprobe.utils.logger import get_logger
from microprobe.utils.misc import RejectingDict
from microprobe.utils.yaml import read_yaml
# Constants
SCHEMA = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "schemas", "property.yaml"
)
LOG = get_logger(__name__)
__all__ = [
"import_properties", "list_property_files", "Property", "PropertyHolder"
]
# Functions
[docs]def import_properties(filename, objects):
"""
:param filename:
:param objects:
"""
LOG.info("Start importing object properties")
dirname = filename[::-1].replace('lmay.', 'sporp_', 1)[::-1]
if not os.path.isdir(dirname):
return
for filename in os.listdir(dirname):
if filename.startswith(".") or filename.endswith(".cache"):
continue
if not filename.endswith(".yaml"):
continue
property_definitions = read_yaml(
os.path.join(
dirname, filename
), SCHEMA
)
for property_def in property_definitions:
property_objs = RejectingDict()
property_name = property_def["Name"]
property_description = property_def.get(
"Description", "No description"
)
property_override = property_def.get("Override", False)
property_default_value = property_def.get("Default", "__unset__")
property_values = property_def.get("Values", {})
property_class = Property
LOG.debug(
"Importing property '%s - %s'", property_name,
property_description
)
if "Value" in property_def:
# Single value
property_value = property_def["Value"]
default_property = property_class(
property_name, property_description, property_value
)
for obj in objects.values():
obj.register_property(
default_property,
force=property_override
)
LOG.debug("Single value property")
continue
if (
property_default_value != "__unset__" and
property_default_value != "NO_DEFAULT"
):
default_property = property_class(
property_name,
property_description,
property_default_value,
default=True
)
LOG.debug("Default value: %s", property_default_value)
else:
default_property = None
LOG.debug("Default value: No default value set")
for key, obj in objects.items():
if key not in property_values:
if property_default_value == "NO_DEFAULT":
continue
if default_property is None:
raise MicroprobeArchitectureDefinitionError(
"Wrong property '%s' definition in file '%s'. "
"Value for '%s' is not provided and a default "
"value is not defined" % (
property_name, filename, key
)
)
obj.register_property(
default_property,
force=property_override
)
else:
property_value = property_values[key]
del property_values[key]
property_value_key = property_value
if isinstance(property_value, list):
property_value_key = str(property_value)
if property_value_key in property_objs:
obj.register_property(
property_objs[property_value_key],
force=property_override
)
else:
new_property = property_class(
property_name,
property_description,
property_value,
default=False
)
obj.register_property(
new_property, force=property_override
)
property_objs[property_value_key] = new_property
for key, value in property_values.items():
LOG.warning(
"'%s' not found. Property '%s' not set to '%s'", key,
property_name, value
)
LOG.info("Property '%s' imported", property_name)
LOG.info("End importing object properties")
[docs]def list_property_files(filename):
"""
:param filename:
"""
dirname = filename[::-1].replace('lmay', 'sporp', 1)[::-1]
if not os.path.isdir(dirname):
return []
return [
os.path.join(dirname, cfile)
for cfile in os.listdir(dirname)
if not cfile.startswith(".") and not cfile.endswith(".cache")
]
# Classes
[docs]class Property(object):
"""Class to represent an object property"""
[docs] def __init__(self, name, description, value, default=False):
"""
:param name:
:param description:
:param value:
:param default:
"""
self._name = name
self._description = description
self._value = value
self._default = default
@property
def name(self):
""" """
return self._name
@property
def description(self):
""" """
return self._description
@property
def value(self):
""" """
return self._value
@property
def default(self):
""" """
return self._default
def __str__(self):
""" """
if self.default:
return "%s (%s): %s (default)" % (
self.name, self.description, self.value
)
else:
return "%s (%s): %s" % (self.name, self.description, self.value)
def __repr__(self):
""" """
return "Property('%s', '%s', %s)" % (
self.name, self.description, self.value
)
[docs]class PropertyHolder(object):
"""Class to represent an object containing properties"""
def _init_properties(self):
""" """
if self.__dict__.get("_properties", None) is None:
# pylint: disable=attribute-defined-outside-init
self._properties = RejectingDict()
# pylint: enable=attribute-defined-outside-init
[docs] def register_property(self, prop, force=False):
"""
:param prop:
"""
self._init_properties()
LOG.debug("Registering '%s' in '%s' (Force: '%s')", prop, self, force)
if prop.name not in self._properties:
self._properties[prop.name] = prop
elif not prop.default:
if force or self._properties[prop.name].default:
del self._properties[prop.name]
self._properties[prop.name] = prop
else:
self._properties[prop.name] = prop
[docs] def unregister_property(self, prop):
"""
:param prop:
"""
self._init_properties()
del self._properties[prop.name]
[docs] def list_properties(self, tabs=0):
""" """
self._init_properties()
rstr = ""
if len(self._properties) == 0:
rstr += " No properties \n"
return rstr
maxname = max(
[
len(str(value.name)) for value in
six.itervalues(self._properties)
]
) + 2
maxvalue = max(
[
len(str(value.value)) for value in
six.itervalues(self._properties)
]
)
maxdesc = max(
[
len(str(value.description))
for value in six.itervalues(self._properties)
]
)
strfmt = "\t" * tabs
strfmt += "%%-%ds:\t%%-%ds\t(%%-%ds)\n" % (maxname, maxvalue, maxdesc)
for key in sorted(six.iterkeys(self._properties)):
value = self._properties[key]
rstr += strfmt % (value.name, value.value, value.description)
return rstr
@property
def properties(self):
"""
"""
return self._properties.copy()
def __getattr__(self, name):
"""
:param name:
"""
self._init_properties()
if name in self._properties:
# Memoize properties
# They can not change over the execution
value = self._properties[name].value
setattr(self, name, value)
return value
raise AttributeError(
"'%s' object has no attribute '%s'" %
(self.__class__.__name__, name)
)