14 def __init__(self
, *typeids
):
15 for typeid
in typeids
:
16 if not callable(typeid
):
17 raise ValueError(typeid
)
18 self
.__typeids
= typeids
19 return super().__init
__()
22 yield from self
.__typeids
26 for typeid
in self
.__typeids
:
27 name
= typeid
.__qualname
__
28 module
= typeid
.__module
__
29 if module
not in ("builtins",):
30 name
= f
"{module}.{name}"
32 return f
"<{', '.join(names)}>"
34 def __call__(self
, call
):
35 class ConcreteHook(Hook
):
36 def __call__(self
, dispatcher
, instance
, *args
, **kwargs
):
37 return call(self
=dispatcher
, instance
=instance
,
40 return ConcreteHook(*tuple(self
))
43 class DispatcherMeta(type):
46 def __new__(metacls
, name
, bases
, ns
):
48 ishook
= lambda member
: isinstance(member
, Hook
)
50 for basecls
in reversed(bases
):
51 members
= inspect
.getmembers(basecls
, predicate
=ishook
)
52 for (_
, hook
) in members
:
53 hooks
.update(dict.fromkeys(hook
, hook
))
55 conflicts
= collections
.defaultdict(list)
56 for (key
, value
) in tuple(ns
.items()):
62 conflicts
[typeid
].append(key
)
65 for (typeid
, keys
) in conflicts
.items():
67 raise ValueError(f
"dispatch conflict: {keys!r}")
69 ns
["__hooks__"] = types
.MappingProxyType(hooks
)
71 return super().__new
__(metacls
, name
, bases
, ns
)
73 @functools.lru_cache(maxsize
=None)
74 def dispatch(cls
, typeid
=object):
75 hook
= cls
.__hooks
__.get(typeid
)
78 for (checker
, hook
) in cls
.__hooks
__.items():
79 if not isinstance(checker
, type) and checker(typeid
):
84 class Dispatcher(metaclass
=DispatcherMeta
):
85 def __call__(self
, instance
, *args
, **kwargs
):
86 for typeid
in instance
.__class
__.__mro
__:
87 hook
= self
.__class
__.dispatch(typeid
=typeid
)
91 hook
= self
.__class
__.dispatch()
92 return hook(dispatcher
=self
, instance
=instance
, *args
, **kwargs
)
95 def dispatch_object(self
, instance
, *args
, **kwargs
):
96 raise NotImplementedError()