Beispiel #1
0
def test_get_field() -> None:
    extensions = ("FortranExt1",)
    tgt = FortranTarget({FortranExtensions.alias: extensions}, Address("", target_name="lib"))

    assert tgt[FortranExtensions].value == extensions
    assert tgt.get(FortranExtensions).value == extensions
    assert tgt.get(FortranExtensions, default_raw_value=["FortranExt2"]).value == extensions

    # Default field value. This happens when the field is registered on the target type, but the
    # user does not explicitly set the field in the BUILD file.
    default_field_tgt = FortranTarget({}, Address("", target_name="default"))
    assert default_field_tgt[FortranExtensions].value == ()
    assert default_field_tgt.get(FortranExtensions).value == ()
    assert default_field_tgt.get(FortranExtensions, default_raw_value=["FortranExt2"]).value == ()
    # Example of a call site applying its own default value instead of the field's default value.
    assert default_field_tgt[FortranExtensions].value or 123 == 123

    assert (
        FortranTarget.class_get_field(FortranExtensions, union_membership=UnionMembership({}))
        is FortranExtensions
    )

    # Field is not registered on the target.
    with pytest.raises(KeyError) as exc:
        default_field_tgt[UnrelatedField]
    assert UnrelatedField.__name__ in str(exc)

    with pytest.raises(KeyError) as exc:
        FortranTarget.class_get_field(UnrelatedField, union_membership=UnionMembership({}))
    assert UnrelatedField.__name__ in str(exc)

    assert default_field_tgt.get(UnrelatedField).value == UnrelatedField.default
    assert default_field_tgt.get(
        UnrelatedField, default_raw_value=not UnrelatedField.default
    ).value == (not UnrelatedField.default)
Beispiel #2
0
def test_union_membership_from_rules() -> None:
    @union
    class Base:
        pass

    class A:
        pass

    class B:
        pass

    assert UnionMembership.from_rules([UnionRule(Base, A), UnionRule(Base, B)]) == UnionMembership(
        {Base: FrozenOrderedSet([A, B])}
    )
Beispiel #3
0
    def __init__(
        self,
        unhydrated_values: Dict[str, Any],
        *,
        address: Address,
        # NB: `union_membership` is only optional to facilitate tests. In production, we should
        # always provide this parameter. This should be safe to do because production code should
        # rarely directly instantiate Targets and should instead use the engine to request them.
        union_membership: Optional[UnionMembership] = None,
    ) -> None:
        self.address = address
        self.plugin_fields = self._find_plugin_fields(union_membership
                                                      or UnionMembership({}))

        field_values = {}
        aliases_to_field_types = {
            field_type.alias: field_type
            for field_type in self.field_types
        }
        for alias, value in unhydrated_values.items():
            if alias not in aliases_to_field_types:
                raise InvalidFieldException(
                    f"Unrecognized field `{alias}={value}` in target {address}. Valid fields for "
                    f"the target type `{self.alias}`: {sorted(aliases_to_field_types.keys())}.",
                )
            field_type = aliases_to_field_types[alias]
            field_values[field_type] = field_type(value, address=address)
        # For undefined fields, mark the raw value as None.
        for field_type in set(self.field_types) - set(field_values.keys()):
            field_values[field_type] = field_type(raw_value=None,
                                                  address=address)
        self.field_values = FrozenDict(field_values)
Beispiel #4
0
def test_determine_renamed_fields() -> None:
    class DeprecatedField(StringField):
        alias = "new_name"
        deprecated_alias = "old_name"
        deprecated_alias_removal_version = "99.9.0.dev0"

    class OkayField(StringField):
        alias = "okay"

    class Tgt(Target):
        alias = "tgt"
        core_fields = (DeprecatedField, OkayField)
        deprecated_alias = "deprecated_tgt"
        deprecated_alias_removal_version = "99.9.0.dev0"

    class TgtGenerator(TargetGenerator):
        alias = "generator"
        core_fields = ()
        moved_fields = (DeprecatedField, OkayField)

    registered_targets = RegisteredTargetTypes.create([Tgt, TgtGenerator])
    result = determine_renamed_field_types(registered_targets,
                                           UnionMembership({}))
    deprecated_fields = FrozenDict(
        {DeprecatedField.deprecated_alias: DeprecatedField.alias})
    assert result.target_field_renames == FrozenDict({
        k: deprecated_fields
        for k in (TgtGenerator.alias, Tgt.alias, Tgt.deprecated_alias)
    })
