def test_nestedbackend(): obj = object() be_outer = Backend() be_outer.__ua_function__ = lambda f, a, kw: obj mm1 = ua.generate_multimethod(lambda: (), lambda a, kw, d: (a, kw), "ua_tests") def default(*a, **kw): return mm1(*a, **kw) mm2 = ua.generate_multimethod(lambda: (), lambda a, kw, d: (a, kw), "ua_tests", default=default) be_inner = Backend() def be2_ua_func(f, a, kw): with ua.skip_backend(be_inner): return f(*a, **kw) be_inner.__ua_function__ = be2_ua_func with ua.set_backend(be_outer), ua.set_backend(be_inner): assert mm2() is obj
def test_hierarchical_backends(): mm = ua.generate_multimethod(lambda: (), lambda a, kw, d: (a, kw), "ua_tests.foo.bar") subdomains = "ua_tests.foo.bar".split(".") depth = len(subdomains) mms = [ ua.generate_multimethod(lambda: (), lambda a, kw, d: (a, kw), ".".join(subdomains[:i + 1])) for i in range(depth) ] be = [DisableBackend(".".join(subdomains[:i + 1])) for i in range(depth)] ua.set_global_backend(be[1]) with pytest.raises(ua.BackendNotImplementedError): mms[0]() for i in range(1, depth): assert mms[i]() is be[1].ret ua.set_global_backend(be[0]) for i in range(depth): assert mms[i]() is be[min(i, 1)].ret ua.set_global_backend(be[2]) for i in range(depth): assert mms[i]() is be[i].ret be[2].active = False for i in range(depth): print(i) assert mms[i]() is be[min(i, 1)].ret be[1].active = False for i in range(depth): assert mms[i]() is be[0].ret be[0].active = False for i in range(depth): with pytest.raises(ua.BackendNotImplementedError): mms[i]() # only=True prevents all further domain checking be[0].active = True be[1].active = True with ua.set_backend(be[2], only=True), pytest.raises( ua.BackendNotImplementedError): mms[2]()
def test_default(nullary_mm): obj = object() be = Backend() be.__ua_function__ = lambda f, a, kw: NotImplemented # If a backend returns NotImplemented, the default is called def default1(*a, **kw): return obj mm1 = ua.generate_multimethod(lambda: (), lambda a, kw, d: (a, kw), "ua_tests", default=default1) with ua.set_backend(be): assert mm1() is obj # If all backends fail, the default is called again without a specific backend num_calls = [0] def default2(*a, **kw): num_calls[0] = num_calls[0] + 1 raise ua.BackendNotImplementedError() mm2 = ua.generate_multimethod(lambda: (), lambda a, kw, d: (a, kw), "ua_tests", default=default2) with ua.set_backend(be), pytest.raises(ua.BackendNotImplementedError): mm2() assert num_calls[0] == 2 # If the last backend is set as only or coerce, the last default call is skipped num_calls[0] = 0 with ua.set_backend(be, only=True), pytest.raises( ua.BackendNotImplementedError): mm2() assert num_calls[0] == 1 num_calls[0] = 0 with ua.set_backend(be, coerce=True), pytest.raises( ua.BackendNotImplementedError): mm2() assert num_calls[0] == 1
def test_global(cleanup_backends): obj = object() be = Backend() be.__ua_function__ = lambda f, a, kw: obj mm = ua.generate_multimethod(lambda: (), lambda a, kw, d: (a, kw), "ua_tests") ua.set_global_backend(be) assert mm() is obj
def test_get_extractor_replacer(): def extractor(): return () def replacer(a, kw, d): return a, kw mm = ua.generate_multimethod(extractor, replacer, "ua_tests") assert mm.arg_extractor is extractor assert mm.arg_replacer is replacer
def test_determine_backend_coerce(nullary_mm): class TypeA: pass class TypeB: pass mark = "determine_backend_test" class TypeBackend: __ua_domain__ = "ua_tests" def __init__(self, my_type): self.my_type = my_type def __ua_convert__(self, dispatchables, coerce): if len(dispatchables) > 0: print(dispatchables[0], coerce) if coerce and all(d.coercible for d in dispatchables): return tuple(self.my_type() for _ in dispatchables) if not all( type(d.value) is self.my_type and d.type is mark for d in dispatchables): return NotImplemented return tuple(d.value for d in dispatchables) def __ua_function__(self, func, args, kwargs): return self.my_type BackendA = TypeBackend(TypeA) BackendB = TypeBackend(TypeB) unary_mm = ua.generate_multimethod(lambda a: (ua.Dispatchable(a, mark), ), lambda a, kw, d: (d, kw), "ua_tests") # coercion is not forced on the existing set backend with ua.set_backend(BackendA), ua.set_backend(BackendB): with ua.determine_backend(TypeA(), mark, domain="ua_tests", coerce=True): assert nullary_mm() is TypeA assert unary_mm(TypeB()) is TypeA # But is allowed if the backend was set with coerce in the first place with ua.set_backend(BackendA), ua.set_backend(BackendB, coerce=True): with ua.determine_backend(TypeA(), mark, domain="ua_tests", coerce=True): assert nullary_mm() is TypeB assert unary_mm(TypeA()) is TypeB
def test_global_before_registered(cleanup_backends): obj = object() obj2 = object() be = Backend() be.__ua_function__ = lambda f, a, kw: obj be2 = Backend() be2.__ua_function__ = lambda f, a, kw: obj2 mm = ua.generate_multimethod(lambda: (), lambda a, kw, d: (a, kw), "ua_tests") ua.set_global_backend(be) ua.register_backend(be2) assert mm() is obj
def test_global_only(cleanup_backends): obj = object() be = Backend() be.__ua_function__ = lambda f, a, kw: NotImplemented be2 = Backend() be2.__ua_function__ = lambda f, a, kw: obj mm = ua.generate_multimethod(lambda: (), lambda a, kw, d: (a, kw), "ua_tests") ua.set_global_backend(be, only=True) ua.register_backend(be2) with pytest.raises(ua.BackendNotImplementedError): mm()
def test_clear_backends(cleanup_backends): obj = object() obj2 = object() be = Backend() be.__ua_function__ = lambda f, a, kw: obj be2 = Backend() be2.__ua_function__ = lambda f, a, kw: obj2 mm = ua.generate_multimethod(lambda: (), lambda a, kw, d: (a, kw), "ua_tests") ua.set_global_backend(be) ua.register_backend(be2) ua.clear_backends(Backend.__ua_domain__, registered=True, globals=True) with pytest.raises(ua.BackendNotImplementedError): mm()
def test_multidomain_backends(): n_domains = 2 be = DisableBackend(domain=["ua_tests" + str(i) for i in range(n_domains)]) mms = [ ua.generate_multimethod(lambda: (), lambda a, kw, d: (a, kw), "ua_tests" + str(i)) for i in range(n_domains) ] def assert_no_backends(): for i in range(len(mms)): with pytest.raises(ua.BackendNotImplementedError): mms[i]() def assert_backend_active(backend): assert all(mms[i]() is backend.ret for i in range(len(mms))) assert_no_backends() with ua.set_backend(be): assert_backend_active(be) ua.set_global_backend(be) assert_backend_active(be) with ua.skip_backend(be): assert_no_backends() assert_backend_active(be) for i in range(len(mms)): ua.clear_backends(mms[i].domain, globals=True) with pytest.raises(ua.BackendNotImplementedError): mms[i]() for j in range(i + 1, len(mms)): assert mms[j]() is be.ret assert_no_backends() ua.register_backend(be) assert_backend_active(be)
def test_function_attrs(): def extractor(): return () def replacer(a, kw, d): return a, kw def default(): return NotImplemented mm = ua.generate_multimethod(extractor, replacer, "ua_tests", default=default) assert mm.arg_extractor is extractor assert mm.arg_replacer is replacer assert mm.default is default assert mm.domain == "ua_tests"
def nullary_mm(): return ua.generate_multimethod(lambda: (), lambda a, kw, d: (a, kw), "ua_tests")
return self.my_types[0]() class TypeA: @classmethod def __repr__(cls): return cls.__name__ class TypeB(TypeA): pass class TypeC(TypeA): pass BackendA = _TypedBackend(TypeA) BackendB = _TypedBackend(TypeB) BackendC = _TypedBackend(TypeC) BackendAB = _TypedBackend(TypeA, TypeB) BackendBC = _TypedBackend(TypeB, TypeC) creation_multimethod = ua.generate_multimethod(lambda: (), lambda a, kw, d: (a, kw), "ua_examples") call_multimethod = ua.generate_multimethod( lambda *a: tuple(ua.Dispatchable(x, "mark") for x in a), lambda a, kw, d: (a, kw), "ua_examples", )