Commit | Line | Data |
---|---|---|
e88ff533 FT |
1 | import threading, weakref |
2 | ||
3 | __all__ = ["environment", "root", "get", "binding", "var"] | |
4 | ||
5 | class stack(object): | |
6 | __slots__ = ["env", "prev"] | |
7 | def __init__(self, env, prev): | |
8 | self.env = env | |
9 | self.prev = prev | |
10 | ||
11 | class environment(object): | |
12 | __slots__ = ["parent", "map"] | |
9bc70dab | 13 | def __init__(self, parent=None): |
8d285e43 | 14 | self.parent = parent |
e88ff533 FT |
15 | self.map = weakref.WeakKeyDictionary() |
16 | ||
17 | def get(self, var): | |
18 | if var in self.map: | |
19 | return self.map[var] | |
20 | if self.parent is None: | |
21 | return None | |
22 | return self.parent.get(var) | |
23 | ||
24 | def set(self, var, val): | |
25 | self.map[var] = val | |
26 | ||
27 | def __enter__(self): | |
28 | cur = context.env | |
29 | context.prev = stack(cur, context.prev) | |
30 | context.env = self | |
31 | return None | |
32 | ||
33 | def __exit__(self, *excinfo): | |
34 | prev = context.prev | |
35 | if prev is None: | |
36 | raise Exception("Unbalanced __enter__/__exit__") | |
37 | context.env = prev.env | |
38 | context.prev = prev.prev | |
39 | return False | |
40 | ||
41 | root = environment() | |
42 | ||
43 | class context(threading.local): | |
44 | env = root | |
45 | prev = None | |
46 | context = context() | |
47 | ||
48 | def get(): | |
49 | return context.env | |
50 | ||
51 | class binding(object): | |
52 | __slots__ = ["bindings"] | |
53 | def __init__(self, bindings): | |
54 | if isinstance(bindings, dict): | |
f55d762c | 55 | bindings = list(bindings.items()) |
e88ff533 FT |
56 | self.bindings = bindings |
57 | ||
58 | def __enter__(self): | |
59 | cur = context.env | |
60 | new = environment(cur) | |
61 | for var, val in self.bindings: | |
62 | new.map[var] = val | |
63 | context.prev = stack(cur, context.prev) | |
64 | context.env = new | |
65 | return None | |
66 | ||
67 | def __exit__(self, *excinfo): | |
68 | prev = context.prev | |
69 | if prev is None: | |
70 | raise Exception("Unbalanced __enter__/__exit__") | |
71 | context.env = prev.env | |
72 | context.prev = prev.prev | |
73 | return False | |
74 | ||
75 | class var(object): | |
76 | __slots__ = ["__weakref__"] | |
9bc70dab | 77 | def __init__(self, default=None): |
e88ff533 FT |
78 | if default is not None: |
79 | root.map[self] = default | |
80 | ||
81 | @property | |
82 | def val(self): | |
83 | return context.env.get(self) | |
84 | ||
85 | def binding(self, val): | |
86 | return binding([(self, val)]) | |
f55d762c FT |
87 | |
88 | def boundvars(bindings, dynamic=[]): | |
89 | if isinstance(bindings, dict): | |
90 | bindings = list(bindings.items()) | |
91 | if isinstance(dynamic, dict): | |
92 | dynamic = list(dynamic.items()) | |
93 | def dec(fun): | |
94 | def wrapper(*args, **kwargs): | |
95 | calc = bindings | |
96 | if dynamic: | |
97 | calc = list(calc) | |
98 | for var, val in dynamic: | |
99 | calc.append((var, val())) | |
100 | with binding(bindings): | |
101 | return fun(*args, **kwargs) | |
16ce1238 | 102 | wrapper.__wrapped__ = fun |
f55d762c FT |
103 | return wrapper |
104 | return dec |