blob: 924fe5a9b8b92c17424cd1f7bbf7a9dc55ccb124 (
plain) (
blame)
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
|
from __future__ import absolute_import
import sys
MACRO_FAIL = "<\x01\x01MACRO_HAS_NO_VALUE\x01\01>"
def macro_val(thunk, fallback=MACRO_FAIL):
try:
return thunk()
except AttributeError:
return fallback
try:
from collections.abc import MutableMapping # pylint: disable=no-name-in-module
except ImportError:
from collections import MutableMapping # pylint: disable=deprecated-class
class MacroDict(MutableMapping):
"""Mapping that returns a fallback value when thunks error
Macros can be used in scenarios where several attributes aren't
initialized yet. To avoid errors in these cases we have to delay the
evaluation of these attributes using ``lambda``s. This
``MutableMapping`` evaluates these thunks before returning them
replacing them with a fallback value if necessary.
For convenience it also catches ``TypeError`` so you can store
non-callable values without thunking.
>>> m = MacroDict()
>>> o = type("", (object,), {})()
>>> o.existing_attribute = "I exist!"
>>> m['a'] = "plain value"
>>> m['b'] = lambda: o.non_existent_attribute
>>> m['c'] = lambda: o.existing_attribute
>>> m['a']
'plain value'
>>> m['b']
'<\\x01\\x01MACRO_HAS_NO_VALUE\\x01\\x01>'
>>> m['c']
'I exist!'
"""
def __init__(self, *args, **kwargs):
super(MacroDict, self).__init__()
self.__dict__.update(*args, **kwargs)
def __setitem__(self, key, value):
try:
real_val = value()
if real_val is None:
real_val = MACRO_FAIL
except AttributeError:
real_val = MACRO_FAIL
except TypeError:
real_val = value
self.__dict__[key] = real_val
def __getitem__(self, key):
return self.__dict__[key]
def __delitem__(self, key):
del self.__dict__[key]
def __iter__(self):
return iter(self.__dict__)
def __len__(self):
return len(self.__dict__)
def __str__(self):
return str(self.__dict__)
if __name__ == '__main__':
import doctest
sys.exit(doctest.testmod()[0])
|