Beispiel #5
0
def test_list_single() -> None:
    class CustomField(BoolField):
        """My custom field!

        Use this field to...
        """

        alias = "custom_field"
        required = True

    tests_target_stdout = run_goal(
        union_membership=UnionMembership.from_rules(
            [FortranTests.register_plugin_field(CustomField)]
        ),
        details_target=FortranTests.alias,
    )
    assert tests_target_stdout == dedent(
        """\

        fortran_tests
        -------------

        Tests for Fortran code.

        This assumes that you use the FRUIT test framework.

        Valid fields:

            custom_field
                type: bool, required
                My custom field! Use this field to...

            fortran_version
                type: str | None, default: None

            timeout
                type: int | None, default: None
                The number of seconds to run before timing out.
        """
    )

    binary_target_stdout = run_goal(details_target=FortranBinary.alias)
    assert binary_target_stdout == dedent(
        """\

        fortran_binary
        --------------

        Valid fields:

            archive_format
                type: '.tar' | '.tgz' | None, default: '.tgz'

            error_behavior
                type: 'error' | 'ignore' | 'warn', required

            fortran_version
                type: str | None, default: None
        """
    )
Beispiel #6
0
async def resources_bsp_target(
    request: ResourcesForOneBSPTargetRequest,
    union_membership: UnionMembership,
) -> BSPResourcesResult:
    targets = await Get(Targets, BSPBuildTargetInternal, request.bsp_target)
    resources_request_types: FrozenOrderedSet[Type[BSPResourcesRequest]] = union_membership.get(
        BSPResourcesRequest
    )
    field_sets_by_request_type: dict[Type[BSPResourcesRequest], set[FieldSet]] = defaultdict(set)
    for target in targets:
        for resources_request_type in resources_request_types:
            field_set_type = resources_request_type.field_set_type
            if field_set_type.is_applicable(target):
                field_set = field_set_type.create(target)
                field_sets_by_request_type[resources_request_type].add(field_set)

    resources_results = await MultiGet(
        Get(
            BSPResourcesResult,
            BSPResourcesRequest,
            resources_request_type(bsp_target=request.bsp_target, field_sets=tuple(field_sets)),
        )
        for resources_request_type, field_sets in field_sets_by_request_type.items()
    )

    resources = tuple(sorted({resource for rr in resources_results for resource in rr.resources}))

    output_digest = await Get(Digest, MergeDigests([rr.output_digest for rr in resources_results]))

    return BSPResourcesResult(
        resources=resources,
        output_digest=output_digest,
    )
Beispiel #7
0
    def for_targets(
        union_membership: UnionMembership, component: CoarsenedTarget, resolve: CoursierResolveKey
    ) -> ClasspathEntryRequest:
        """Constructs a subclass compatible with the members of the CoarsenedTarget."""
        compatible = []
        impls = union_membership.get(ClasspathEntryRequest)
        for impl in impls:
            if all(any(fs.is_applicable(t) for fs in impl.field_sets) for t in component.members):
                compatible.append(impl)

        if len(compatible) == 1:
            return compatible[0](component, resolve)

        impls_str = ", ".join(sorted(impl.__name__ for impl in impls))
        targets_str = "\n  ".join(
            sorted(f"{t.address.spec}\t({type(t).alias})" for t in component.members)
        )
        if compatible:
            raise ClasspathSourceAmbiguity(
                f"More than one JVM compiler instance ({impls_str}) was compatible with "
                f"the inputs:\n  {targets_str}"
            )
        else:
            raise ClasspathSourceMissing(
                f"No single JVM compiler instance (from: {impls_str}) was compatible with all of the "
                f"the inputs:\n  {targets_str}"
            )
Beispiel #8
0
async def resolve_bsp_build_target_addresses(
    bsp_target: BSPBuildTargetInternal,
    union_membership: UnionMembership,
) -> Targets:
    # NB: Using `RawSpecs` directly rather than `RawSpecsWithoutFileOwners` results in a rule graph cycle.
    targets = await Get(
        Targets,
        RawSpecsWithoutFileOwners,
        RawSpecsWithoutFileOwners.from_raw_specs(bsp_target.specs),
    )
    if bsp_target.definition.resolve_filter is None:
        return targets

    resolve_filter = bsp_target.definition.resolve_filter
    resolve_prefix, matched, resolve_value = resolve_filter.partition(":")
    if not resolve_prefix or not matched:
        raise ValueError(
            f"The `resolve` filter for `{bsp_target}` must have a platform or language specific "
            f"prefix like `$lang:$filter`, but the configured value: `{resolve_filter}` did not."
        )

    # TODO: See `BSPResolveFieldFactoryRequest` re: this awkwardness.
    factories = await MultiGet(
        Get(BSPResolveFieldFactoryResult, BSPResolveFieldFactoryRequest,
            request())
        for request in union_membership.get(BSPResolveFieldFactoryRequest)
        if request.resolve_prefix == resolve_prefix)

    return Targets(t for t in targets
                   if any((factory.resolve_field_value)(t) == resolve_value
                          for factory in factories))
