コード例 #1
0
def make_example_resolver():
    res = Resolver()
    import metagraph

    res.register({
        "example_plugin": {
            "abstract_types": {MyAbstractType, MyNumericAbstractType},
            "concrete_types": {StrType, IntType, FloatType, OtherType},
            "wrappers": {StrNum},
            "translators": {int_to_str, str_to_int},
            "abstract_algorithms": {
                abstract_power,
                abstract_ln,
                abstract_echo,
                odict_reverse,
            },
            "concrete_algorithms": {
                int_power,
                float_ln,
                simple_echo,
                simple_odict_rev,
            },
        }
    })
    return res
コード例 #2
0
def test_list_signatures():
    class Abstract1(AbstractType):
        pass

    class Abstract2(AbstractType):
        pass

    @abstract_algorithm("testing.typing_list_types")
    def typing_list_types(
        a: typing.List[int],
        b: typing.Optional[typing.List[float]],
        c: typing.Optional[typing.Union[int, float]],
        d: typing.List[Abstract1],
        e: typing.Optional[typing.List[Abstract1]],
        f: typing.Union[int, None],
    ) -> int:
        pass  # pragma: no cover

    @abstract_algorithm("testing.mg_list_types")
    def mg_list_types(
        a: mg.List[int],
        b: mg.Optional[mg.List[float]],
        c: mg.Optional[mg.Union[int, float]],
        d: mg.List[Abstract1],
        e: mg.Optional[mg.List[Abstract1]],
        # This is not currently supported. If the need arises, we should
        # add mg.UnionList[int, float] which is a Combo with a new kind=UnionList
        # f: mg.Union[mg.List[int], mg.List[float]],
    ) -> int:
        pass  # pragma: no cover

    @abstract_algorithm("testing.mg_list_instances")
    def mg_list_instances(
        a: mg.List[Abstract1()],
        b: mg.Optional[mg.List[Abstract1()]],
        c: mg.Optional[Abstract2()],
    ) -> int:
        pass  # pragma: no cover

    registry = PluginRegistry("test_list_signatures_good")
    registry.register(Abstract1)
    registry.register(Abstract2)
    registry.register(typing_list_types)
    registry.register(mg_list_types)
    registry.register(mg_list_instances)

    res_good = Resolver()
    res_good.register(registry.plugins)

    sig = res_good.abstract_algorithms[
        "testing.typing_list_types"].__signature__
    for letter in "bcef":
        assert sig.parameters[letter].annotation.optional
    for letter in "abde":
        assert sig.parameters[letter].annotation.kind == "List"
    assert sig.parameters["c"].annotation.kind == "Union"
    assert sig.parameters["f"].annotation.kind is None
コード例 #3
0
def test_roundtripper(default_plugin_resolver):
    dpr = default_plugin_resolver

    # Register translators that aren't round-trippable to induce failures
    registry = PluginRegistry("test_roundtripper")
    for at in dpr.abstract_types:
        registry.register(at)
    for ct in dpr.concrete_types:
        registry.register(ct)
    registry.register(
        dpr.translators[
            (dpr.types.NodeMap.PythonNodeMapType, dpr.types.NodeMap.NumpyNodeMapType)
        ]
    )
    registry.register(
        dpr.translators[
            (dpr.types.NodeMap.NumpyNodeMapType, dpr.types.NodeMap.GrblasNodeMapType)
        ]
    )
    registry.register(
        dpr.translators[
            (dpr.types.NodeMap.PythonNodeMapType, dpr.types.NodeSet.PythonNodeSetType)
        ]
    )

    res = Resolver()
    res.register(registry.plugins)
    # If test is in dask mode, convert our resolver to a dask resolver
    if isinstance(dpr, DaskResolver):
        res = DaskResolver(res)

    gnm = dpr.wrappers.NodeMap.GrblasNodeMap(
        grblas.Vector.from_values([0, 2], [1.1, 5.5])
    )
    gns = dpr.wrappers.NodeSet.GrblasNodeSet(grblas.Vector.from_values([0, 2], [1, 1]))
    pnm = {0: 1.1, 2: 5.5}
    pns = {0, 2}

    rt = RoundTripper(res)
    with pytest.raises(
        UnreachableTranslationError, match="Impossible to return from target"
    ):
        rt.verify_round_trip(pnm)
    with pytest.raises(UnreachableTranslationError, match="Impossible to reach source"):
        rt.verify_round_trip(gnm)

    with pytest.raises(
        TypeError, match="start and end must have different abstract types"
    ):
        rt.verify_one_way(pnm, gnm)
    with pytest.raises(UnreachableTranslationError, match="Impossible to reach source"):
        rt.verify_one_way(gnm, pns)
    with pytest.raises(
        UnreachableTranslationError, match="Impossible to return from target"
    ):
        rt.verify_one_way(pnm, gns)
