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

__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. **Features:** * Two objects with exact values are considered the same * Objects are immutable (raise ImmutableInstanceError) :Example: .. code-block:: python >>> val = Value(name='Primary', price=10.35, currency='USD') >>> val.price 10.35 >>> val['currency'] 'USD' :param **kwargs: Any key=value pairs. """ __checksum = None __checksum_tpl = '_{}__checksum' 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)) cks_tpl = self.__checksum_tpl.format(self.__class__.__name__) object.__setattr__(self, cks_tpl, hashlib.sha224(ck_sum).hexdigest()) def __repr__(self): cks_tpl = self.__checksum_tpl.format(self.__class__.__name__) values = ", ".join('{}={}'.format(key, repr(val)) for key, val in self.to_dict().items() if key != cks_tpl) return '{cls}({val})'.format(cls=self.__class__.__name__, val=values) def __str__(self): return '{} Value'.format(self.__class__.__name__) def __eq__(self, other: "Value") -> bool: return self.__checksum == other.__checksum def __ne__(self, other: "Value") -> bool: return self.__checksum != other.__checksum def __hash__(self) -> hash: return hash(self.__checksum) def __contains__(self, item: Hashable): return item in self.__dict__ def __getitem__(self, item): 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) -> dict: """Dump values to dict. :return: dict with values :rtype: dict """ dct = OrderedDict(sorted(deepcopy(self.__dict__).items())) del dct[self.__checksum_tpl.format(self.__class__.__name__)] return dct
[docs] def to_json(self) -> str: """Dump values to JSON. :return: JSON string. :rtype: str """ return json.dumps(self.to_dict())
[docs] def to_bytes(self) -> bytes: """Convert values to bytes. :return: byte string :rtype: bytes """ return str_to_bytes(self.to_json())