Пример #1
0
 def test_get_registration(self):
     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)
Пример #2
0
def test_get_registration():
    registry = Registry(("type", TypeAxis()), ("name", SimpleAxis()))
    registry.register("one", object)
    registry.register("two", DummyA, "foo")
    assert registry.get_registration(object) == "one"
    assert registry.get_registration(DummyA, "foo") == "two"
    assert registry.get_registration(object, "foo") is None
    assert registry.get_registration(DummyA) is None
Пример #3
0
    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)
Пример #4
0
def test_bad_lookup():
    registry = Registry(("name", SimpleAxis()), ("grade", SimpleAxis()))
    with pytest.raises(ValueError):
        registry.register(1, foo=1)
    with pytest.raises(ValueError):
        registry.lookup(foo=1)
    with pytest.raises(ValueError):
        registry.register(1, "foo", name="foo")
Пример #5
0
def test_subtyping_on_axes():
    registry = Registry(("type", TypeAxis()))

    target1 = "one"
    registry.register(target1, object)

    target2 = "two"
    registry.register(target2, DummyA)

    target3 = "three"
    registry.register(target3, DummyB)

    assert registry.lookup(object()) == target1
    assert registry.lookup(DummyA()) == target2
    assert registry.lookup(DummyB()) == target3
Пример #6
0
    def test_subtyping_on_axes(self):
        registry = Registry(("type", TypeAxis()))

        target1 = "one"
        registry.register(target1, object)

        target2 = "two"
        registry.register(target2, DummyA)

        target3 = "three"
        registry.register(target3, DummyB)

        self.assertEqual(registry.lookup(object()), target1)
        self.assertEqual(registry.lookup(DummyA()), target2)
        self.assertEqual(registry.lookup(DummyB()), target3)
Пример #7
0
def test_two_axes():
    registry: Registry[Union[str, object]] = Registry(("type", TypeAxis()),
                                                      ("name", SimpleAxis()))

    target1 = "one"
    registry.register(target1, object)

    target2 = "two"
    registry.register(target2, DummyA)

    target3 = "three"
    registry.register(target3, DummyA, "foo")

    context1 = object()
    assert registry.lookup(context1) == target1

    context2 = DummyB()
    assert registry.lookup(context2) == target2
    assert registry.lookup(context2, "foo") == target3

    target4 = object()
    registry.register(target4, DummyB)

    assert registry.lookup(context2) == target4
    assert registry.lookup(context2, "foo") == target3
Пример #8
0
def test_one_axis_no_specificity():
    registry = Registry(("foo", SimpleAxis()))
    a = object()
    b = object()
    registry.register(a)
    registry.register(b, "foo")

    assert registry.lookup() == a
    assert registry.lookup("foo") == b
    assert registry.lookup("bar") is None
Пример #9
0
    def test_one_axis_no_specificity(self):
        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)
Пример #10
0
def test_query_subtyping_on_axes():
    registry: Registry[str] = Registry(("type", TypeAxis()))

    target1 = "one"
    registry.register(target1, object)

    target2 = "two"
    registry.register(target2, DummyA)

    target3 = "three"
    registry.register(target3, DummyB)

    target4 = "four"
    registry.register(target4, int)

    assert list(registry.query(object())) == [target1]
    assert list(registry.query(DummyA())) == [target2, target1]
    assert list(registry.query(DummyB())) == [target3, target2, target1]
    assert list(registry.query(3)) == [target4, target1]
Пример #11
0
 def test_miss(self):
     registry = Registry(
         ("one", SimpleAxis()), ("two", SimpleAxis()), ("three", SimpleAxis())
     )
     registry.register("foo", 1, 2)
     self.assertEqual(registry.lookup(one=1, three=3), None)
Пример #12
0
 def test_bad_lookup(self):
     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")
Пример #13
0
def test_miss():
    registry = Registry(
        ("one", SimpleAxis()), ("two", SimpleAxis()), ("three", SimpleAxis())
    )
    registry.register("foo", 1, 2)
    assert registry.lookup(one=1, three=3) is None
