Example #1
0
def test_register_union_rules(bc_builder: BuildConfiguration.Builder) -> None:
    @union
    class Base:
        pass

    class A:
        pass

    class B:
        pass

    union_a = UnionRule(Base, A)
    union_b = UnionRule(Base, B)
    bc_builder.register_rules([union_a])
    bc_builder.register_rules([union_b])
    assert bc_builder.create().union_rules == FrozenOrderedSet(
        [union_a, union_b])
Example #2
0
def load_backend(build_configuration: BuildConfiguration.Builder, backend_package: str) -> None:
    """Installs the given backend package into the build configuration.

    :param build_configuration: the BuildConfiguration to install the backend plugin into.
    :param backend_package: the package name containing the backend plugin register module that
      provides the plugin entrypoints.
    :raises: :class:``pants.base.exceptions.BuildConfigurationError`` if there is a problem loading
      the build configuration.
    """
    backend_module = backend_package + ".register"
    try:
        module = importlib.import_module(backend_module)
    except ImportError as ex:
        traceback.print_exc()
        raise BackendConfigurationError(f"Failed to load the {backend_module} backend: {ex!r}")

    def invoke_entrypoint(name):
        entrypoint = getattr(module, name, lambda: None)
        try:
            return entrypoint()
        except TypeError as e:
            traceback.print_exc()
            raise BackendConfigurationError(
                f"Entrypoint {name} in {backend_module} must be a zero-arg callable: {e!r}"
            )

    target_types = invoke_entrypoint("target_types")
    if target_types:
        build_configuration.register_target_types(backend_package, target_types)
    build_file_aliases = invoke_entrypoint("build_file_aliases")
    if build_file_aliases:
        build_configuration.register_aliases(build_file_aliases)
    rules = invoke_entrypoint("rules")
    if rules:
        build_configuration.register_rules(backend_package, rules)
Example #3
0
def test_validation(caplog, bc_builder: BuildConfiguration.Builder) -> None:
    def mk_dummy_opt(_options_scope: str,
                     goal: bool = False) -> Type[Optionable]:
        class DummyOptionable(GoalSubsystem if goal else Subsystem
                              ):  # type: ignore[misc]
            options_scope = _options_scope

        return DummyOptionable

    def mk_dummy_tgt(_alias: str) -> Type[Target]:
        class DummyTarget(Target):
            alias = _alias
            core_fields = tuple()  # type: ignore[var-annotated]

        return DummyTarget

    bc_builder.register_optionables((
        mk_dummy_opt("foo"),
        mk_dummy_opt("Bar-bar"),
        mk_dummy_opt("baz"),
        mk_dummy_opt("qux", goal=True),
        mk_dummy_opt("global"),
    ))
    bc_builder.register_target_types(
        (mk_dummy_tgt("bar_bar"), mk_dummy_tgt("qux"), mk_dummy_tgt("global")))
    with pytest.raises(TypeError) as e:
        bc_builder.create()
    assert (
        "Naming collision: `Bar-bar`/`bar_bar` is registered as a subsystem and a "
        "target type." in caplog.text)
    assert "Naming collision: `qux` is registered as a goal and a target type." in caplog.text
    assert (
        "Naming collision: `global` is registered as a reserved name, a subsystem "
        "and a target type." in caplog.text)
    assert "Found naming collisions" in str(e)
def test_register_subsystems(bc_builder: BuildConfiguration.Builder) -> None:
    def mk_dummy_subsys(_options_scope: str) -> Type[Subsystem]:
        class DummySubsystem(Subsystem):
            options_scope = _options_scope

        return DummySubsystem

    foo = mk_dummy_subsys("foo")
    bar = mk_dummy_subsys("bar")
    baz = mk_dummy_subsys("baz")
    bc_builder.register_subsystems("backend1", [foo, bar])
    bc_builder.register_subsystems("backend2", [bar, baz])
    bc_builder.register_subsystems("backend3", [baz])
    bc = bc_builder.create()

    assert bc.subsystem_to_providers == FrozenDict({
        foo: ("backend1", ),
        bar: ("backend1", "backend2"),
        baz: (
            "backend2",
            "backend3",
        ),
    })