コード例 #4
0
def test_type_of(example_resolver):
    empty_res = Resolver()
    with pytest.raises(TypeError, match="registered type"):
        t = empty_res.type_of(4)

    from .util import StrType, IntType, OtherType

    assert example_resolver.type_of(4) == IntType()
    assert example_resolver.type_of("python") == StrType(lowercase=True)
    assert example_resolver.type_of("Python") == StrType(lowercase=False)
    assert StrType().is_satisfied_by(example_resolver.type_of("python"))
コード例 #5
0
def test_unambiguous_subcomponents():
    class AbstractType1(AbstractType):
        properties = {
            "divisible_by_two": [False, True],
            "in_fizzbuzz_club": [False, True],
        }

    class AbstractType2(AbstractType):
        properties = {
            "divisible_by_two": [False, True],
        }
        unambiguous_subcomponents = {AbstractType1}

    res = Resolver()
    with pytest.raises(
            KeyError,
            match="unambiguous subcomponent .* has not been registered"):
        res.register({
            "test_unambiguous_subcomponents": {
                "abstract_types": {AbstractType2}
            }
        })

    res = Resolver()
    with pytest.raises(
            ValueError,
            match="unambiguous subcomponent .* has additional properties beyond"
    ):
        res.register({
            "test_unambiguous_subcomponents": {
                "abstract_types": {AbstractType1, AbstractType2}
            }
        })
コード例 #6
0
def test_union_signatures():
    class Abstract1(AbstractType):
        pass

    class Abstract2(AbstractType):
        pass

    @abstract_algorithm("testing.typing_union_types")
    def typing_union_types(
        a: typing.Union[int, float],
        b: typing.Optional[typing.Union[int, float]],
        c: typing.Optional[float],
        d: typing.Union[Abstract1, Abstract2],
        e: typing.Optional[typing.Union[Abstract1, Abstract2]],
        f: typing.Optional[Abstract2],
    ) -> int:
        pass  # pragma: no cover

    @abstract_algorithm("testing.mg_union_types")
    def mg_union_types(
        a: mg.Union[int, float],
        b: mg.Optional[mg.Union[int, float]],
        c: mg.Optional[float],
        d: mg.Union[Abstract1, Abstract2],
        e: mg.Optional[mg.Union[Abstract1, Abstract2]],
        f: mg.Optional[Abstract2],
    ) -> int:
        pass  # pragma: no cover

    @abstract_algorithm("testing.mg_union_instances")
    def mg_union_instances(
        a: mg.Union[Abstract1(), Abstract2()],
        b: mg.Optional[mg.Union[Abstract1(), Abstract2()]],
        c: mg.Optional[Abstract2()],
    ) -> int:
        pass  # pragma: no cover

    registry = PluginRegistry("test_union_signatures_good")
    registry.register(Abstract1)
    registry.register(Abstract2)
    registry.register(typing_union_types)
    registry.register(mg_union_types)
    registry.register(mg_union_instances)

    res_good = Resolver()
    res_good.register(registry.plugins)

    @abstract_algorithm("testing.typing_union_mixed")
    def typing_union_mixed(
            a: typing.Union[int, Abstract1]) -> int:  # pragma: no cover
        pass

    registry = PluginRegistry("test_union_signatures_bad")
    registry.register(Abstract1)
    registry.register(typing_union_mixed)

    with pytest.raises(TypeError, match="Cannot mix"):
        res_bad = Resolver()
        res_bad.register(registry.plugins)
コード例 #7
0
ファイル: test_resolver.py プロジェクト: eriknw/metagraph-1
def test_load_plugins(site_dir):
    res = Resolver()
    res.load_plugins_from_environment()

    def is_from_plugin1(x):
        if isinstance(x, set):
            return any(is_from_plugin1(item) for item in x)
        if hasattr(x, "__wrapped__"):
            x = x.__wrapped__
        return x.__module__.endswith("plugin1")

    assert len([x for x in res.abstract_types if is_from_plugin1(x)]) == 1
    assert len([x for x in res.concrete_types if is_from_plugin1(x)]) == 2
    assert len([x for x in res.translators.values() if is_from_plugin1(x)]) == 2
    assert len([x for x in res.abstract_algorithms.values() if is_from_plugin1(x)]) == 1
    assert len([x for x in res.concrete_algorithms.values() if is_from_plugin1(x)]) == 1
    assert "hyperstuff.supercluster" in res.concrete_algorithms