Beispiel #9
0
 def run_typecheck_rule(
     *,
     request_types: List[Type[TypecheckRequest]],
     targets: List[TargetWithOrigin],
     include_sources: bool = True,
 ) -> Tuple[int, str]:
     console = MockConsole(use_colors=False)
     union_membership = UnionMembership({TypecheckRequest: request_types})
     result: Typecheck = run_rule(
         typecheck,
         rule_args=[console, TargetsWithOrigins(targets), union_membership],
         mock_gets=[
             MockGet(
                 product_type=TypecheckResults,
                 subject_type=TypecheckRequest,
                 mock=lambda field_set_collection: field_set_collection.typecheck_results,
             ),
             MockGet(
                 product_type=FieldSetsWithSources,
                 subject_type=FieldSetsWithSourcesRequest,
                 mock=lambda field_sets: FieldSetsWithSources(
                     field_sets if include_sources else ()
                 ),
             ),
         ],
         union_membership=union_membership,
     )
     assert not console.stdout.getvalue()
     return result.exit_code, console.stderr.getvalue()
Beispiel #10
0
def test_has_fields() -> None:
    empty_union_membership = UnionMembership({})
    tgt = FortranTarget({}, Address("", target_name="lib"))

    assert tgt.field_types == (FortranExtensions, FortranVersion)
    assert FortranTarget.class_field_types(
        union_membership=empty_union_membership) == (
            FortranExtensions,
            FortranVersion,
        )

    assert tgt.has_fields([]) is True
    assert FortranTarget.class_has_fields(
        [], union_membership=empty_union_membership) is True

    assert tgt.has_fields([FortranExtensions]) is True
    assert tgt.has_field(FortranExtensions) is True
    assert (FortranTarget.class_has_fields(
        [FortranExtensions], union_membership=empty_union_membership) is True)
    assert (FortranTarget.class_has_field(
        FortranExtensions, union_membership=empty_union_membership) is True)

    assert tgt.has_fields([UnrelatedField]) is False
    assert tgt.has_field(UnrelatedField) is False
    assert (FortranTarget.class_has_fields(
        [UnrelatedField], union_membership=empty_union_membership) is False)
    assert (FortranTarget.class_has_field(
        UnrelatedField, union_membership=empty_union_membership) is False)

    assert tgt.has_fields([FortranExtensions, UnrelatedField]) is False
    assert (FortranTarget.class_has_fields(
        [FortranExtensions, UnrelatedField],
        union_membership=empty_union_membership) is False)
Beispiel #11
0
async def merge_first_party_module_mappings(
    union_membership: UnionMembership,
) -> FirstPartyPythonModuleMapping:
    all_mappings = await MultiGet(
        Get(
            FirstPartyPythonMappingImpl,
            FirstPartyPythonMappingImplMarker,
            marker_cls(),
        )
        for marker_cls in union_membership.get(FirstPartyPythonMappingImplMarker)
    )
    resolves_to_modules_to_providers: DefaultDict[
        ResolveName, DefaultDict[str, list[ModuleProvider]]
    ] = defaultdict(lambda: defaultdict(list))
    for mapping_impl in all_mappings:
        for resolve, modules_to_providers in mapping_impl.items():
            for module, providers in modules_to_providers.items():
                resolves_to_modules_to_providers[resolve][module].extend(providers)
    return FirstPartyPythonModuleMapping(
        (
            resolve,
            FrozenDict(
                (mod, tuple(sorted(providers))) for mod, providers in sorted(mapping.items())
            ),
        )
        for resolve, mapping in sorted(resolves_to_modules_to_providers.items())
    )
Beispiel #12
0
    def _register_task(self, tasks, rule: TaskRule, union_membership: UnionMembership) -> None:
        """Register the given TaskRule with the native scheduler."""
        self._native.lib.tasks_task_begin(
            tasks,
            rule.func,
            rule.output_type,
            issubclass(rule.output_type, EngineAwareReturnType),
            rule.cacheable,
            rule.canonical_name,
            rule.desc or "",
            rule.level.level,
        )
        for selector in rule.input_selectors:
            self._native.lib.tasks_add_select(tasks, selector)

        def add_get_edge(product, subject):
            self._native.lib.tasks_add_get(tasks, product, subject)

        for the_get in rule.input_gets:
            if union.is_instance(the_get.input_type):
                # If the registered subject type is a union, add Get edges to all registered
                # union members.
                for union_member in union_membership.get(the_get.input_type):
                    add_get_edge(the_get.output_type, union_member)
            else:
                # Otherwise, the Get subject is a "concrete" type, so add a single Get edge.
                add_get_edge(the_get.output_type, the_get.input_type)

        self._native.lib.tasks_task_end(tasks)