def test_register_target_types(bc_builder: BuildConfiguration.Builder) -> None:
    def mk_dummy_tgt(_alias: str) -> Type[Target]:
        class DummyTarget(Target):
            alias = _alias
            core_fields = tuple()  # type: ignore[var-annotated]

        return DummyTarget

    foo = mk_dummy_tgt("foo")
    bar = mk_dummy_tgt("bar")
    baz = mk_dummy_tgt("baz")
    bc_builder.register_target_types("backend1", [foo, bar])
    bc_builder.register_target_types("backend2", [bar, baz])
    bc_builder.register_target_types("backend3", [baz])
    bc = bc_builder.create()

    assert bc.target_type_to_providers == FrozenDict({
        foo: ("backend1", ),
        bar: ("backend1", "backend2"),
        baz: (
            "backend2",
            "backend3",
        ),
    })
Example #6
0
def load_plugins(
    build_configuration: BuildConfiguration.Builder,
    plugins: List[str],
    working_set: WorkingSet,
) -> None:
    """Load named plugins from the current working_set into the supplied build_configuration.

    "Loading" a plugin here refers to calling registration methods -- it is assumed each plugin
    is already on the path and an error will be thrown if it is not. Plugins should define their
    entrypoints in the `pantsbuild.plugin` group when configuring their distribution.

    Like source backends, the `build_file_aliases`, and `register_goals` methods are called if
    those entry points are defined.

    * Plugins are loaded in the order they are provided. *

    This is important as loading can add, remove or replace existing tasks installed by other plugins.

    If a plugin needs to assert that another plugin is registered before it, it can define an
    entrypoint "load_after" which can return a list of plugins which must have been loaded before it
    can be loaded. This does not change the order or what plugins are loaded in any way -- it is
    purely an assertion to guard against misconfiguration.

    :param build_configuration: The BuildConfiguration (for adding aliases).
    :param plugins: A list of plugin names optionally with versions, in requirement format.
                              eg ['widgetpublish', 'widgetgen==1.2'].
    :param working_set: A pkg_resources.WorkingSet to load plugins from.
    """
    loaded: Dict = {}
    for plugin in plugins or []:
        req = Requirement.parse(plugin)
        dist = working_set.find(req)

        if not dist:
            raise PluginNotFound(f"Could not find plugin: {req}")

        entries = dist.get_entry_map().get("pantsbuild.plugin", {})

        if "load_after" in entries:
            deps = entries["load_after"].load()()
            for dep_name in deps:
                dep = Requirement.parse(dep_name)
                if dep.key not in loaded:
                    raise PluginLoadOrderError(
                        f"Plugin {plugin} must be loaded after {dep}")
        if "target_types" in entries:
            target_types = entries["target_types"].load()()
            build_configuration.register_target_types(target_types)
        if "build_file_aliases" in entries:
            aliases = entries["build_file_aliases"].load()()
            build_configuration.register_aliases(aliases)
        if "rules" in entries:
            rules = entries["rules"].load()()
            build_configuration.register_rules(rules)
        loaded[dist.as_requirement().key] = dist
Example #7
0
def test_register_exposed_context_aware_object_factory(
    bc_builder: BuildConfiguration.Builder, ) -> None:
    def caof_function(parse_context):
        return parse_context.rel_path

    class CaofClass:
        def __init__(self, parse_context):
            self._parse_context = parse_context

        def __call__(self):
            return self._parse_context.rel_path

    _register_aliases(bc_builder,
                      context_aware_object_factories={
                          "func": caof_function,
                          "cls": CaofClass
                      })
    aliases = bc_builder.create().registered_aliases
    assert FrozenDict() == aliases.objects
    assert (FrozenDict({
        "func": caof_function,
        "cls": CaofClass
    }) == aliases.context_aware_object_factories)
Example #8
0
def register_builtin_goals(
        build_configuration: BuildConfiguration.Builder) -> None:
    build_configuration.register_subsystems("pants.goal", builtin_goals())
Example #9
0
def test_register_exposed_object(
        bc_builder: BuildConfiguration.Builder) -> None:
    _register_aliases(bc_builder, objects={"jane": 42})
    aliases = bc_builder.create().registered_aliases
    assert FrozenDict() == aliases.context_aware_object_factories
    assert FrozenDict(jane=42) == aliases.objects
Example #10
0
def test_register_bad(bc_builder: BuildConfiguration.Builder) -> None:
    with pytest.raises(TypeError):
        bc_builder.register_aliases(42)