Exemple #1
0
    def test_owners_multiple_owners(self) -> None:
        """Even if there are multiple owners of the same file, we still use generated subtargets."""
        self.create_files("demo", ["f1.txt", "f2.txt"])
        self.add_to_build_file(
            "demo",
            dedent(
                """\
                target(name='all', sources=['*.txt'])
                target(name='f2', sources=['f2.txt'])
                """
            ),
        )

        one_owner_result = self.request_single_product(Owners, OwnersRequest(("demo/f1.txt",)))
        assert one_owner_result == Owners(
            [Address("demo", relative_file_path="f1.txt", target_name="all")]
        )

        two_owners_result = self.request_single_product(Owners, OwnersRequest(("demo/f2.txt",)))
        assert two_owners_result == Owners(
            [
                Address("demo", relative_file_path="f2.txt", target_name="all"),
                Address("demo", relative_file_path="f2.txt", target_name="f2"),
            ]
        )
Exemple #2
0
async def find_owners(
    build_configuration: BuildConfiguration,
    address_mapper: AddressMapper,
    changed_request: ChangedRequest,
) -> ChangedAddresses:
    owners = await Get[Owners](OwnersRequest(sources=changed_request.sources))

    # If the ChangedRequest does not require dependees, then we're done.
    if changed_request.include_dependees == IncludeDependeesOption.NONE:
        return ChangedAddresses(owners.addresses)

    # Otherwise: find dependees.
    all_addresses = await Get[Addresses](AddressSpecs(
        (DescendantAddresses(""), )))
    all_structs = [
        s.value for s in await MultiGet(Get[HydratedStruct](Address, a)
                                        for a in all_addresses)
    ]

    bfa = build_configuration.registered_aliases()
    graph = _DependentGraph.from_iterable(
        target_types_from_build_file_aliases(bfa), address_mapper, all_structs)
    if changed_request.include_dependees == IncludeDependeesOption.DIRECT:
        return ChangedAddresses(
            Addresses(graph.dependents_of_addresses(owners.addresses)))
    return ChangedAddresses(
        Addresses(graph.transitive_dependents_of_addresses(owners.addresses)))
Exemple #3
0
async def addresses_from_raw_specs_with_only_file_owners(
        specs: RawSpecsWithOnlyFileOwners,
        owners_not_found_behavior: OwnersNotFoundBehavior) -> Addresses:
    """Find the owner(s) for each spec."""
    paths_per_include = await MultiGet(
        Get(Paths, PathGlobs, specs.path_globs_for_spec(spec))
        for spec in specs.all_specs())
    owners_per_include = await MultiGet(
        Get(
            Owners,
            OwnersRequest(
                paths.files,
                filter_by_global_options=specs.filter_by_global_options),
        ) for paths in paths_per_include)
    addresses: set[Address] = set()
    for spec, owners in zip(specs.all_specs(), owners_per_include):
        if (not specs.from_change_detection
                and owners_not_found_behavior != OwnersNotFoundBehavior.ignore
                and isinstance(spec, FileLiteralSpec) and not owners):
            _log_or_raise_unmatched_owners(
                [PurePath(str(spec))],
                owners_not_found_behavior,
                ignore_option="--owners-not-found-behavior=ignore",
            )
        addresses.update(owners)
    return Addresses(sorted(addresses))
Exemple #4
0
async def find_changed_owners(request: ChangedRequest) -> ChangedAddresses:
    owners = await Get(Owners, OwnersRequest(request.sources))
    if request.dependees == DependeesOption.NONE:
        return ChangedAddresses(owners)
    dependees_with_roots = await Get(
        Dependees,
        DependeesRequest(
            owners, transitive=request.dependees == DependeesOption.TRANSITIVE, include_roots=True,
        ),
    )
    return ChangedAddresses(dependees_with_roots)
Exemple #5
0
    def test_owners_source_file_does_not_exist(self) -> None:
        """Test when a source file belongs to a target, even though the file does not actually
        exist.

        This happens, for example, when the file is deleted and we're computing `--changed-since`.
        In this case, we should not attempt to generate a subtarget and should use the original
        target.
        """
        self.create_file("demo/f.txt")
        self.add_to_build_file("demo", "target(sources=['*.txt'])")
        result = self.request_single_product(Owners, OwnersRequest(("demo/deleted.txt",)))
        assert result == Owners([Address("demo", target_name="demo")])
        # For files that do exist, we should still use a generated subtarget, though.
        result = self.request_single_product(Owners, OwnersRequest(("demo/f.txt",)))
        assert result == Owners([Address("demo", relative_file_path="f.txt", target_name="demo")])
        # However, if a sibling file uses the original target, then both should be used.
        result = self.request_single_product(
            Owners, OwnersRequest(("demo/f.txt", "demo/deleted.txt"))
        )
        assert result == Owners([Address("demo", relative_file_path="f.txt"), Address("demo")])
