import types
[docs]class PropertyDictionary():
"""
Implements a dict with additional control on which objects can be added to which keys and whether these are optional
or mandatory.
"""
def __init__(self, mandatory_properties={}, optional_properties={}):
"""
The values for each of the required dicts can either be:
- A type (such as str, int, etc...)
- A function returning a boolean and accepting a single value as argument
- A list of admissible values
Parameters
----------
mandatory_properties: A dict[str,function] mapping the keys of mandatory properties with one of three options
for values, as described above
optional_properties: A dict[str,function] mapping the keys of optional properties with one of three options
for values, as described above
"""
self.__mandatory_properties = mandatory_properties
self.__optional_properties = optional_properties
self.__properties = {}
[docs] def add_new_properties(self, mandatory_properties, optional_properties):
"""
Adds new properties to the dictionary and/or updates existing ones, if present.
Parameters
----------
mandatory_properties: A dict[str, function]
optional_properties: A dict[str, function]
-------
"""
self.__mandatory_properties.update(mandatory_properties)
self.__optional_properties.update(optional_properties)
[docs] def get_mandatory_properties(self):
"""
Returns a dictionary containing the mapping between mandatory keys and function/type/list controlling values.
-------
"""
return self.__mandatory_properties
[docs] def get_optional_properties(self):
"""
Returns a dictionary containing the mapping between optional keys and function/type/list controlling values.
-------
"""
return self.__optional_properties
def __getitem__(self, item):
"""
Overloaded indexing to allow the square brace syntax for accessing values through keys. If the key was not
registered, an exception will be raised. If the key was registered but no value exists, None will be returned.
Parameters
----------
item: Key for the value to be accessed
Returns an object.
-------
"""
if item not in self.__mandatory_properties.keys() and item not in self.__optional_properties.keys():
raise Exception(str(item) + " has not been registered as a mandatory or optional property.")
elif item not in self.__properties.keys():
return None
else:
return self.__properties[item]
def __setitem__(self, key, value):
"""
Sets the value for the supplied key
Parameters
----------
key - str representing the key, preferrably contained in the mandatory or optional properties.
value - an object compliant with the functions set for the key.
-------
"""
if key in self.__mandatory_properties.keys() or key in self.__optional_properties.keys():
expected_type = self.__mandatory_properties[key] if key in self.__mandatory_properties.keys() else \
self.__optional_properties[key]
if self.__check_key_value_pair(expected_type, value):
self.__properties[key] = value
else:
raise Exception(str(key) + " does not accept the supplied `value` as a valid argument.")
[docs] def has_required_properties(self):
"""
Returns a boolean value if the mandatory properties all have an associated value.
-------
"""
return set(self.__properties.keys()) & set(self.__mandatory_properties.keys()) == set(
self.__mandatory_properties.keys())
def __check_key_value_pair(self, expected_type, value):
"""
Checks whether a value is compliant with a function/type or is contained in a list of admissible values.
Parameters
----------
expected_type: A type to be compared with value, a function returning a boolean and accepting value as argument
or a list of values where value should be contained.
value
Returns a boolean indicating whether the value can be added, assuming the conditions set by `expected_type`
-------
"""
if type(expected_type) is type:
is_ok = expected_type == type(value)
if not is_ok:
raise TypeError(
"\'value\' has type " + str(type(value)) + " but \'key\' requires type " + str(expected_type))
elif type(expected_type) == types.FunctionType:
is_ok = expected_type(value)
if not is_ok:
raise AssertionError(
"Property checking function " + expected_type.__name__ + " does not allow the specified \'value\'")
else:
is_ok = value in expected_type
if not is_ok:
raise AssertionError("\'value\' is not contained in " + str(expected_type))
return is_ok
[docs] def add_if_not_none(self, key, value):
if value is not None:
self[key] = value
def __repr__(self):
"""
Returns a string representation of the internal dictionary where all keys/values are stored.
-------
"""
return '\n'.join([str(k) + " = " + str(v) for k, v in self.__properties.items()])