Beispiel #13
0
    def mk_scheduler(
        self,
        rules,
        include_trace_on_error: bool = True,
    ) -> SchedulerSession:
        """Creates a SchedulerSession for a Scheduler with the given Rules installed."""
        work_dir = self._create_work_dir()

        build_root = os.path.join(work_dir, "build_root")
        os.makedirs(build_root)

        local_store_dir = os.path.realpath(safe_mkdtemp())
        local_execution_root_dir = os.path.realpath(safe_mkdtemp())
        named_caches_dir = os.path.realpath(safe_mkdtemp())
        scheduler = Scheduler(
            native=self._native,
            ignore_patterns=[],
            use_gitignore=False,
            build_root=build_root,
            local_store_dir=local_store_dir,
            local_execution_root_dir=local_execution_root_dir,
            named_caches_dir=named_caches_dir,
            ca_certs_path=None,
            rules=rules,
            union_membership=UnionMembership({}),
            executor=self._executor,
            execution_options=DEFAULT_EXECUTION_OPTIONS,
            include_trace_on_error=include_trace_on_error,
        )
        return scheduler.new_session(build_id="buildid_for_test", )
Beispiel #14
0
def target_types_to_generate_targets_requests(
    union_membership: UnionMembership,
) -> TargetTypesToGenerateTargetsRequests:
    return TargetTypesToGenerateTargetsRequests({
        request_cls.generate_from: request_cls  # type: ignore[misc]
        for request_cls in union_membership.get(GenerateTargetsRequest)
    })
Beispiel #15
0
def test_targets_with_sources_types() -> None:
    class Sources1(Sources):
        pass

    class Sources2(Sources):
        pass

    class CodegenSources(Sources):
        pass

    class Tgt1(Target):
        alias = "tgt1"
        core_fields = (Sources1,)

    class Tgt2(Target):
        alias = "tgt2"
        core_fields = (Sources2,)

    class CodegenTgt(Target):
        alias = "codegen_tgt"
        core_fields = (CodegenSources,)

    class GenSources(GenerateSourcesRequest):
        input = CodegenSources
        output = Sources1

    tgt1 = Tgt1({}, address=Address("tgt1"))
    tgt2 = Tgt2({}, address=Address("tgt2"))
    codegen_tgt = CodegenTgt({}, address=Address("codegen_tgt"))
    result = targets_with_sources_types(
        [Sources1],
        [tgt1, tgt2, codegen_tgt],
        union_membership=UnionMembership({GenerateSourcesRequest: [GenSources]}),
    )
    assert set(result) == {tgt1, codegen_tgt}
Beispiel #16
0
def run_typecheck_rule(
    *,
    request_types: Sequence[Type[CheckRequest]],
    targets: list[Target],
    only: list[str] | None = None,
) -> Tuple[int, str]:
    union_membership = UnionMembership({CheckRequest: request_types})
    check_subsystem = create_subsystem(CheckSubsystem, only=only or [])
    with mock_console(create_options_bootstrapper()) as (console, stdio_reader):
        rule_runner = RuleRunner()
        result: Check = run_rule_with_mocks(
            check,
            rule_args=[
                console,
                Workspace(rule_runner.scheduler, _enforce_effects=False),
                Targets(targets),
                DistDir(relpath=Path("dist")),
                union_membership,
                check_subsystem,
            ],
            mock_gets=[
                MockGet(
                    output_type=CheckResults,
                    input_type=CheckRequest,
                    mock=lambda field_set_collection: field_set_collection.check_results,
                ),
            ],
            union_membership=union_membership,
        )
        assert not stdio_reader.get_stdout()
        return result.exit_code, stdio_reader.get_stderr()