コード例 #8
0
ファイル: test_multiverify.py プロジェクト: seibert/metagraph
def test_unnormalizable(default_plugin_resolver):
    dpr = default_plugin_resolver
    # Only register some of the translators to force a multi-step translation path
    res_small = Resolver()
    res_small.register({
        "foo": {
            "abstract_types": dpr.abstract_types,
            "concrete_types": dpr.concrete_types,
            "wrappers": {PythonNodeMapType, NumpyNodeMap, GrblasNodeMap},
            "translators": {
                dpr.translators[(PythonNodeMapType, NumpyNodeMap.Type)],
            },
        }
    })
    mv = MultiVerify(res_small)
    mr = MultiResult(mv, {"testing.foo": {0: 1.1, 2: 4.5}})

    with pytest.raises(UnsatisfiableAlgorithmError,
                       match="Unable to convert type"):
        mr.normalize(GrblasNodeMap)
コード例 #9
0
def make_example_resolver():
    res = Resolver()
    import metagraph

    res.register({
        "example_plugin": {
            "abstract_types": {MyAbstractType, MyNumericAbstractType},
            "concrete_types": {StrType, IntType, FloatType, OtherType},
            "wrappers": {StrNum, StrNumRot13},
            "translators": {int_to_str, str_to_int, str_to_rot13},
            "abstract_algorithms": {
                abstract_power,
                abstract_ln,
                abstract_echo,
                odict_reverse,
                abstract_repeat,
                fizzbuzz_club,
                add_me_up,
                crazy_inputs,
            },
            "concrete_algorithms": {
                int_power,
                float_ln,
                simple_echo,
                simple_odict_rev,
                string_repeat,
                strnum_fizzbuzz_club,
                simple_add_me_up,
                simple_crazy_inputs,
            },
            "compilers": {FailCompiler(), IdentityCompiler()},
        },
        "example2_plugin": {
            "concrete_algorithms": {strnum_power}
        },
        "example3_plugin": {
            "concrete_algorithms": {strnumrot13_power}
        },
    })
    return res
コード例 #10
0
def default_plugin_resolver(request):  # pragma: no cover
    res = Resolver()
    if request.config.getoption("--no-plugins", default=False):
        from metagraph.plugins import find_plugins

        res.register(**find_plugins())
    else:
        res.load_plugins_from_environment()
    return res
コード例 #11
0
def test_invalid_plugin_names():
    res = Resolver()
    invalid_plugin_name = "invalid_name!#@$%#^&*()[]"

    class Abstract1(AbstractType):
        pass

    class Concrete1(ConcreteType, abstract=Abstract1):
        pass

    with pytest.raises(ValueError, match="is not a valid plugin name"):
        registry = PluginRegistry(invalid_plugin_name)

    registry = PluginRegistry("test_invalid_plugin_names_default_plugin")
    with pytest.raises(ValueError, match="is not a valid plugin name"):
        registry.register(Abstract1, invalid_plugin_name)

    with pytest.raises(ValueError, match="is not a valid plugin name"):
        registry.register_from_modules(mg.plugins.core.types,
                                       mg.plugins.core.algorithms,
                                       name=invalid_plugin_name)

    with pytest.raises(ValueError, match="is not a valid plugin name"):
        res.register({invalid_plugin_name: {"abstract_types": {Abstract1}}})
コード例 #12
0
def res():
    from metagraph.plugins.core.types import Vector
    from metagraph.plugins.numpy.types import NumpyVectorType

    @abstract_algorithm("testing.add")
    def testing_add(a: Vector, b: Vector) -> Vector:  # pragma: no cover
        pass

    @concrete_algorithm("testing.add", compiler="numba")
    def compiled_add(
            a: NumpyVectorType,
            b: NumpyVectorType) -> NumpyVectorType:  # pragma: no cover
        return a + b

    @abstract_algorithm("testing.scale")
    def testing_scale(a: Vector, scale: float) -> Vector:  # pragma: no cover
        pass

    @concrete_algorithm("testing.scale", compiler="numba")
    def compiled_scale(a: NumpyVectorType,
                       scale: float) -> NumpyVectorType:  # pragma: no cover
        return a * scale

    @abstract_algorithm("testing.offset")
    def testing_offset(a: Vector, *,
                       offset: float) -> Vector:  # pragma: no cover
        pass

    @concrete_algorithm("testing.offset", compiler="identity_comp")
    def compiled_offset(a: NumpyVectorType, *,
                        offset: float) -> NumpyVectorType:  # pragma: no cover
        return a + offset

    registry = PluginRegistry("test_subgraphs_plugin")
    registry.register(testing_add)
    registry.register(compiled_add)
    registry.register(testing_scale)
    registry.register(compiled_scale)
    registry.register(testing_offset)
    registry.register(compiled_offset)

    resolver = Resolver()
    # NumbaCompiler will be picked up from environment
    resolver.load_plugins_from_environment()
    resolver.register(registry.plugins)

    return resolver
