-
Notifications
You must be signed in to change notification settings - Fork 7
/
lazydict.py
109 lines (92 loc) · 3.48 KB
/
lazydict.py
1
2
3
4
5
6
7
8
9
10
11
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
from collections import MutableMapping
from threading import RLock
from inspect import getargspec
from copy import copy
def get_version():
VERSION = ( # SEMANTIC
1, # major
0, # minor
0, # patch
'beta.2', # pre-release
None # build metadata
)
version = "%i.%i.%i" % (VERSION[0], VERSION[1], VERSION[2])
if VERSION[3]:
version += "-%s" % VERSION[3]
if VERSION[4]:
version += "+%s" % VERSION[4]
return version
CONSTANT = frozenset(['evaluating', 'evaluated', 'error'])
class LazyDictionaryError(Exception):
pass
class CircularReferenceError(LazyDictionaryError):
pass
class ConstantRedefinitionError(LazyDictionaryError):
pass
class LazyDictionary(MutableMapping):
def __init__(self, values={}):
self.lock = RLock()
self.values = copy(values)
self.states = {}
for key in self.values:
self.states[key] = 'defined'
def __len__(self):
return len(self.values)
def __iter__(self):
return iter(self.values)
def __getitem__(self, key):
with self.lock:
if key in self.states:
if self.states[key] == 'evaluating':
raise CircularReferenceError('value of "%s" depends on itself' % key)
elif self.states[key] == 'error':
raise self.values[key]
elif self.states[key] == 'defined':
value = self.values[key]
if callable(value):
args, _, _, _ = getargspec(value)
if len(args) == 0:
self.states[key] = 'evaluating'
try:
self.values[key] = value()
except Exception as ex:
self.values[key] = ex
self.states[key] = 'error'
raise ex
elif len(args) == 1:
self.states[key] = 'evaluating'
try:
self.values[key] = value(self)
except Exception as ex:
self.values[key] = ex
self.states[key] = 'error'
raise ex
self.states[key] = 'evaluated'
return self.values[key]
def __contains__(self, key):
return key in self.values
def __setitem__(self, key, value):
with self.lock:
if self.states.get(key) in CONSTANT:
raise ConstantRedefinitionError('"%s" is immutable' % key)
self.values[key] = value
self.states[key] = 'defined'
def __delitem__(self, key):
with self.lock:
if self.states.get(key) in CONSTANT:
raise ConstantRedefinitionError('"%s" is immutable' % key)
del self.values[key]
del self.states[key]
def __str__(self):
return str(self.values)
def __repr__(self):
return "LazyDictionary({0})".format(repr(self.values))
class MutableLazyDictionary(LazyDictionary):
def __setitem__(self, key, value):
with self.lock:
self.values[key] = value
self.states[key] = 'defined'
def __delitem__(self, key):
with self.lock:
del self.values[key]
del self.states[key]