+import inspect as _inspect
+import operator as _operator
+
from . import core as _core
class DispatcherMeta(type):
- @classmethod
- def dispatch(metacls, typeid=object):
- module = typeid.__module__
- qualname = typeid.__qualname__
- if module == "builtins":
- return qualname
- return f"{module}.{qualname}".replace(".", "_")
-
def __new__(metacls, name, bases, ns):
hooks = {}
for (key, value) in tuple(ns.items()):
- if isinstance(value, _core.CallHook):
- hook = ns.pop(key)
- for typeid in hook.typeids:
- site = metacls.dispatch(typeid)
- hooks[typeid] = (hook, site)
-
- for (typeid, (hook, site)) in tuple(hooks.items()):
+ if not isinstance(value, _core.CallHook):
+ continue
+ hook = ns.pop(key)
+ for typeid in hook.typeids:
+ if typeid in hooks:
+ raise ValueError(f"conflicting hook: {typeid!r}")
+ hooks[typeid] = hook
+ site = hook.call.__name__
ns[site] = hook
return super().__new__(metacls, name, bases, ns)
+ def __init__(cls, name, bases, ns):
+ hooks = {}
+ for hook in map(_operator.itemgetter(1), _inspect.getmembers(cls,
+ predicate=lambda member: isinstance(member, _core.CallHook))):
+ for typeid in hook.typeids:
+ hooks[typeid] = hook
+ cls.__hooks = hooks
+ return super().__init__(name, bases, ns)
+
+ def dispatch(cls, typeid=object):
+ return cls.__hooks.get(typeid)
+
class Dispatcher(metaclass=DispatcherMeta):
def __call__(self, instance):
- nil = object()
for typeid in instance.__class__.__mro__:
- site = self.__class__.dispatch(typeid)
- call = getattr(self, site, nil)
- if call is not nil:
- return call(self, instance)
- return getattr(self, self.__class__.dispatch())
+ hook = self.__class__.dispatch(typeid=typeid)
+ if hook is not None:
+ break
+ if hook is None:
+ hook = self.__class__.dispatch()
+ return hook(dispatcher=self, instance=instance)