コード例 #13
0
ファイル: __init__.py プロジェクト: eriknw/metagraph-1
    def __init__(self, resolver: Resolver, algo: Union[Dispatcher, AnyStr],
                 *args, **kwargs):
        """
        Object which calls `algo` for each concrete type, translating inputs as needed.
        No work is actually performed until `.assert_equals(val)` is called.

        :param resolver: Resolver
        :param algo: abstract algorithm (resolver.algo.path.to.algo or 'path.to.algo')
        :param args: positional parameters passed to algo
        :param kwargs: keyword parameters passed to algo
        """
        if type(algo) is Dispatcher:
            algo = algo._algo_name

        self.resolver = resolver
        self.algo = algo
        self._args = args
        self._kwargs = kwargs

        all_concrete_algos = set(resolver.concrete_algorithms[algo])
        plans = resolver.find_algorithm_solutions(algo, *args, **kwargs)
        # Check if any concrete algorithm failed to find a valid plan
        for plan in plans:
            all_concrete_algos.remove(plan.algo)
        if all_concrete_algos:
            missing_algos = [
                f"{algo.func.__module__}.{algo.func.__qualname__}"
                for algo in all_concrete_algos
            ]
            if len(missing_algos) == 1:
                missing_algos = missing_algos[0]
            else:
                missing_algos = f"[{', '.join(missing_algos)}]"

            raise UnsatisfiableAlgorithmError(
                f"No plan found for {missing_algos}")

        self.plans = plans
