-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvisit.py
51 lines (42 loc) · 1.41 KB
/
visit.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
import inspect
__all__ = ['on', 'when']
def on(param_name):
def f(fn):
dispatcher = Dispatcher(param_name, fn)
return dispatcher
return f
def when(param_type):
# f - actual decorator
# fn - decorated method, i.e. visit
# ff - fn gets replaced by ff in the effect of applying @when decorator
# dispatcher is an function object
def f(fn):
frame = inspect.currentframe().f_back
dispatcher = frame.f_locals[fn.__name__]
if not isinstance(dispatcher, Dispatcher):
dispatcher = dispatcher.dispatcher
dispatcher.add_target(param_type, fn)
def ff(*args, **kw):
return dispatcher(*args, **kw)
ff.dispatcher = dispatcher
return ff
return f
class Dispatcher(object):
def __init__(self, param_name, fn):
frame = inspect.currentframe().f_back.f_back # these 2 lines
top_level = frame.f_locals == frame.f_globals # seem redundant
self.param_index = inspect.getargspec(fn).args.index(param_name)
self.param_name = param_name
self.targets = {}
def __call__(self, *args, **kw):
typ = args[self.param_index].__class__
d = self.targets.get(typ)
if d is not None:
return d(*args, **kw)
else:
issub = issubclass
t = self.targets
ks = t.keys()
return [ t[k](*args, **kw) for k in ks if issub(typ, k) ]
def add_target(self, typ, target):
self.targets[typ] = target