Exemple #6
0
async def infer_python_init_dependencies(
        request: InferInitDependencies,
        python_infer_subsystem: PythonInferSubsystem) -> InferredDependencies:
    if not python_infer_subsystem.inits:
        return InferredDependencies([])

    fp = request.sources_field.file_path
    assert fp is not None
    init_files = await Get(
        AncestorFiles,
        AncestorFilesRequest(input_files=(fp, ),
                             requested=("__init__.py", "__init__.pyi")),
    )
    owners = await MultiGet(
        Get(Owners, OwnersRequest((f, ))) for f in init_files.snapshot.files)
    return InferredDependencies(itertools.chain.from_iterable(owners))
Exemple #7
0
async def find_changed_owners(request: ChangedRequest,
                              specs_filter: SpecsFilter) -> ChangedAddresses:
    no_dependees = request.dependees == DependeesOption.NONE
    owners = await Get(
        Owners,
        OwnersRequest(
            request.sources,
            # If `--changed-dependees` is used, we cannot eagerly filter out root targets. We
            # need to first find their dependees, and only then should we filter. See
            # https://github.com/pantsbuild/pants/issues/15544
            filter_by_global_options=no_dependees,
        ),
    )
    if no_dependees:
        return ChangedAddresses(owners)

    # See https://github.com/pantsbuild/pants/issues/15313. We filter out target generators because
    # they are not useful as aliases for their generated targets in the context of
    # `--changed-since`. Including them makes it look like all sibling targets from the same
    # target generator have also changed.
    #
    # However, we also must be careful to preserve if target generators are direct owners, which
    # happens when a generated file is deleted.
    owner_target_generators = FrozenOrderedSet(
        addr.maybe_convert_to_target_generator() for addr in owners
        if addr.is_generated_target)
    dependees = await Get(
        Dependees,
        DependeesRequest(
            owners,
            transitive=request.dependees == DependeesOption.TRANSITIVE,
            include_roots=False,
        ),
    )
    result = FrozenOrderedSet(owners) | (dependees - owner_target_generators)
    if specs_filter.is_specified:
        # Finally, we must now filter out the result to only include what matches our tags, as the
        # last step of https://github.com/pantsbuild/pants/issues/15544.
        #
        # Note that we use `UnexpandedTargets` rather than `Targets` or `FilteredTargets` so that
        # we preserve target generators.
        result_as_tgts = await Get(UnexpandedTargets, Addresses(result))
        result = FrozenOrderedSet(tgt.address for tgt in result_as_tgts
                                  if specs_filter.matches(tgt))

    return ChangedAddresses(result)
Exemple #8
0
async def infer_python_init_dependencies(
    request: InferInitDependencies,
    python_infer_subsystem: PythonInferSubsystem,
    python_setup: PythonSetup,
) -> InferredDependencies:
    if (
        not python_infer_subsystem.options.is_default("inits") and not python_infer_subsystem.inits
    ) or python_infer_subsystem.init_files is InitFilesInference.never:
        return InferredDependencies([])

    ignore_empty_files = (
        python_infer_subsystem.options.is_default("inits")
        and python_infer_subsystem.init_files is InitFilesInference.content_only
    )
    fp = request.sources_field.file_path
    assert fp is not None
    init_files = await Get(
        AncestorFiles,
        AncestorFilesRequest(
            input_files=(fp,),
            requested=("__init__.py", "__init__.pyi"),
            ignore_empty_files=ignore_empty_files,
        ),
    )
    owners = await MultiGet(Get(Owners, OwnersRequest((f,))) for f in init_files.snapshot.files)

    original_tgt, owner_tgts = await MultiGet(
        Get(
            WrappedTarget,
            WrappedTargetRequest(
                request.sources_field.address, description_of_origin="<infallible>"
            ),
        ),
        Get(Targets, Addresses(itertools.chain.from_iterable(owners))),
    )
    resolve = original_tgt.target[PythonResolveField].normalized_value(python_setup)
    python_owners = [
        tgt.address
        for tgt in owner_tgts
        if (
            tgt.has_field(PythonSourceField)
            and tgt[PythonResolveField].normalized_value(python_setup) == resolve
        )
    ]
    return InferredDependencies(python_owners)