Пример #14
0
def test_skip_nodes():
    registry = Registry(
        ("one", SimpleAxis()), ("two", SimpleAxis()), ("three", SimpleAxis())
    )
    registry.register("foo", one=1, three=3)
    assert registry.lookup(1, three=3) == "foo"
Пример #15
0
def test_conflict_error():
    registry = Registry(("name", SimpleAxis()))
    registry.register(object(), name="foo")
    with pytest.raises(ValueError):
        registry.register(object, "foo")
Пример #16
0
    def test_two_axes(self):
        registry = Registry(("type", TypeAxis()), ("name", SimpleAxis()))

        target1 = "one"
        registry.register(target1, object)

        target2 = "two"
        registry.register(target2, DummyA)

        target3 = "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)
Пример #17
0
def test_register_too_many_keys():
    registry = Registry(("name", SimpleAxis()))
    with pytest.raises(ValueError):
        registry.register(object, "one", "two")
Пример #18
0
 def test_conflict_error(self):
     registry = Registry(("name", SimpleAxis()))
     registry.register(object(), name="foo")
     self.assertRaises(ValueError, registry.register, object(), "foo")
Пример #19
0
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.
    """
    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 handler_set is None:
            handler_set = self._register_handler_set(event_type)
        handler_set.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:
            handler_set.remove(handler)

    def handle(self, event):
        """ Fire ``event``

        All subscribers will be executed with no determined order.
        """
        handler_sets = self.registry.query(event)
        for handler_set in handler_sets:
            for handler in set(handler_set):
                handler(event)

    def _register_handler_set(self, event_type):
        """ Register new handler set for ``event_type``.
        """
        handler_set = set()
        self.registry.register(handler_set, event_type)
        return handler_set

    def subscriber(self, event_type):
        """ 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):
            self.subscribe(func, event_type)
            return func

        return registrator
Пример #20
0
 def test_register_too_many_keys(self):
     registry = Registry(("name", SimpleAxis()))
     self.assertRaises(ValueError, registry.register, object(), "one", "two")
Пример #21
0
 def test_skip_nodes(self):
     registry = Registry(
         ("one", SimpleAxis()), ("two", SimpleAxis()), ("three", SimpleAxis())
     )
     registry.register("foo", one=1, three=3)
     self.assertEqual(registry.lookup(1, three=3), "foo")
Пример #22
0
def test_lookup_too_many_keys():
    registry = Registry(("name", SimpleAxis()))
    with pytest.raises(ValueError):
        registry.register(registry.lookup("one", "two"))
Пример #23
0
class FunctionDispatcher:
    """ 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.
    """

    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 = [(f"arg_{n:d}", 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(
                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("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 register(self, *argtypes):
        """ 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):
            self.register_rule(func, *argtypes)
            return func

        return register_rule

    def __call__(self, *args, **kwargs):
        """ Dispatch call to appropriate rule."""
        trimmed_args = args[: self.params_arity]
        rule = self.registry.lookup(*trimmed_args)
        if not rule:
            raise TypeError(f"No available rule found for {trimmed_args!r}")
        return rule(*args, **kwargs)
Пример #24
0
    def test_query_subtyping_on_axes(self):
        registry = Registry(("type", TypeAxis()))

        target1 = "one"
        registry.register(target1, object)

        target2 = "two"
        registry.register(target2, DummyA)

        target3 = "three"
        registry.register(target3, DummyB)

        target4 = "four"
        registry.register(target4, int)

        self.assertEqual(list(registry.query(object())), [target1])
        self.assertEqual(list(registry.query(DummyA())), [target2, target1])
        self.assertEqual(list(registry.query(DummyB())), [target3, target2, target1])
        self.assertEqual(list(registry.query(3)), [target4, target1])
Пример #25
0
 def __init__(self):
     axes = (("event_type", TypeAxis()), )
     self.registry = Registry(*axes)
Пример #26
0
 def test_lookup_too_many_keys(self):
     registry = Registry(("name", SimpleAxis()))
     self.assertRaises(ValueError, registry.lookup, "one", "two")