def test_skip_nodes(self): from generic.registry import Registry from generic.registry import SimpleAxis registry = Registry(('one', SimpleAxis()), ('two', SimpleAxis()), ('three', SimpleAxis())) registry.register('foo', one=1, three=3) self.assertEqual(registry.lookup(1, three=3), 'foo')
def test_miss(self): from generic.registry import Registry from generic.registry import SimpleAxis registry = Registry(('one', SimpleAxis()), ('two', SimpleAxis()), ('three', SimpleAxis())) registry.register('foo', 1, 2) self.assertEqual(registry.lookup(one=1, three=3), None)
def test_miss(self): from generic.registry import Registry from generic.registry import SimpleAxis registry = Registry( ('one', SimpleAxis()), ('two', SimpleAxis()), ('three', SimpleAxis()) ) registry.register('foo', 1, 2) self.assertEqual(registry.lookup(one=1, three=3), None)
def test_skip_nodes(self): from generic.registry import Registry from generic.registry import SimpleAxis registry = Registry( ('one', SimpleAxis()), ('two', SimpleAxis()), ('three', SimpleAxis()) ) registry.register('foo', one=1, three=3) self.assertEqual(registry.lookup(1, three=3), 'foo')
def __init__(self, argspec, params_arity): """ Initialize dispatcher with ``argspec`` of type :class:`inspect.ArgSpec` and ``params_arity`` that represent number params.""" # Check if we have enough positional arguments for number of type params if arity(argspec) < params_arity: raise TypeError("Not enough positional arguments " "for number of type parameters provided.") self.argspec = argspec self.params_arity = params_arity axis = [("arg_%d" % n, TypeAxis()) for n in range(params_arity)] self.registry = Registry(*axis)
def test_get_registration(self): from generic.registry import Registry from generic.registry import SimpleAxis from generic.registry import TypeAxis registry = Registry(('type', TypeAxis()), ('name', SimpleAxis())) registry.register('one', object) registry.register('two', DummyA, 'foo') self.assertEqual(registry.get_registration(object), 'one') self.assertEqual(registry.get_registration(DummyA, 'foo'), 'two') self.assertEqual(registry.get_registration(object, 'foo'), None) self.assertEqual(registry.get_registration(DummyA), None)
def test_bad_lookup(self): from generic.registry import Registry from generic.registry import SimpleAxis registry = Registry(('name', SimpleAxis()), ('grade', SimpleAxis())) self.assertRaises(ValueError, registry.register, 1, foo=1) self.assertRaises(ValueError, registry.lookup, foo=1) self.assertRaises(ValueError, registry.register, 1, 'foo', name='foo')
def test_override(self): from generic.registry import Registry from generic.registry import SimpleAxis registry = Registry(('name', SimpleAxis())) registry.register(1, name='foo') registry.override(2, name='foo') self.assertEqual(registry.lookup('foo'), 2)
def test_one_axis_no_specificity(self): from generic.registry import Registry from generic.registry import SimpleAxis registry = Registry(('foo', SimpleAxis())) a = object() b = object() registry.register(a) registry.register(b, 'foo') self.assertEqual(registry.lookup(), a) self.assertEqual(registry.lookup('foo'), b) self.assertEqual(registry.lookup('bar'), None)
class Manager(object): """ Event manager.""" def __init__(self): axes = (("event_type", TypeAxis()), ) self.registry = Registry(*axes) def subscribe(self, handler, event_type): """ Subscribe ``handler`` to specified ``event_type``.""" handler_set = self.registry.get_registration(event_type) if not handler_set: handler_set = self._register_handler_set(event_type) handler_set.handlers.add(handler) def unsubscribe(self, handler, event_type): """ Unsubscribe ``handler`` from ``event_type``.""" handler_set = self.registry.get_registration(event_type) if handler_set and handler in handler_set.handlers: handler_set.handlers.remove(handler) def fire(self, event): """ Fire event instance.""" handler_set = self.registry.lookup(event) for handler in handler_set.all_handlers: handler(event) def _register_handler_set(self, event_type): """ Register new handler set for ``event_type``.""" # Collect handler sets for supertypes parent_handler_sets = [] parents = event_type.__bases__ for parent in parents: parent_handlers = self.registry.get_registration(parent) if parent_handlers is None: parent_handlers = self._register_handler_set(parent) parent_handler_sets.append(parent_handlers) handler_set = HandlerSet(parents=parent_handler_sets, handlers=set()) self.registry.register(handler_set, event_type) return handler_set def subscriber(self, event_type): """ Decorator for subscribing decorated functions. Works like this: >>> @mymanager.subscriber(MyEvent) ... def mysubscriber(evt): ... # handle event ... return >>> mymanager.fire(MyEvent()) """ def registrator(func): self.subscribe(func, event_type) return func return registrator
class Manager(object): """ Event manager.""" def __init__(self): axes = (("event_type", TypeAxis()),) self.registry = Registry(*axes) def subscribe(self, handler, event_type): """ Subscribe ``handler`` to specified ``event_type``.""" handler_set = self.registry.get_registration(event_type) if not handler_set: handler_set = self._register_handler_set(event_type) handler_set.handlers.add(handler) def unsubscribe(self, handler, event_type): """ Unsubscribe ``handler`` from ``event_type``.""" handler_set = self.registry.get_registration(event_type) if handler_set and handler in handler_set.handlers: handler_set.handlers.remove(handler) def fire(self, event): """ Fire event instance.""" handler_set = self.registry.lookup(event) for handler in handler_set.all_handlers: handler(event) def _register_handler_set(self, event_type): """ Register new handler set for ``event_type``.""" # Collect handler sets for supertypes parent_handler_sets = [] parents = event_type.__bases__ for parent in parents: parent_handlers = self.registry.get_registration(parent) if parent_handlers is None: parent_handlers = self._register_handler_set(parent) parent_handler_sets.append(parent_handlers) handler_set = HandlerSet(parents=parent_handler_sets, handlers=set()) self.registry.register(handler_set, event_type) return handler_set def subscriber(self, event_type): """ Decorator for subscribing decorated functions. Works like this: >>> @mymanager.subscriber(MyEvent) ... def mysubscriber(evt): ... # handle event ... return >>> mymanager.fire(MyEvent()) """ def registrator(func): self.subscribe(func, event_type) return func return registrator
class Manager(object): """ Event manager Provides API for subscribing for and firing events. There's also global event manager instantiated at module level with functions :func:`.subscribe`, :func:`.fire` and decorator :func:`.subscriber` aliased to corresponding methods of class. """ def __init__(self): axes = (("event_type", TypeAxis()), ) self.registry = Registry(*axes) def subscribe(self, handler, event_type): """ Subscribe ``handler`` to specified ``event_type``""" handler_set = self.registry.get_registration(event_type) if not handler_set: handler_set = self._register_handler_set(event_type) handler_set.handlers.add(handler) def unsubscribe(self, handler, event_type): """ Unsubscribe ``handler`` from ``event_type``""" handler_set = self.registry.get_registration(event_type) if handler_set and handler in handler_set.handlers: handler_set.handlers.remove(handler) def fire(self, event): """ Fire ``event`` All subscribers will be executed with no determined order. """ handler_set = self.registry.lookup(event) for handler in handler_set.all_handlers: handler(event) def _register_handler_set(self, event_type): """ Register new handler set for ``event_type``.""" # Collect handler sets for supertypes parent_handler_sets = [] parents = event_type.__bases__ for parent in parents: parent_handlers = self.registry.get_registration(parent) if parent_handlers is None: parent_handlers = self._register_handler_set(parent) parent_handler_sets.append(parent_handlers) handler_set = HandlerSet(parents=parent_handler_sets, handlers=set()) self.registry.register(handler_set, event_type) return handler_set def subscriber(self, event_type): """ Decorator for subscribing handlers Works like this: >>> @mymanager.subscriber(MyEvent) ... def mysubscriber(evt): ... # handle event ... return >>> mymanager.fire(MyEvent()) """ def registrator(func): self.subscribe(func, event_type) return func return registrator
class FunctionDispatcher(object): """ Multidispatcher for functions This object dispatch calls to function by its argument types. Usually it is produced by :func:`.multifunction` decorator. You should not manually create objects of this type. """ def __init__(self, argspec, params_arity): """ Initialize dispatcher with ``argspec`` of type :class:`inspect.ArgSpec` and ``params_arity`` that represent number params.""" # Check if we have enough positional arguments for number of type params if arity(argspec) < params_arity: raise TypeError("Not enough positional arguments " "for number of type parameters provided.") self.argspec = argspec self.params_arity = params_arity axis = [("arg_%d" % n, TypeAxis()) for n in range(params_arity)] self.registry = Registry(*axis) def check_rule(self, rule, *argtypes): # Check if we have the right number of parametrized types if len(argtypes) != self.params_arity: raise TypeError("Wrong number of type parameters.") # Check if we have the same argspec (by number of args) rule_argspec = inspect.getargspec(rule) if not is_equalent_argspecs(rule_argspec, self.argspec): raise TypeError("Rule does not conform " "to previous implementations.") def register_rule(self, rule, *argtypes): """ Register new ``rule`` for ``argtypes``.""" self.check_rule(rule, *argtypes) self.registry.register(rule, *argtypes) def override_rule(self, rule, *argtypes): """ Override ``rule`` for ``argtypes``.""" self.check_rule(rule, *argtypes) self.registry.override(rule, *argtypes) def lookup_rule(self, *args): """ Lookup rule by ``args``. Returns None if no rule was found.""" args = args[:self.params_arity] rule = self.registry.lookup(*args) if rule is None: raise TypeError("No available rule found for %r" % (args,)) return rule def when(self, *argtypes): """ Decorator for registering new case for multifunction New case will be registered for types identified by ``argtypes``. The length of ``argtypes`` should be equal to the length of ``argtypes`` argument were passed corresponding :func:`.multifunction` call, which also indicated the number of arguments multifunction dispatches on. """ def register_rule(func): self.register_rule(func, *argtypes) return self return register_rule @property def otherwise(self): """ Decorator which registeres "catch-all" case for multifunction""" def register_rule(func): self.register_rule(func, [object]*self.params_arity) return self return register_rule def override(self, *argtypes): """ Decorator for overriding case for ``argtypes``""" def override_rule(func): self.override_rule(func, *argtypes) return self return override_rule def __call__(self, *args, **kwargs): """ Dispatch call to appropriate rule.""" rule = self.lookup_rule(*args) return rule(*args, **kwargs)
def test_conflict_error(self): from generic.registry import Registry from generic.registry import SimpleAxis registry = Registry(('name', SimpleAxis())) registry.register(object(), name='foo') self.assertRaises(ValueError, registry.register, object(), 'foo')
def __init__(self): axes = (("event_type", TypeAxis()), ) self.registry = Registry(*axes)
class Manager(object): """ Event manager Provides API for subscribing for and firing events. There's also global event manager instantiated at module level with functions :func:`.subscribe`, :func:`.fire` and decorator :func:`.subscriber` aliased to corresponding methods of class. """ def __init__(self): axes = (("event_type", TypeAxis()),) self.registry = Registry(*axes) def subscribe(self, handler, event_type): """ Subscribe ``handler`` to specified ``event_type``""" handler_set = self.registry.get_registration(event_type) if not handler_set: handler_set = self._register_handler_set(event_type) handler_set.handlers.add(handler) def unsubscribe(self, handler, event_type): """ Unsubscribe ``handler`` from ``event_type``""" handler_set = self.registry.get_registration(event_type) if handler_set and handler in handler_set.handlers: handler_set.handlers.remove(handler) def fire(self, event): """ Fire ``event`` All subscribers will be executed with no determined order. """ handler_set = self.registry.lookup(event) for handler in handler_set.all_handlers: handler(event) def _register_handler_set(self, event_type): """ Register new handler set for ``event_type``.""" # Collect handler sets for supertypes parent_handler_sets = [] parents = event_type.__bases__ for parent in parents: parent_handlers = self.registry.get_registration(parent) if parent_handlers is None: parent_handlers = self._register_handler_set(parent) parent_handler_sets.append(parent_handlers) handler_set = HandlerSet(parents=parent_handler_sets, handlers=set()) self.registry.register(handler_set, event_type) return handler_set def subscriber(self, event_type): """ Decorator for subscribing handlers Works like this: >>> @mymanager.subscriber(MyEvent) ... def mysubscriber(evt): ... # handle event ... return >>> mymanager.fire(MyEvent()) """ def registrator(func): self.subscribe(func, event_type) return func return registrator
def test_lookup_too_many_keys(self): from generic.registry import Registry from generic.registry import SimpleAxis registry = Registry(('name', SimpleAxis())) self.assertRaises(ValueError, registry.lookup, 'one', 'two')
def test_two_axes(self): from generic.registry import Registry from generic.registry import SimpleAxis from generic.registry import TypeAxis registry = Registry(('type', TypeAxis()), ('name', SimpleAxis())) target1 = Target('one') registry.register(target1, object) target2 = Target('two') registry.register(target2, DummyA) target3 = Target('three') registry.register(target3, DummyA, 'foo') context1 = object() self.assertEqual(registry.lookup(context1), target1) context2 = DummyB() self.assertEqual(registry.lookup(context2), target2) self.assertEqual(registry.lookup(context2, 'foo'), target3) target4 = object() registry.register(target4, DummyB) self.assertEqual(registry.lookup(context2), target4) self.assertEqual(registry.lookup(context2, 'foo'), target3)
class FunctionDispatcher(object): """ Multidispatcher for functions This object dispatch calls to function by its argument types. Usually it is produced by :func:`.multifunction` decorator. You should not manually create objects of this type. """ def __init__(self, argspec, params_arity): """ Initialize dispatcher with ``argspec`` of type :class:`inspect.ArgSpec` and ``params_arity`` that represent number params.""" # Check if we have enough positional arguments for number of type params if arity(argspec) < params_arity: raise TypeError("Not enough positional arguments " "for number of type parameters provided.") self.argspec = argspec self.params_arity = params_arity axis = [("arg_%d" % n, TypeAxis()) for n in range(params_arity)] self.registry = Registry(*axis) def check_rule(self, rule, *argtypes): # Check if we have the right number of parametrized types if len(argtypes) != self.params_arity: raise TypeError("Wrong number of type parameters.") # Check if we have the same argspec (by number of args) rule_argspec = inspect.getargspec(rule) if not is_equalent_argspecs(rule_argspec, self.argspec): raise TypeError("Rule does not conform " "to previous implementations.") def register_rule(self, rule, *argtypes): """ Register new ``rule`` for ``argtypes``.""" self.check_rule(rule, *argtypes) self.registry.register(rule, *argtypes) def override_rule(self, rule, *argtypes): """ Override ``rule`` for ``argtypes``.""" self.check_rule(rule, *argtypes) self.registry.override(rule, *argtypes) def lookup_rule(self, *args): """ Lookup rule by ``args``. Returns None if no rule was found.""" args = args[:self.params_arity] rule = self.registry.lookup(*args) if rule is None: raise TypeError("No available rule found for %r" % (args, )) return rule def when(self, *argtypes): """ Decorator for registering new case for multifunction New case will be registered for types identified by ``argtypes``. The length of ``argtypes`` should be equal to the length of ``argtypes`` argument were passed corresponding :func:`.multifunction` call, which also indicated the number of arguments multifunction dispatches on. """ def register_rule(func): self.register_rule(func, *argtypes) return self return register_rule @property def otherwise(self): """ Decorator which registeres "catch-all" case for multifunction""" def register_rule(func): self.register_rule(func, [object] * self.params_arity) return self return register_rule def override(self, *argtypes): """ Decorator for overriding case for ``argtypes``""" def override_rule(func): self.override_rule(func, *argtypes) return self return override_rule def __call__(self, *args, **kwargs): """ Dispatch call to appropriate rule.""" rule = self.lookup_rule(*args) return rule(*args, **kwargs)
def __init__(self): axes = (("event_type", TypeAxis()),) self.registry = Registry(*axes)
class FunctionDispatcher(Generic[T]): """ Multidispatcher for functions This object dispatch calls to function by its argument types. Usually it is produced by :func:`.multidispatch` decorator. You should not manually create objects of this type. """ registry: Registry[T] def __init__(self, argspec: inspect.FullArgSpec, params_arity: int) -> None: """ Initialize dispatcher with ``argspec`` of type :class:`inspect.ArgSpec` and ``params_arity`` that represent number params.""" # Check if we have enough positional arguments for number of type params if _arity(argspec) < params_arity: raise TypeError("Not enough positional arguments " "for number of type parameters provided.") self.argspec = argspec self.params_arity = params_arity axis = [(f"arg_{n:d}", TypeAxis()) for n in range(params_arity)] self.registry = Registry(*axis) def check_rule(self, rule: T, *argtypes: KeyType) -> None: # Check if we have the right number of parametrized types if len(argtypes) != self.params_arity: raise TypeError( f"Wrong number of type parameters: have {len(argtypes)}, expected {self.params_arity}." ) # Check if we have the same argspec (by number of args) rule_argspec = inspect.getfullargspec(rule) left_spec = tuple(x and len(x) or 0 for x in rule_argspec[:4]) right_spec = tuple(x and len(x) or 0 for x in self.argspec[:4]) if left_spec != right_spec: raise TypeError( f"Rule does not conform to previous implementations: {left_spec} != {right_spec}." ) def register_rule(self, rule: T, *argtypes: KeyType) -> None: """ Register new ``rule`` for ``argtypes``.""" self.check_rule(rule, *argtypes) self.registry.register(rule, *argtypes) def register(self, *argtypes: KeyType) -> Callable[[T], T]: """ Decorator for registering new case for multidispatch New case will be registered for types identified by ``argtypes``. The length of ``argtypes`` should be equal to the length of ``argtypes`` argument were passed corresponding :func:`.multidispatch` call, which also indicated the number of arguments multidispatch dispatches on. """ def register_rule(func: T) -> T: self.register_rule(func, *argtypes) return func return register_rule def __call__(self, *args: Any, **kwargs: Any) -> Any: """ Dispatch call to appropriate rule.""" trimmed_args = args[:self.params_arity] rule = self.registry.lookup(*trimmed_args) if not rule: print(self.registry._tree) raise TypeError(f"No available rule found for {trimmed_args!r}") return rule(*args, **kwargs)
class Manager: """ Event manager Provides API for subscribing for and firing events. There's also global event manager instantiated at module level with functions :func:`.subscribe`, :func:`.handle` and decorator :func:`.subscriber` aliased to corresponding methods of class. """ registry: Registry[HandlerSet] def __init__(self) -> None: axes = (("event_type", TypeAxis()), ) self.registry = Registry(*axes) def subscribe(self, handler: Handler, event_type: Type[Event]) -> None: """ Subscribe ``handler`` to specified ``event_type``""" handler_set = self.registry.get_registration(event_type) if handler_set is None: handler_set = self._register_handler_set(event_type) handler_set.add(handler) def unsubscribe(self, handler: Handler, event_type: Type[Event]) -> None: """ Unsubscribe ``handler`` from ``event_type``""" handler_set = self.registry.get_registration(event_type) if handler_set and handler in handler_set: handler_set.remove(handler) def handle(self, event: Event) -> None: """ Fire ``event`` All subscribers will be executed with no determined order. """ handler_sets = self.registry.query(event) for handler_set in handler_sets: if handler_set: for handler in set(handler_set): handler(event) def _register_handler_set(self, event_type: Type[Event]) -> HandlerSet: """ Register new handler set for ``event_type``. """ handler_set: HandlerSet = set() self.registry.register(handler_set, event_type) return handler_set def subscriber(self, event_type: Type[Event]) -> Callable[[Handler], Handler]: """ Decorator for subscribing handlers Works like this: >>> mymanager = Manager() >>> class MyEvent(): ... pass >>> @mymanager.subscriber(MyEvent) ... def mysubscriber(evt): ... # handle event ... return >>> mymanager.handle(MyEvent()) """ def registrator(func: Handler) -> Handler: self.subscribe(func, event_type) return func return registrator