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