Beispiel #17
0
async def export(
    console: Console,
    targets: Targets,
    export_subsystem: ExportSubsystem,
    workspace: Workspace,
    union_membership: UnionMembership,
    build_root: BuildRoot,
    dist_dir: DistDir,
) -> Export:
    request_types = cast("Iterable[type[ExportableDataRequest]]",
                         union_membership.get(ExportableDataRequest))
    requests = tuple(request_type(targets) for request_type in request_types)
    exportables = await MultiGet(
        Get(ExportableData, ExportableDataRequest, request)
        for request in requests)
    prefixed_digests = await MultiGet(
        Get(Digest, AddPrefix(exp.digest, exp.reldir)) for exp in exportables)
    output_dir = os.path.join(str(dist_dir.relpath), "export")
    merged_digest = await Get(Digest, MergeDigests(prefixed_digests))
    dist_digest = await Get(Digest, AddPrefix(merged_digest, output_dir))
    workspace.write_digest(dist_digest)
    for exp in exportables:
        for symlink in exp.symlinks:
            # Note that if symlink.source_path is an abspath, join returns it unchanged.
            source_abspath = os.path.join(build_root.path, symlink.source_path)
            link_abspath = os.path.abspath(
                os.path.join(output_dir, exp.reldir, symlink.link_rel_path))
            absolute_symlink(source_abspath, link_abspath)
        console.print_stdout(
            f"Wrote {exp.description} to {os.path.join(output_dir, exp.reldir)}"
        )
    return Export(exit_code=0)
Beispiel #18
0
 def run_lint_rule(
     *,
     config_collection_types: List[Type[LinterConfigurations]],
     targets: List[TargetWithOrigin],
     per_target_caching: bool,
     include_sources: bool = True,
 ) -> Tuple[int, str]:
     console = MockConsole(use_colors=False)
     union_membership = UnionMembership({LinterConfigurations: config_collection_types})
     result: Lint = run_rule(
         lint,
         rule_args=[
             console,
             TargetsWithOrigins(targets),
             create_goal_subsystem(LintOptions, per_target_caching=per_target_caching),
             union_membership,
         ],
         mock_gets=[
             MockGet(
                 product_type=LintResult,
                 subject_type=LinterConfigurations,
                 mock=lambda config_collection: config_collection.lint_result,
             ),
             MockGet(
                 product_type=ConfigurationsWithSources,
                 subject_type=ConfigurationsWithSourcesRequest,
                 mock=lambda configs: ConfigurationsWithSources(
                     configs if include_sources else ()
                 ),
             ),
         ],
         union_membership=union_membership,
     )
     return result.exit_code, console.stdout.getvalue()
Beispiel #19
0
async def determine_setup_kwargs(
        exported_target: ExportedTarget,
        union_membership: UnionMembership) -> SetupKwargs:
    target = exported_target.target
    setup_kwargs_requests = union_membership.get(
        SetupKwargsRequest)  # type: ignore[misc]
    applicable_setup_kwargs_requests = tuple(
        request for request in setup_kwargs_requests
        if request.is_applicable(target))

    # If no provided implementations, fall back to our default implementation that simply returns
    # what the user explicitly specified in the BUILD file.
    if not applicable_setup_kwargs_requests:
        return SetupKwargs(exported_target.provides.kwargs,
                           address=target.address)

    if len(applicable_setup_kwargs_requests) > 1:
        possible_requests = sorted(
            plugin.__name__ for plugin in applicable_setup_kwargs_requests)
        raise ValueError(
            f"Multiple of the registered `SetupKwargsRequest`s can work on the target "
            f"{target.address}, and it's ambiguous which to use: {possible_requests}\n\nPlease "
            "activate fewer implementations, or make the classmethod `is_applicable()` more "
            "precise so that only one implementation is applicable for this target."
        )
    setup_kwargs_request = tuple(applicable_setup_kwargs_requests)[0]
    return await Get(SetupKwargs, SetupKwargsRequest,
                     setup_kwargs_request(target))
Beispiel #20
0
def run_typecheck_rule(
    *,
    request_types: List[Type[TypecheckRequest]],
    targets: List[Target],
    include_sources: bool = True,
) -> Tuple[int, str]:
    union_membership = UnionMembership({TypecheckRequest: request_types})
    with mock_console(create_options_bootstrapper()) as (console,
                                                         stdio_reader):
        result: Typecheck = run_rule_with_mocks(
            typecheck,
            rule_args=[console, Targets(targets), union_membership],
            mock_gets=[
                MockGet(
                    output_type=EnrichedTypecheckResults,
                    input_type=TypecheckRequest,
                    mock=lambda field_set_collection: field_set_collection.
                    typecheck_results,
                ),
                MockGet(
                    output_type=FieldSetsWithSources,
                    input_type=FieldSetsWithSourcesRequest,
                    mock=lambda field_sets: FieldSetsWithSources(
                        field_sets if include_sources else ()),
                ),
            ],
            union_membership=union_membership,
        )
        assert not stdio_reader.get_stdout()
        return result.exit_code, stdio_reader.get_stderr()
