"""Base and utility Classes for Mambu Objects.
.. autosummary::
:nosignatures:
:toctree: _autosummary
"""
import copy
[docs]class GenericClass: # pragma: no coverage
"""Generic class for init of MambuMapObj"""
[docs] def __init__(self, *args, **kwargs):
pass
[docs]class MambuMapObj:
"""An object with dictionary-like behaviour for key-value data"""
[docs] def __init__(self, cf_class=GenericClass, **kwargs):
self._attrs = {}
self._cf_class = cf_class
if hasattr(self, "_default_tzattrs"):
self._tzattrs = copy.deepcopy(self._default_tzattrs)
else:
self._tzattrs = {}
if "tzattrs" in kwargs:
self._tzattrs.update(copy.deepcopy(kwargs.pop("tzattrs")))
if kwargs:
for key, val in kwargs.items():
self._attrs[key] = val
[docs] def __getattribute__(self, name):
"""Object-like get attribute
When accessing an attribute, tries to find it in the _attrs
dictionary, so now MambuMapObj may act not only as a dict-like
structure, but as a full object-like too (this is the getter
side).
"""
try:
# first, try to read 'name' as if it's a property of the object
# if it doesn't exists as property, AttributeError raises
return object.__getattribute__(self, name)
except AttributeError:
# try to read the _attrs property
_attrs = object.__getattribute__(self, "_attrs")
if isinstance(_attrs, list) or name not in _attrs:
# magic won't happen when not a dict-like MambuMapObj or
# when _attrs has not the 'name' key (this last one means
# that if 'name' is not a property of the object too,
# AttributeError will raise by default)
return object.__getattribute__(self, name)
# all else, read the property from the _attrs dict, but with a . syntax
# if a cf_class, just return its value
if _attrs[name].__class__.__name__ == self._cf_class.__name__:
return _attrs[name]["value"]
return _attrs[name]
[docs] def __setattr__(self, name, value):
"""Object-like set attribute
When setting an attribute, tries to set it in the _attrs
dictionary, so now MambuMapObj acts not only as a dict-like
structure, but as a full object-like too (this is the setter
side).
"""
# if name beginning with _, assign it as a property of the object
if name[0] == "_":
object.__setattr__(self, name, value)
else:
try:
# _attrs needs to exist to make the magic happen!
# ... if not, AttributeError raises
_attrs = object.__getattribute__(self, "_attrs")
if isinstance(_attrs, list):
# when not treating with a dict-like MambuMapObj...
raise AttributeError
try:
# see if 'name' is currently a property of the object
object.__getattribute__(self, name)
except AttributeError:
# if not, then assign it as a new key in the dict
if (
name in _attrs
and _attrs[name].__class__.__name__ == self._cf_class.__name__
):
path = _attrs[name]["path"]
typecf = _attrs[name]["type"]
_attrs[name] = self._cf_class(value, path, typecf)
else:
_attrs[name] = value
else: # pragma: no cover
raise AttributeError
except AttributeError:
# all else assign it as a property of the object
object.__setattr__(self, name, value)
[docs] def __getitem__(self, key):
"""Dict-like key query"""
# if a cf_class, just return its value
if self._attrs[key].__class__.__name__ == self._cf_class.__name__:
return self._attrs[key]["value"]
return self._attrs[key]
[docs] def __setitem__(self, key, value):
"""Dict-like set"""
# if no _attrs attribute, should be automatically created?
if (
key in self._attrs
and self._attrs[key].__class__.__name__ == self._cf_class.__name__
):
path = self._attrs[key]["path"]
typecf = self._attrs[key]["type"]
self._attrs[key] = self._cf_class(value, path, typecf)
else:
self._attrs[key] = value
[docs] def __delitem__(self, key):
"""Dict-like del key"""
del self._attrs[key]
[docs] def __str__(self):
"""Mambu object str gives a string representation of the _attrs attribute."""
try:
return self.__class__.__name__ + " - " + str(self._attrs)
except AttributeError:
return repr(self)
[docs] def __repr__(self):
"""Mambu object repr tells the class name and the usual 'id' for it.
If an iterable, it instead gives its length.
"""
try:
return self.__class__.__name__ + " - id: %s" % self._attrs["id"]
except KeyError:
return self.__class__.__name__ + " - " + str(self._attrs)
except AttributeError:
return (
self.__class__.__name__
+ " - id: '%s' (not synced with Mambu)" % self.entid
)
except TypeError:
return self.__class__.__name__ + " - len: %s" % len(self._attrs)
[docs] def __eq__(self, other):
"""Very basic way to compare to Mambu objects.
Only looking at their EncodedKey field (its primary key on the
Mambu DB).
.. todo:: a lot of improvements may be done here.
"""
if isinstance(other, MambuMapObj):
try:
if "encodedKey" not in other._attrs or "encodedKey" not in self._attrs:
return NotImplemented
except AttributeError:
return NotImplemented
return other["encodedKey"] == self["encodedKey"]
[docs] def __hash__(self):
"""Hash of the object"""
try:
return hash(self.encodedKey)
except AttributeError:
try:
return hash(self.__class__.__name__ + self.id)
except Exception:
return hash(self.__repr__())
[docs] def __len__(self):
"""Length of the _attrs attribute.
If dict-like (not iterable), it's the number of keys holded on the _attrs dictionary.
If list-like (iterable), it's the number of elements of the _attrs list.
"""
return len(self._attrs)
[docs] def __contains__(self, item):
"""Dict-like and List-like behaviour"""
return item in self._attrs
[docs] def get(self, key, default=None):
"""Dict-like behaviour"""
if isinstance(self._attrs, dict):
return self._attrs.get(key, default)
raise NotImplementedError # if _attrs is not a dict
[docs] def keys(self):
"""Dict-like behaviour"""
try:
return self._attrs.keys()
except AttributeError:
raise NotImplementedError
[docs] def items(self):
"""Dict-like behaviour"""
try:
return self._attrs.items()
except AttributeError:
raise NotImplementedError
[docs] def values(self):
"""Dict-like behaviour"""
try:
return self._attrs.values()
except AttributeError:
raise NotImplementedError
[docs] def has_key(self, key):
"""Dict-like behaviour"""
try:
if isinstance(self._attrs, dict):
return key in self._attrs
raise AttributeError # if _attrs is not a dict
except AttributeError: # if _attrs doesnt exist
raise NotImplementedError