Source code for vo.value

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import hashlib
import json
from collections import Hashable, OrderedDict
from copy import deepcopy
from typing import Any

__author__ = 'Paweł Zadrożny'
__copyright__ = 'Copyright (c) 2017, Pawelzny'


[docs]class ImmutableInstanceError(Exception): """Raised on any attempt to modify values in Value object. :param message: Optional message. :type message: str """ message = 'Modification of Value instance is forbidden.' def __init__(self, message: str = None): super().__init__(message or self.message)
def _str_to_bytes(string: str) -> bytes: return bytes(repr(string), 'utf-8')
[docs]class Value: """Basic implementation of DDD immutable Value Object. Implementation provides: * Immutability of once created object * Comparison of two objects * Conversion to other data types :param: Any ``key=value`` pairs. """ _checksum = None def __init__(self, **kwargs): ck_sum = _str_to_bytes('checksum:') for attr, value in sorted(kwargs.items()): object.__setattr__(self, attr, value) ck_sum += _str_to_bytes(str(attr) + str(value)) object.__setattr__(self, '_checksum', hashlib.sha224(ck_sum).hexdigest()) def __repr__(self): values = ", ".join('{}={}'.format(key, repr(val)) for key, val in self.to_dict().items() if key != '_checksum') return '{cls}({val})'.format(cls=self.__class__.__name__, val=values) def __str__(self) -> str: return self.to_json() def __eq__(self, other: "Value") -> bool: try: return hash(self) == hash(other) except TypeError: # catch unhashable type return False def __ne__(self, other: "Value") -> bool: return not self.__eq__(other) def __hash__(self) -> hash: return hash(self._checksum) def __contains__(self, item: Hashable) -> bool: return item in self.__dict__ def __getitem__(self, item) -> Any: return self.__dict__[item] def __setattr__(self, name, value): raise ImmutableInstanceError def __setitem__(self, key, value): raise ImmutableInstanceError def __delattr__(self, item): raise ImmutableInstanceError def __delitem__(self, key): raise ImmutableInstanceError
[docs] def to_dict(self) -> OrderedDict: """Dump all values to OrderedDict. All keys are sorted in ascending direction. Dump does not include hash and checksum. :return: dict with values :rtype: collections.OrderedDict """ def dd(dct: dict) -> dict: dct = deepcopy(dct) try: del dct['_checksum'] except KeyError: pass for key, val in dct.items(): # If any of value is instance of Value dump it recursively if isinstance(val, Value): dct[key] = dd(val.to_dict()) return dct return OrderedDict(sorted(dd(self.__dict__).items()))
[docs] def to_json(self) -> str: """Dump values to JSON. Feed for JSON comes from ``.to_dict()`` method. :return: JSON string. :rtype: str """ return json.dumps(self.to_dict())
[docs] def to_bytes(self) -> bytes: """Dump values to bytes. Feed for byte string comes from ``.to_json()`` method. :return: byte string :rtype: bytes """ return _str_to_bytes(self.to_json())