Beispiel #21
0
    def create(
        cls,
        env: Mapping[str, str],
        options_bootstrapper: OptionsBootstrapper,
        scheduler: Optional[LegacyGraphScheduler] = None,
    ) -> "LocalPantsRunner":
        """Creates a new LocalPantsRunner instance by parsing options.

        By the time this method runs, logging will already have been initialized in either
        PantsRunner or DaemonPantsRunner.

        :param env: The environment (e.g. os.environ) for this run.
        :param options_bootstrapper: The OptionsBootstrapper instance to reuse.
        :param scheduler: If being called from the daemon, a warmed scheduler to use.
        """
        build_root = get_buildroot()
        global_bootstrap_options = options_bootstrapper.bootstrap_options.for_global_scope(
        )
        options, build_config = LocalPantsRunner.parse_options(
            options_bootstrapper)

        # Option values are usually computed lazily on demand,
        # but command line options are eagerly computed for validation.
        for scope in options.scope_to_flags.keys():
            options.for_scope(scope)

        # Verify configs.
        if global_bootstrap_options.verify_config:
            options.verify_configs(options_bootstrapper.config)

        union_membership = UnionMembership(build_config.union_rules())

        # If we're running with the daemon, we'll be handed a warmed Scheduler, which we use
        # to initialize a session here.
        graph_session = cls._init_graph_session(options_bootstrapper,
                                                build_config, options,
                                                scheduler)

        global_options = options.for_global_scope()
        specs = SpecsCalculator.create(
            options=options,
            build_root=build_root,
            session=graph_session.scheduler_session,
            exclude_patterns=tuple(global_options.exclude_target_regexp),
            tags=tuple(global_options.tag),
        )

        profile_path = env.get("PANTS_PROFILE")

        return cls(
            build_root=build_root,
            options=options,
            options_bootstrapper=options_bootstrapper,
            build_config=build_config,
            specs=specs,
            graph_session=graph_session,
            union_membership=union_membership,
            profile_path=profile_path,
            _run_tracker=RunTracker.global_instance(),
        )
    def mk_scheduler(
        self,
        tmp_path: Path,
        rules,
        include_trace_on_error: bool = True,
        max_workunit_verbosity: LogLevel = LogLevel.DEBUG,
    ) -> SchedulerSession:
        """Creates a SchedulerSession for a Scheduler with the given Rules installed."""

        build_root = tmp_path / "build_root"
        build_root.mkdir(parents=True, exist_ok=True)

        local_execution_root_dir = os.path.realpath(safe_mkdtemp())
        named_caches_dir = os.path.realpath(safe_mkdtemp())
        scheduler = Scheduler(
            ignore_patterns=[],
            use_gitignore=False,
            build_root=build_root.as_posix(),
            local_execution_root_dir=local_execution_root_dir,
            named_caches_dir=named_caches_dir,
            ca_certs_path=None,
            rules=rules,
            union_membership=UnionMembership({}),
            executor=self._executor,
            execution_options=DEFAULT_EXECUTION_OPTIONS,
            local_store_options=DEFAULT_LOCAL_STORE_OPTIONS,
            include_trace_on_error=include_trace_on_error,
        )
        return scheduler.new_session(
            build_id="buildid_for_test",
            max_workunit_level=max_workunit_verbosity,
        )
Beispiel #23
0
def run_lint_rule(
    rule_runner: RuleRunner,
    *,
    lint_request_types: List[Type[LintRequest]],
    targets: List[Target],
    per_file_caching: bool,
) -> Tuple[int, str]:
    with mock_console(rule_runner.options_bootstrapper) as (console,
                                                            stdio_reader):
        union_membership = UnionMembership({LintRequest: lint_request_types})
        result: Lint = run_rule_with_mocks(
            lint,
            rule_args=[
                console,
                Workspace(rule_runner.scheduler, _enforce_effects=False),
                Targets(targets),
                create_goal_subsystem(LintSubsystem,
                                      per_file_caching=per_file_caching,
                                      per_target_caching=False),
                union_membership,
                DistDir(relpath=Path("dist")),
            ],
            mock_gets=[
                MockGet(
                    output_type=LintResults,
                    input_type=LintRequest,
                    mock=lambda field_set_collection: field_set_collection.
                    lint_results,
                )
            ],
            union_membership=union_membership,
        )
        assert not stdio_reader.get_stdout()
        return result.exit_code, stdio_reader.get_stderr()
