c2c85f483cfb1fcfdf4a54021e90c11a1112345e
[mdis.git] / src / mdis / dispatcher.py
1 import collections as _collections
2 import inspect as _inspect
3 import operator as _operator
4
5 from . import core as _core
6
7
8 class DispatcherMeta(type):
9 def __new__(metacls, name, bases, ns):
10 conflicts = _collections.defaultdict(list)
11 for (key, value) in tuple(ns.items()):
12 if not isinstance(value, _core.CallHook):
13 continue
14 hook = value
15 for typeid in hook:
16 conflicts[typeid].append(key)
17 ns[key] = hook
18
19 for (typeid, keys) in conflicts.items():
20 if len(keys) > 1:
21 raise ValueError(f"dispatch conflict: {keys!r}")
22
23 return super().__new__(metacls, name, bases, ns)
24
25 def __init__(cls, name, bases, ns):
26 hooks = {}
27 for hook in map(_operator.itemgetter(1), _inspect.getmembers(cls,
28 predicate=lambda member: isinstance(member, _core.CallHook))):
29 for typeid in hook:
30 hooks[typeid] = hook
31 cls.__hooks = hooks
32 return super().__init__(name, bases, ns)
33
34 def dispatch(cls, typeid=object):
35 return cls.__hooks.get(typeid)
36
37
38 class Dispatcher(metaclass=DispatcherMeta):
39 def __call__(self, instance):
40 for typeid in instance.__class__.__mro__:
41 hook = self.__class__.dispatch(typeid=typeid)
42 if hook is not None:
43 break
44 if hook is None:
45 hook = self.__class__.dispatch()
46 return hook(dispatcher=self, instance=instance)
47
48 @_core.hook(object)
49 def dispatch_object(self, instance):
50 raise NotImplementedError()