コード例 #14
0
def test_register_errors():
    res = Resolver()

    class Abstract1(AbstractType):
        pass

    class Concrete1(ConcreteType, abstract=Abstract1):
        pass

    registry = PluginRegistry("test_register_errors_default_plugin")
    registry.register(Concrete1)
    with pytest.raises(ValueError, match="unregistered abstract"):
        res.register(registry.plugins)

    # forgetting to set abstract attribute is tested in test_plugins now

    class Abstract2(AbstractType):
        pass

    class Concrete2(ConcreteType, abstract=Abstract2):
        pass

    @translator
    def c1_to_c2(src: Concrete1, **props) -> Concrete2:  # pragma: no cover
        pass

    @translator
    def c2_to_c1(src: Concrete2, **props) -> Concrete1:  # pragma: no cover
        pass

    registry.register(c1_to_c2)
    with pytest.raises(ValueError, match="has unregistered abstract type "):
        res.register(registry.plugins)

    registry.register(Abstract1)
    with pytest.raises(
            ValueError,
            match="translator destination type .* has not been registered"):
        res.register(registry.plugins)
    with pytest.raises(
            ValueError,
            match="translator source type .* has not been registered"):
        res.register({
            "test_register_errors_default_plugin": {
                "translators": {c2_to_c1}
            }
        })

    # Fresh start
    res = Resolver()

    registry.register(Abstract2)
    registry.register(Concrete2)
    with pytest.raises(ValueError, match="convert between concrete types"):
        res.register(registry.plugins)

    @abstract_algorithm("testing.myalgo")
    def my_algo(a: Abstract1) -> Abstract2:  # pragma: no cover
        pass

    @abstract_algorithm("testing.myalgo")
    def my_algo2(a: Abstract1) -> Abstract2:  # pragma: no cover
        pass

    my_algo_registry = PluginRegistry("test_register_errors_default_plugin")
    my_algo_registry.register(my_algo)
    res.register(my_algo_registry.plugins)
    with pytest.raises(ValueError, match="already exists"):
        my_algo2_registry = PluginRegistry(
            "test_register_errors_default_plugin")
        my_algo2_registry.register(my_algo2)
        res.register(my_algo2_registry.plugins)

    @abstract_algorithm("testing.bad_input_type")
    def my_algo_bad_list_input_type(a: List) -> int:  # pragma: no cover
        pass

    @abstract_algorithm("testing.bad_output_type")
    def my_algo_bad_output_type(a: Abstract1) -> res:  # pragma: no cover
        pass

    # @abstract_algorithm("testing.bad_compound_output_type")
    # def my_algo_bad_compound_list_output_type(
    #     a: Abstract1,
    # ) -> Tuple[List, List]:  # pragma: no cover
    #     pass

    with pytest.raises(TypeError,
                       match="Found an empty list of types for kind=List"):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(my_algo_bad_list_input_type)
        res_tmp = Resolver()
        res_tmp.register(registry.plugins)

    with pytest.raises(TypeError,
                       match="return type may not be an instance of type"):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(my_algo_bad_output_type)
        res_tmp = Resolver()
        res_tmp.register(registry.plugins)

    # with pytest.raises(
    #     TypeError,
    #     match="return type may not be an instance of type <class 'typing.TypeVar'>",
    # ):
    #     registry = PluginRegistry("test_register_errors_default_plugin")
    #     registry.register(my_algo_bad_compound_list_output_type)
    #     res_tmp = Resolver()
    #     res_tmp.register(registry.plugins)

    @abstract_algorithm("testing.bad_input_type")
    def my_algo_bad_dict_input_type(a: Dict) -> Resolver:  # pragma: no cover
        pass

    @abstract_algorithm("testing.bad_compound_output_type")
    def my_algo_bad_compound_dict_output_type(
        a: Abstract1, ) -> Tuple[Dict, Dict]:  # pragma: no cover
        pass

    with pytest.raises(TypeError, match="may not be typing.Dict"):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(my_algo_bad_dict_input_type)
        res_tmp = Resolver()
        res_tmp.register(registry.plugins)

    with pytest.raises(TypeError, match="may not be typing.Dict"):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(my_algo_bad_compound_dict_output_type)
        res_tmp = Resolver()
        res_tmp.register(registry.plugins)

    @concrete_algorithm("testing.does_not_exist")
    def my_algo3(a: Abstract1) -> Abstract2:  # pragma: no cover
        pass

    my_algo3_registry = PluginRegistry("test_register_errors_default_plugin")
    my_algo3_registry.register(my_algo3)
    with pytest.raises(ValueError, match="unregistered abstract"):
        res.register(my_algo3_registry.plugins)

    class Concrete3(ConcreteType, abstract=Abstract1):
        value_type = int

    class Concrete4(ConcreteType, abstract=Abstract1):
        value_type = int

    registry = PluginRegistry("test_register_errors_default_plugin")
    registry.register(Concrete3)
    registry.register(Concrete4)
    registry.register(Abstract1)
    with pytest.raises(ValueError, match=r"abstract type .+ already exists"):
        res.register(registry.plugins)

    @concrete_algorithm("testing.myalgo")
    def conc_algo_with_defaults(
            a: Concrete1 = 17) -> Concrete2:  # pragma: no cover
        return a

    with pytest.raises(TypeError,
                       match='argument "a" declares a default value'):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(conc_algo_with_defaults)
        res.register(registry.plugins)

    @concrete_algorithm("testing.myalgo", include_resolver=True)
    def conc_algo_with_resolver_default(a: Concrete1,
                                        *,
                                        resolver=14) -> Concrete2:
        return a

    with pytest.raises(TypeError, match='"resolver" cannot have a default'):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(conc_algo_with_resolver_default)
        res.register(registry.plugins)

    @abstract_algorithm("testing.multi_ret")
    def my_multi_ret_algo() -> Tuple[int, int, int]:  # pragma: no cover
        pass

    @concrete_algorithm("testing.multi_ret")
    def conc_algo_wrong_output_nums() -> Tuple[int, int]:  # pragma: no cover
        return (0, 100)

    with pytest.raises(
            TypeError,
            match=
            "return type is not compatible with abstract function signature",
    ):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(my_multi_ret_algo)
        registry.register(conc_algo_wrong_output_nums)
        res.register(registry.plugins)

    @abstract_algorithm("testing.any")
    def abstract_any(x: Any) -> int:  # pragma: no cover
        pass

    @concrete_algorithm("testing.any")
    def my_any(x: int) -> int:  # pragma: no cover
        return 12

    with pytest.raises(
            TypeError,
            match=
            'argument "x": .* does not match typing.Any in abstract signature',
    ):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(abstract_any)
        registry.register(my_any, "my_any_plugin")
        res.register(registry.plugins)

    # Don't allow concrete types in abstract algorithm signature

    @abstract_algorithm("testing.abst_algo_bad_return_type")
    def abst_algo_bad_return_type_1(x: int) -> Concrete1:  # pragma: no cover
        pass

    with pytest.raises(TypeError,
                       match=" may not have Concrete types in signature"):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(abst_algo_bad_return_type_1)
        res_tmp = Resolver()
        res_tmp.register(registry.plugins)

    @abstract_algorithm("testing.abst_algo_bad_parameter_type")
    def abst_algo_bad_parameter_type_1(
            x: Concrete1) -> int:  # pragma: no cover
        pass

    with pytest.raises(TypeError,
                       match=" may not have Concrete types in signature"):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(abst_algo_bad_parameter_type_1)
        res_tmp = Resolver()
        res_tmp.register(registry.plugins)

    @abstract_algorithm("testing.abst_algo_bad_return_type")
    def abst_algo_bad_return_type_2(
            x: int) -> List[Concrete1]:  # pragma: no cover
        pass

    with pytest.raises(
            TypeError,
            match="return type may not have Concrete types in signature"):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(abst_algo_bad_return_type_2)
        res_tmp = Resolver()
        res_tmp.register(registry.plugins)

    @abstract_algorithm("testing.abst_algo_bad_parameter_type")
    def abst_algo_bad_parameter_type_2(
            x: List[Concrete1]) -> int:  # pragma: no cover
        pass

    with pytest.raises(
            TypeError,
            match='argument "x" may not have Concrete types in signature'):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(abst_algo_bad_parameter_type_2)
        res_tmp = Resolver()
        res_tmp.register(registry.plugins)

    @abstract_algorithm("testing.abst_algo_combo_combo_bad")
    def abst_algo_combo_combo_bad(x: Union[List[int], List[float]]) -> int:
        pass

    with pytest.raises(
            TypeError,
            match="Nesting a Combo type inside a Combo type is not allowed"):
        registry = PluginRegistry("test_register_errors_combo_combo")
        registry.register(abst_algo_combo_combo_bad)
        res_tmp = Resolver()
        res_tmp.register(registry.plugins)

    @abstract_algorithm("testing.abst_algo_combo_combo_good")
    def abst_algo_combo_combo_good(x: Optional[List[int]]) -> int:
        pass

    @concrete_algorithm("testing.abst_algo_combo_combo_good")
    def my_algo_combo_combo_good(x: Optional[List[int]]) -> int:
        return 12

    registry = PluginRegistry("test_register_good_combo_combo")
    registry.register(abst_algo_combo_combo_good)
    registry.register(my_algo_combo_combo_good)
    res_tmp = Resolver()
    res_tmp.register(registry.plugins)

    @concrete_algorithm("testing.abst_algo_combo_combo_good")
    def my_algo_combo_combo_not_so_good(
            x: Union[List[int], List[float]]) -> int:
        return 12

    with pytest.raises(
            TypeError,
            match="Nesting a Combo type inside a Combo type is not allowed"):
        registry = PluginRegistry("test_register_good_combo_combo")
        registry.register(abst_algo_combo_combo_good)
        registry.register(my_algo_combo_combo_not_so_good)
        res_tmp = Resolver()
        res_tmp.register(registry.plugins)

    # Return type cannot be a Combo
    @abstract_algorithm("testing.combos")
    def abst_combos(x: Union[int, float]) -> int:
        pass

    @concrete_algorithm("testing.combos")
    def conc_combos(x: Union[int, float]) -> Union[int, float]:
        return x

    with pytest.raises(TypeError, match="return type may not be a Combo"):
        registry = PluginRegistry("test_register_combos")
        registry.register(abst_combos)
        registry.register(conc_combos)
        res_tmp = Resolver()
        res_tmp.register(registry.plugins)

    # Optional flag of a Combo must match what the abstract definition
    @concrete_algorithm("testing.combos")
    def conc_combos2(x: Optional[Union[int, float]]) -> int:
        return x

    with pytest.raises(TypeError, match="does not match optional flag in"):
        registry = PluginRegistry("test_register_combos")
        registry.register(abst_combos)
        registry.register(conc_combos2)
        res_tmp = Resolver()
        res_tmp.register(registry.plugins)

    # Subtypes of a Combo must match those in the abstract definition
    @concrete_algorithm("testing.combos")
    def conc_combos3(x: Union[complex, str]) -> int:
        return x

    with pytest.raises(TypeError, match="not found in mg.Union"):
        registry = PluginRegistry("test_register_combos")
        registry.register(abst_combos)
        registry.register(conc_combos3)
        res_tmp = Resolver()
        res_tmp.register(registry.plugins)