async def construct_workunits_callback_factories(
    union_membership: UnionMembership, ) -> WorkunitsCallbackFactories:
    request_types = union_membership.get(WorkunitsCallbackFactoryRequest)
    workunit_callback_factories = await MultiGet(
        Get(WorkunitsCallbackFactory, WorkunitsCallbackFactoryRequest,
            request_type()) for request_type in request_types)
    return WorkunitsCallbackFactories(workunit_callback_factories)
Beispiel #25
0
def test_has_fields() -> None:
    empty_union_membership = UnionMembership({})
    tgt = FortranTarget({}, address=Address.parse(":lib"))

    assert tgt.field_types == (FortranExtensions, FortranSources)
    assert FortranTarget.class_field_types(
        union_membership=empty_union_membership) == (
            FortranExtensions,
            FortranSources,
        )

    assert tgt.has_fields([]) is True
    assert FortranTarget.class_has_fields(
        [], union_membership=empty_union_membership) is True

    assert tgt.has_fields([FortranExtensions]) is True
    assert tgt.has_field(FortranExtensions) is True
    assert (FortranTarget.class_has_fields(
        [FortranExtensions], union_membership=empty_union_membership) is True)
    assert (FortranTarget.class_has_field(
        FortranExtensions, union_membership=empty_union_membership) is True)

    assert tgt.has_fields([UnrelatedField]) is False
    assert tgt.has_field(UnrelatedField) is False
    assert (FortranTarget.class_has_fields(
        [UnrelatedField], union_membership=empty_union_membership) is False)
    assert (FortranTarget.class_has_field(
        UnrelatedField, union_membership=empty_union_membership) is False)

    assert tgt.has_fields([FortranExtensions, UnrelatedField]) is False
    assert (FortranTarget.class_has_fields(
        [FortranExtensions, UnrelatedField],
        union_membership=empty_union_membership) is False)
Beispiel #26
0
def test_add_custom_fields() -> None:
    class CustomField(BoolField):
        alias: ClassVar = "custom_field"
        default: ClassVar = False

    union_membership = UnionMembership({FortranTarget.PluginField: [CustomField]})
    tgt_values = {CustomField.alias: True}
    tgt = FortranTarget(
        tgt_values, address=Address.parse(":lib"), union_membership=union_membership
    )

    assert tgt.field_types == (FortranExtensions, FortranSources, CustomField)
    assert tgt.core_fields == (FortranExtensions, FortranSources)
    assert tgt.plugin_fields == (CustomField,)
    assert tgt.has_field(CustomField) is True

    assert FortranTarget.class_field_types(union_membership=union_membership) == (
        FortranExtensions,
        FortranSources,
        CustomField,
    )
    assert FortranTarget.class_has_field(CustomField, union_membership=union_membership) is True

    assert tgt[CustomField].value is True

    default_tgt = FortranTarget(
        {}, address=Address.parse(":default"), union_membership=union_membership
    )
    assert default_tgt[CustomField].value is False
Beispiel #27
0
def find_valid_field_sets(
        request: FieldSetsPerTargetRequest,
        union_membership: UnionMembership) -> FieldSetsPerTarget:
    field_set_types = union_membership.get(request.field_set_superclass)
    return FieldSetsPerTarget((field_set_type.create(target)
                               for field_set_type in field_set_types
                               if field_set_type.is_applicable(target))
                              for target in request.targets)
Beispiel #28
0
async def export_codegen(
    targets: Targets,
    union_membership: UnionMembership,
    workspace: Workspace,
    dist_dir: DistDir,
    registered_target_types: RegisteredTargetTypes,
) -> ExportCodegen:
    # We run all possible code generators. Running codegen requires specifying the expected
    # output_type, so we must inspect what is possible to generate.
    all_generate_request_types = union_membership.get(GenerateSourcesRequest)
    inputs_to_outputs = {
        req.input: req.output
        for req in all_generate_request_types
    }
    codegen_sources_fields_with_output = []
    for tgt in targets:
        if not tgt.has_field(SourcesField):
            continue
        sources = tgt[SourcesField]
        for input_type in inputs_to_outputs:
            if isinstance(sources, input_type):
                output_type = inputs_to_outputs[input_type]
                codegen_sources_fields_with_output.append(
                    (sources, output_type))

    if not codegen_sources_fields_with_output:
        codegen_targets = sorted({
            tgt_type.alias
            for tgt_type in registered_target_types.types
            for input_sources in inputs_to_outputs.keys()
            if tgt_type.class_has_field(input_sources,
                                        union_membership=union_membership)
        })
        logger.warning(
            "No codegen files/targets matched. All codegen target types: "
            f"{', '.join(codegen_targets)}")
        return ExportCodegen(exit_code=0)

    all_hydrated_sources = await MultiGet(
        Get(
            HydratedSources,
            HydrateSourcesRequest(
                sources_and_output_type[0],
                for_sources_types=(sources_and_output_type[1], ),
                enable_codegen=True,
            ),
        ) for sources_and_output_type in codegen_sources_fields_with_output)

    merged_digest = await Get(
        Digest,
        MergeDigests(hydrated_sources.snapshot.digest
                     for hydrated_sources in all_hydrated_sources),
    )

    dest = str(dist_dir.relpath / "codegen")
    logger.info(f"Writing generated files to {dest}")
    workspace.write_digest(merged_digest, path_prefix=dest)
    return ExportCodegen(exit_code=0)
