multi_domain.py 2.03 KB
Newer Older
Martin Reinecke's avatar
step 1    
Martin Reinecke committed
1
2
3
4
5
6
7
8
import collections
from ..domain_tuple import DomainTuple

__all = ["MultiDomain"]


class frozendict(collections.Mapping):
    """
9
10
11
    An immutable wrapper around dictionaries that implements the complete
    :py:class:`collections.Mapping` interface. It can be used as a drop-in
    replacement for dictionaries where immutability is desired.
Martin Reinecke's avatar
step 1    
Martin Reinecke committed
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
    """

    dict_cls = dict

    def __init__(self, *args, **kwargs):
        self._dict = self.dict_cls(*args, **kwargs)
        self._hash = None

    def __getitem__(self, key):
        return self._dict[key]

    def __contains__(self, key):
        return key in self._dict

    def copy(self, **add_or_replace):
        return self.__class__(self, **add_or_replace)

    def __iter__(self):
        return iter(self._dict)

    def __len__(self):
        return len(self._dict)

    def __repr__(self):
        return '<%s %r>' % (self.__class__.__name__, self._dict)

    def __hash__(self):
        if self._hash is None:
            h = 0
            for key, value in self._dict.items():
                h ^= hash((key, value))
            self._hash = h
        return self._hash


class MultiDomain(frozendict):
    _domainCache = {}

Martin Reinecke's avatar
Martin Reinecke committed
50
    def __init__(self, domain, _callingfrommake=False):
Martin Reinecke's avatar
step 1    
Martin Reinecke committed
51
        if not _callingfrommake:
52
            raise NotImplementedError('To create a MultiDomain call `MultiDomain.make()`.')
Martin Reinecke's avatar
step 1    
Martin Reinecke committed
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
        super(MultiDomain, self).__init__(domain)

    @staticmethod
    def make(domain):
        if isinstance(domain, MultiDomain):
            return domain
        if not isinstance(domain, dict):
            raise TypeError("dict expected")
        tmp = {}
        for key, value in domain.items():
            if not isinstance(key, str):
                raise TypeError("keys must be strings")
            tmp[key] = DomainTuple.make(value)
        domain = frozendict(tmp)
        obj = MultiDomain._domainCache.get(domain)
        if obj is not None:
            return obj
        obj = MultiDomain(domain, _callingfrommake=True)
        MultiDomain._domainCache[domain] = obj
        return obj