コード例 #15
0
ファイル: test_resolver.py プロジェクト: eriknw/metagraph-1
def test_register_errors():
    res = Resolver()

    class Abstract1(AbstractType):
        pass

    class Concrete1(ConcreteType, abstract=Abstract1):
        pass

    registry = PluginRegistry("test_register_errors_default_plugin")
    registry.register(Concrete1)
    with pytest.raises(ValueError, match="unregistered abstract"):
        res.register(registry.plugins)

    # forgetting to set abstract attribute is tested in test_plugins now

    class Abstract2(AbstractType):
        pass

    class Concrete2(ConcreteType, abstract=Abstract2):
        pass

    @translator
    def c1_to_c2(src: Concrete1, **props) -> Concrete2:  # pragma: no cover
        pass

    registry.register(c1_to_c2)
    with pytest.raises(ValueError, match="has unregistered abstract type "):
        res.register(registry.plugins)

    registry.register(Abstract1)
    with pytest.raises(
        ValueError, match="translator destination type .* has not been registered"
    ):
        res.register(registry.plugins)

    # Fresh start -- too much baggage of things partially registered above
    res = Resolver()

    registry.register(Abstract2)
    registry.register(Concrete2)
    with pytest.raises(ValueError, match="convert between concrete types"):
        res.register(registry.plugins)

    @abstract_algorithm("testing.myalgo")
    def my_algo(a: Abstract1) -> Abstract2:  # pragma: no cover
        pass

    @abstract_algorithm("testing.myalgo")
    def my_algo2(a: Abstract1) -> Abstract2:  # pragma: no cover
        pass

    my_algo_registry = PluginRegistry("test_register_errors_default_plugin")
    my_algo_registry.register(my_algo)
    res.register(my_algo_registry.plugins)
    with pytest.raises(ValueError, match="already exists"):
        my_algo2_registry = PluginRegistry("test_register_errors_default_plugin")
        my_algo2_registry.register(my_algo2)
        res.register(my_algo2_registry.plugins)

    @abstract_algorithm("testing.bad_input_type")
    def my_algo_bad_input_type(a: List) -> Resolver:  # pragma: no cover
        pass

    @abstract_algorithm("testing.bad_output_type")
    def my_algo_bad_output_type(a: Abstract1) -> res:  # pragma: no cover
        pass

    @abstract_algorithm("testing.bad_compound_output_type")
    def my_algo_bad_compound_output_type(
        a: Abstract1,
    ) -> Tuple[List, List]:  # pragma: no cover
        pass

    with pytest.raises(TypeError, match='argument "a" may not be typing.List'):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(my_algo_bad_input_type)
        res_tmp = Resolver()
        res_tmp.register(registry.plugins)

    with pytest.raises(TypeError, match="return type may not be an instance of"):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(my_algo_bad_output_type)
        res_tmp = Resolver()
        res_tmp.register(registry.plugins)

    with pytest.raises(TypeError, match="return type may not be typing.List"):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(my_algo_bad_compound_output_type)
        res_tmp = Resolver()
        res_tmp.register(registry.plugins)

    @concrete_algorithm("testing.does_not_exist")
    def my_algo3(a: Abstract1) -> Abstract2:  # pragma: no cover
        pass

    my_algo3_registry = PluginRegistry("test_register_errors_default_plugin")
    my_algo3_registry.register(my_algo3)
    with pytest.raises(ValueError, match="unregistered abstract"):
        res.register(my_algo3_registry.plugins)

    class Concrete3(ConcreteType, abstract=Abstract1):
        value_type = int

    class Concrete4(ConcreteType, abstract=Abstract1):
        value_type = int

    registry = PluginRegistry("test_register_errors_default_plugin")
    registry.register(Concrete3)
    registry.register(Concrete4)
    registry.register(Abstract1)
    with pytest.raises(ValueError, match=r"abstract type .+ already exists"):
        res.register(registry.plugins)

    @concrete_algorithm("testing.myalgo")
    def conc_algo_with_defaults(a: Concrete1 = 17) -> Concrete2:  # pragma: no cover
        return a

    with pytest.raises(TypeError, match='argument "a" declares a default value'):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(conc_algo_with_defaults)
        res.register(registry.plugins)

    @abstract_algorithm("testing.multi_ret")
    def my_multi_ret_algo() -> Tuple[int, int, int]:  # pragma: no cover
        pass

    @concrete_algorithm("testing.multi_ret")
    def conc_algo_wrong_output_nums() -> Tuple[int, int]:  # pragma: no cover
        return (0, 100)

    with pytest.raises(
        TypeError,
        match="return type is not compatible with abstract function signature",
    ):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(my_multi_ret_algo)
        registry.register(conc_algo_wrong_output_nums)
        res.register(registry.plugins)

    @abstract_algorithm("testing.any")
    def abstract_any(x: Any) -> int:  # pragma: no cover
        pass

    @concrete_algorithm("testing.any")
    def my_any(x: int) -> int:  # pragma: no cover
        return 12

    with pytest.raises(
        TypeError, match='argument "x" does not match abstract function signature'
    ):
        registry = PluginRegistry("test_register_errors_default_plugin")
        registry.register(abstract_any)
        registry.register(my_any, "my_any_plugin")
        res.register(registry.plugins)