Beispiel #29
0
def test_get_field() -> None:
    extensions = ("FortranExt1", )
    tgt = FortranTarget({FortranExtensions.alias: extensions},
                        address=Address.parse(":lib"))

    assert tgt[FortranExtensions].value == extensions
    assert tgt.get(FortranExtensions).value == extensions
    assert tgt.get(FortranExtensions,
                   default_raw_value=["FortranExt2"]).value == extensions

    # Default field value. This happens when the field is registered on the target type, but the
    # user does not explicitly set the field in the BUILD file.
    #
    # NB: `default_raw_value` is not used in this case - that parameter is solely used when
    # the field is not registered on the target type. To override the default field value, either
    # subclass the Field and create a new target, or, in your call site, interpret the result and
    # and apply your default.
    default_field_tgt = FortranTarget({}, address=Address.parse(":default"))
    assert default_field_tgt[FortranExtensions].value == ()
    assert default_field_tgt.get(FortranExtensions).value == ()
    assert default_field_tgt.get(FortranExtensions,
                                 default_raw_value=["FortranExt2"]).value == ()
    # Example of a call site applying its own default value instead of the field's default value.
    assert default_field_tgt[FortranExtensions].value or 123 == 123

    assert (FortranTarget.class_get_field(FortranExtensions,
                                          union_membership=UnionMembership({}))
            is FortranExtensions)

    # Field is not registered on the target.
    with pytest.raises(KeyError) as exc:
        default_field_tgt[UnrelatedField]
    assert UnrelatedField.__name__ in str(exc)

    with pytest.raises(KeyError) as exc:
        FortranTarget.class_get_field(UnrelatedField,
                                      union_membership=UnionMembership({}))
    assert UnrelatedField.__name__ in str(exc)

    assert default_field_tgt.get(
        UnrelatedField).value == UnrelatedField.default
    assert default_field_tgt.get(
        UnrelatedField,
        default_raw_value=not UnrelatedField.default).value == (
            not UnrelatedField.default)
Beispiel #30
0
    def run_goal_rules(
        self,
        *,
        options_bootstrapper: OptionsBootstrapper,
        union_membership: UnionMembership,
        options: Options,
        goals: Iterable[str],
        specs: Specs,
    ) -> int:
        """Runs @goal_rules sequentially and interactively by requesting their implicit Goal
        products.

        For retryable failures, raises scheduler.ExecutionError.

        :returns: An exit code.
        """

        global_options = options.for_global_scope()

        console = Console(
            use_colors=global_options.colors,
            session=self.scheduler_session
            if global_options.get("v2_ui") else None,
        )
        workspace = Workspace(self.scheduler_session)
        interactive_runner = InteractiveRunner(self.scheduler_session)

        for goal in goals:
            goal_product = self.goal_map[goal]
            # NB: We no-op for goals that have no V2 implementation because no relevant backends are
            # registered. This allows us to safely set `--v1 --v2`, even if no V2 backends are registered.
            # Once V1 is removed, we might want to reconsider the behavior to instead warn or error when
            # trying to run something like `./pants run` without any backends registered.
            is_implemented = union_membership.has_members_for_all(
                goal_product.subsystem_cls.required_union_implementations)
            if not is_implemented:
                continue
            params = Params(
                specs.provided_specs,
                options_bootstrapper,
                console,
                workspace,
                interactive_runner,
            )
            logger.debug(
                f"requesting {goal_product} to satisfy execution of `{goal}` goal"
            )
            try:
                exit_code = self.scheduler_session.run_goal_rule(
                    goal_product, params)
            finally:
                console.flush()

            if exit_code != PANTS_SUCCEEDED_EXIT_CODE:
                return exit_code

        return PANTS_SUCCEEDED_EXIT_CODE