Exemple #9
0
 def test_owners_build_file(self) -> None:
     """A BUILD file owns every target defined in it."""
     self.create_files("demo", ["f1.txt", "f2.txt"])
     self.add_to_build_file(
         "demo",
         dedent(
             """\
             target(name='f1', sources=['f1.txt'])
             target(name='f2_first', sources=['f2.txt'])
             target(name='f2_second', sources=['f2.txt'])
             """
         ),
     )
     result = self.request_single_product(Owners, OwnersRequest(("demo/BUILD",)))
     assert set(result) == {
         Address("demo", relative_file_path="f1.txt", target_name="f1"),
         Address("demo", relative_file_path="f2.txt", target_name="f2_first"),
         Address("demo", relative_file_path="f2.txt", target_name="f2_second"),
     }
Exemple #10
0
async def infer_python_conftest_dependencies(
    request: InferConftestDependencies,
    python_infer_subsystem: PythonInferSubsystem,
) -> InferredDependencies:
    if not python_infer_subsystem.conftests:
        return InferredDependencies([])

    fp = request.sources_field.file_path
    assert fp is not None
    conftest_files = await Get(
        AncestorFiles,
        AncestorFilesRequest(input_files=(fp, ), requested=("conftest.py", )),
    )
    owners = await MultiGet(
        # NB: Because conftest.py files effectively always have content, we require an
        # owning target.
        Get(Owners, OwnersRequest((f, ), OwnersNotFoundBehavior.error))
        for f in conftest_files.snapshot.files)
    return InferredDependencies(itertools.chain.from_iterable(owners))
Exemple #11
0
async def infer_python_conftest_dependencies(
    request: InferConftestDependencies, python_inference: PythonInference,
) -> InferredDependencies:
    if not python_inference.conftests:
        return InferredDependencies()

    # Locate conftest.py files not already in the Snapshot.
    hydrated_sources = await Get(HydratedSources, HydrateSourcesRequest(request.sources_field))
    extra_conftest_files = await Get(
        AncestorFiles, AncestorFilesRequest("conftest.py", hydrated_sources.snapshot),
    )

    # And add dependencies on their owners.
    # NB: Because conftest.py files effectively always have content, we require an owning target.
    owners = await MultiGet(
        Get(Owners, OwnersRequest((f,), OwnersNotFoundBehavior.error))
        for f in extra_conftest_files.snapshot.files
    )
    return InferredDependencies(itertools.chain.from_iterable(owners))
Exemple #12
0
async def infer_python_init_dependencies(
    request: InferInitDependencies, python_inference: PythonInference
) -> InferredDependencies:
    if not python_inference.inits:
        return InferredDependencies()

    # Locate __init__.py files not already in the Snapshot.
    hydrated_sources = await Get(HydratedSources, HydrateSourcesRequest(request.sources_field))
    extra_init_files = await Get(
        AncestorFiles, AncestorFilesRequest("__init__.py", hydrated_sources.snapshot),
    )

    # And add dependencies on their owners.
    # NB: Because the python_sources rules always locate __init__.py files, and will trigger an
    # error for files that have content but have not already been included via a dependency, we
    # don't need to error for unowned files here.
    owners = await MultiGet(
        Get(Owners, OwnersRequest((f,))) for f in extra_init_files.snapshot.files
    )
    return InferredDependencies(itertools.chain.from_iterable(owners))
Exemple #13
0
async def infer_python_conftest_dependencies(
    request: InferConftestDependencies,
    python_infer_subsystem: PythonInferSubsystem,
    python_setup: PythonSetup,
) -> InferredDependencies:
    if not python_infer_subsystem.conftests:
        return InferredDependencies([])

    fp = request.sources_field.file_path
    assert fp is not None
    conftest_files = await Get(
        AncestorFiles,
        AncestorFilesRequest(input_files=(fp,), requested=("conftest.py",)),
    )
    owners = await MultiGet(
        # NB: Because conftest.py files effectively always have content, we require an
        # owning target.
        Get(Owners, OwnersRequest((f,), OwnersNotFoundBehavior.error))
        for f in conftest_files.snapshot.files
    )

    original_tgt, owner_tgts = await MultiGet(
        Get(
            WrappedTarget,
            WrappedTargetRequest(
                request.sources_field.address, description_of_origin="<infallible>"
            ),
        ),
        Get(Targets, Addresses(itertools.chain.from_iterable(owners))),
    )
    resolve = original_tgt.target[PythonResolveField].normalized_value(python_setup)
    python_owners = [
        tgt.address
        for tgt in owner_tgts
        if (
            tgt.has_field(PythonSourceField)
            and tgt[PythonResolveField].normalized_value(python_setup) == resolve
        )
    ]
    return InferredDependencies(python_owners)
Exemple #14
0
 def owner(self, owner, f):
     request = OwnersRequest(sources=(f, ))
     owners = self.request_single_product(Owners, request)
     self.assertEqual(set(owner), {i.spec for i in owners.addresses})