コード例 #16
0
def test_duplicate_plugin():
    class AbstractType1(AbstractType):
        pass

    class AbstractType2(AbstractType):
        pass

    class ConcreteType1(ConcreteType, abstract=AbstractType1):
        value_type = int
        pass

    class ConcreteType2(ConcreteType, abstract=AbstractType2):
        value_type = int
        pass

    @abstract_algorithm("test_duplicate_plugin.test_abstract_algo")
    def abstract_algo1(input_int: int):
        pass  # pragma: no cover

    @concrete_algorithm("test_duplicate_plugin.test_abstract_algo")
    def concrete_algo1(input_int: int):
        pass  # pragma: no cover

    @concrete_algorithm("test_duplicate_plugin.test_abstract_algo")
    def concrete_algo2(input_int: int):
        pass  # pragma: no cover

    res = Resolver()
    with pytest.raises(
            ValueError,
            match="Multiple concrete algorithms for abstract algorithm"):
        res.register({
            "bad_many_conc_algo_to_one_abstract_algo": {
                "abstract_algorithms": {abstract_algo1},
                "concrete_algorithms": {concrete_algo1, concrete_algo2},
            }
        })

    res = Resolver()
    res.register(({
        "bad_many_value_type_to_concrete": {
            "abstract_types": {AbstractType1, AbstractType2},
            "concrete_types": {ConcreteType1},
        }
    }))
    with pytest.raises(
            ValueError,
            match=
            "Python class '<class 'int'>' already has a registered concrete type: ",
    ):
        res.register(({
            "bad_many_value_type_to_concrete": {
                "concrete_types": {ConcreteType2}
            }
        }))

    res = Resolver()
    res.register(
        {"test_duplicate_plugin": {
            "abstract_types": {AbstractType1}
        }})
    with pytest.raises(ValueError, match=" already registered."):
        res.register(
            {"test_duplicate_plugin": {
                "concrete_types": {ConcreteType1}
            }})

    with pytest.raises(ValueError,
                       match=" not known to be the resolver or a plugin."):
        _ResolverRegistrar.register_plugin_attributes_in_tree(
            Resolver(), Resolver(), abstract_types={AbstractType1})
コード例 #17
0
def test_algorithm_versions():
    @abstract_algorithm("test_algorithm_versions.test_abstract_algo")
    def abstract_algo1(input_int: int):
        pass  # pragma: no cover

    @abstract_algorithm("test_algorithm_versions.test_abstract_algo",
                        version=1)
    def abstract_algo2(input_int: int):
        pass  # pragma: no cover

    @concrete_algorithm("test_algorithm_versions.test_abstract_algo")
    def concrete_algo1(input_int: int):
        pass  # pragma: no cover

    @concrete_algorithm("test_algorithm_versions.test_abstract_algo",
                        version=1)
    def concrete_algo2(input_int: int):
        pass  # pragma: no cover

    # Sanity check
    res = Resolver()
    res.register({
        "test_algorithm_versions1": {
            "abstract_algorithms": {abstract_algo1, abstract_algo2},
            "concrete_algorithms": {concrete_algo1, concrete_algo2},
        }
    })
    assert (res.algos.test_algorithm_versions.test_abstract_algo.
            test_algorithm_versions1._algo.__name__ == "concrete_algo2")

    # Unknown concrete, raise
    with pytest.raises(ValueError, match="implements an unknown version"):
        with config.set({"core.algorithm.unknown_concrete_version": "raise"}):
            res = Resolver()
            res.register({
                "test_algorithm_versions1": {
                    "abstract_algorithms": {abstract_algo1},
                    "concrete_algorithms": {concrete_algo1, concrete_algo2},
                }
            })

    # Unknown concrete, use version 0
    with config.set({"core.algorithm.unknown_concrete_version": "ignore"}):
        res = Resolver()
        res.register({
            "test_algorithm_versions1": {
                "abstract_algorithms": {abstract_algo1},
                "concrete_algorithms": {concrete_algo1, concrete_algo2},
            }
        })
    assert (res.algos.test_algorithm_versions.test_abstract_algo.
            test_algorithm_versions1._algo.__name__ == "concrete_algo1")

    # Outdated concrete, raise
    with pytest.raises(
            ValueError,
            match="implements an outdated version of abstract algorithm"):
        with config.set({"core.algorithms.outdated_concrete_version":
                         "raise"}):
            res = Resolver()
            res.register({
                "test_algorithm_versions1": {
                    "abstract_algorithms": {abstract_algo1, abstract_algo2},
                    "concrete_algorithms": {concrete_algo1},
                }
            })

    # Outdated concrete, ignore b/c/ not the latest
    with config.set({"core.algorithms.outdated_concrete_version": None}):
        res = Resolver()
        res.register({
            "test_algorithm_versions1": {
                "abstract_algorithms": {abstract_algo1, abstract_algo2},
                "concrete_algorithms": {concrete_algo1},
            }
        })
    assert not hasattr(res.algos.test_algorithm_versions.test_abstract_algo,
                       "test_algorithm_versions1")

    # Outdated concrete, warn
    with pytest.warns(
            AlgorithmWarning,
            match="implements an outdated version of abstract algorithm"):
        with config.set({"core.algorithms.outdated_concrete_version": "warn"}):
            res = Resolver()
            res.register({
                "test_algorithm_versions1": {
                    "abstract_algorithms": {abstract_algo1, abstract_algo2},
                    "concrete_algorithms": {concrete_algo1},
                }
            })