Пример #1
0
def _get_imports_info(
    address: Address,
    owners_per_import: Iterable[PythonModuleOwners],
    parsed_imports: ParsedPythonImports,
    explicitly_provided_deps: ExplicitlyProvidedDependencies,
) -> tuple[set[Address], set[str]]:
    inferred_deps: set[Address] = set()
    unowned_imports: set[str] = set()

    for owners, imp in zip(owners_per_import, parsed_imports):
        inferred_deps.update(owners.unambiguous)
        explicitly_provided_deps.maybe_warn_of_ambiguous_dependency_inference(
            owners.ambiguous,
            address,
            import_reference="module",
            context=f"The target {address} imports `{imp}`",
        )
        maybe_disambiguated = explicitly_provided_deps.disambiguated(owners.ambiguous)
        if maybe_disambiguated:
            inferred_deps.add(maybe_disambiguated)

        if (
            not owners.unambiguous
            and imp.split(".")[0] not in DEFAULT_UNOWNED_DEPENDENCIES
            and not parsed_imports[imp].weak
        ):
            unowned_imports.add(imp)

    return inferred_deps, unowned_imports
Пример #2
0
 def get_disambiguated(
     *,
     ambiguous: List[Address],
     ignores: List[Address],
     includes: Optional[List[Address]] = None,
 ) -> Optional[Address]:
     epd = ExplicitlyProvidedDependencies(
         includes=FrozenOrderedSet(includes or []), ignores=FrozenOrderedSet(ignores)
     )
     return epd.disambiguated_via_ignores(tuple(ambiguous))
Пример #3
0
 def maybe_warn(
     *,
     ambiguous: List[Address],
     ignores: Optional[List[Address]] = None,
     includes: Optional[List[Address]] = None,
 ) -> None:
     caplog.clear()
     epd = ExplicitlyProvidedDependencies(
         includes=FrozenOrderedSet(includes or []), ignores=FrozenOrderedSet(ignores or [])
     )
     epd.maybe_warn_of_ambiguous_dependency_inference(
         tuple(ambiguous), Address("some_dir"), import_reference="file", context="foo"
     )
Пример #4
0
def test_explicitly_provided_dependencies_remaining_after_disambiguation(
) -> None:
    # First check disambiguation via ignores (`!` and `!!`).
    addr = Address("", target_name="a")
    generated_addr = Address("", target_name="b", generated_name="gen")
    epd = ExplicitlyProvidedDependencies(
        Address("", target_name="input_tgt"),
        includes=FrozenOrderedSet(),
        ignores=FrozenOrderedSet([addr, generated_addr]),
    )

    def assert_disambiguated_via_ignores(ambiguous: List[Address],
                                         expected: Set[Address]) -> None:
        assert (epd.remaining_after_disambiguation(
            tuple(ambiguous), owners_must_be_ancestors=False) == expected)

    assert_disambiguated_via_ignores([], set())
    assert_disambiguated_via_ignores([addr], set())
    assert_disambiguated_via_ignores([generated_addr], set())
    assert_disambiguated_via_ignores([addr, generated_addr], set())
    # Generated targets are covered if their original target generator is in the ignores.
    assert_disambiguated_via_ignores(
        [Address("", target_name="a", generated_name="gen")], set())

    bad_tgt = Address("", target_name="x")
    bad_generated_tgt = Address("", target_name="x", generated_name="gen")
    assert_disambiguated_via_ignores([bad_tgt], {bad_tgt})
    assert_disambiguated_via_ignores([bad_generated_tgt], {bad_generated_tgt})
    assert_disambiguated_via_ignores([bad_generated_tgt, addr, generated_addr],
                                     {bad_generated_tgt})

    # Check disambiguation via `owners_must_be_ancestors`.
    epd = ExplicitlyProvidedDependencies(Address("src/lang/project"),
                                         FrozenOrderedSet(),
                                         FrozenOrderedSet())
    valid_candidates = {
        Address("src/lang/project", target_name="another_tgt"),
        Address("src/lang"),
        Address("src"),
        Address("", target_name="root_owner"),
    }
    invalid_candidates = {
        Address("tests/lang"),
        Address("src/another_lang"),
        Address("src/lang/another_project"),
        Address("src/lang/project/subdir"),
    }
    assert (epd.remaining_after_disambiguation(
        (*valid_candidates, *invalid_candidates),
        owners_must_be_ancestors=True) == valid_candidates)
Пример #5
0
 def get_disambiguated(
     ambiguous: List[Address],
     *,
     ignores: Optional[List[Address]] = None,
     includes: Optional[List[Address]] = None,
     owners_must_be_ancestors: bool = False,
 ) -> Optional[Address]:
     epd = ExplicitlyProvidedDependencies(
         address=Address("dir", target_name="input_tgt"),
         includes=FrozenOrderedSet(includes or []),
         ignores=FrozenOrderedSet(ignores or []),
     )
     return epd.disambiguated(
         tuple(ambiguous),
         owners_must_be_ancestors=owners_must_be_ancestors)
Пример #6
0
 def maybe_warn(
     ambiguous: List[Address],
     *,
     ignores: Optional[List[Address]] = None,
     includes: Optional[List[Address]] = None,
     owners_must_be_ancestors: bool = False,
 ) -> None:
     caplog.clear()
     epd = ExplicitlyProvidedDependencies(
         Address("dir", target_name="input_tgt"),
         includes=FrozenOrderedSet(includes or []),
         ignores=FrozenOrderedSet(ignores or []),
     )
     epd.maybe_warn_of_ambiguous_dependency_inference(
         tuple(ambiguous),
         Address("some_dir"),
         import_reference="file",
         context="foo",
         owners_must_be_ancestors=owners_must_be_ancestors,
     )
Пример #7
0
def _get_inferred_asset_deps(
    address: Address,
    request_file_path: str,
    assets_by_path: AllAssetTargetsByPath,
    assets: ParsedPythonAssetPaths,
    explicitly_provided_deps: ExplicitlyProvidedDependencies,
) -> Iterator[Address]:
    for filepath in assets:
        # NB: Resources in Python's ecosystem are loaded relative to a package, so we only try and
        # query for a resource relative to requesting module's path
        # (I.e. we assume the user is doing something like `pkgutil.get_data(__file__, "foo/bar")`)
        # See https://docs.python.org/3/library/pkgutil.html#pkgutil.get_data
        # and Pants' own docs on resources.
        #
        # Files in Pants are always loaded relative to the build root without any source root
        # stripping, so we use the full filepath to query for files.
        # (I.e. we assume the user is doing something like `open("src/python/configs/prod.json")`)
        #
        # In either case we could also try and query based on the others' key, however this will
        # almost always lead to a false positive.
        resource_path = PurePath(request_file_path).parent / filepath
        file_path = PurePath(filepath)

        inferred_resource_tgts = assets_by_path.resources.get(
            resource_path, frozenset())
        inferred_file_tgts = assets_by_path.files.get(file_path, frozenset())
        inferred_tgts = inferred_resource_tgts | inferred_file_tgts

        if inferred_tgts:
            possible_addresses = tuple(tgt.address for tgt in inferred_tgts)
            explicitly_provided_deps.maybe_warn_of_ambiguous_dependency_inference(
                possible_addresses,
                address,
                import_reference="asset",
                context=f"The target {address} uses `{filepath}`",
            )
            maybe_disambiguated = explicitly_provided_deps.disambiguated(
                possible_addresses)
            if maybe_disambiguated:
                yield maybe_disambiguated
Пример #8
0
def test_explicitly_provided_dependencies_remaining_after_ignores() -> None:
    build_tgt = Address("", target_name="a")
    file_tgt = Address("", target_name="b", relative_file_path="f.ext")
    epd = ExplicitlyProvidedDependencies(
        includes=FrozenOrderedSet(),
        ignores=FrozenOrderedSet([build_tgt, file_tgt]),
    )

    assert epd.remaining_after_ignores(()) == set()
    assert epd.remaining_after_ignores((build_tgt,)) == set()
    assert epd.remaining_after_ignores((file_tgt,)) == set()
    assert epd.remaining_after_ignores((build_tgt, file_tgt)) == set()
    # File addresses are covered if their original BUILD address is in the includes.
    assert (
        epd.remaining_after_ignores((Address("", target_name="a", relative_file_path="f.ext"),))
        == set()
    )

    bad_build_tgt = Address("", target_name="x")
    bad_file_tgt = Address("", target_name="x", relative_file_path="f.ext")
    assert epd.remaining_after_ignores((bad_build_tgt,)) == {bad_build_tgt}
    assert epd.remaining_after_ignores((bad_file_tgt,)) == {bad_file_tgt}
    assert epd.remaining_after_ignores((bad_file_tgt, build_tgt, file_tgt)) == {bad_file_tgt}
Пример #9
0
def test_explicitly_provided_dependencies_any_are_covered_by_includes() -> None:
    build_tgt = Address("", target_name="a")
    file_tgt = Address("", target_name="b", relative_file_path="f.ext")
    epd = ExplicitlyProvidedDependencies(
        includes=FrozenOrderedSet([build_tgt, file_tgt]),
        ignores=FrozenOrderedSet(),
    )

    assert epd.any_are_covered_by_includes(()) is False
    assert epd.any_are_covered_by_includes((build_tgt,)) is True
    assert epd.any_are_covered_by_includes((file_tgt,)) is True
    assert epd.any_are_covered_by_includes((build_tgt, file_tgt)) is True
    # File addresses are covered if their original BUILD address is in the includes.
    assert (
        epd.any_are_covered_by_includes((Address("", target_name="a", relative_file_path="f.ext"),))
        is True
    )
    assert epd.any_are_covered_by_includes((Address("", target_name="x"),)) is False
    assert (
        epd.any_are_covered_by_includes((Address("", target_name="x", relative_file_path="f.ext"),))
        is False
    )
    # Ensure we check for _any_, not _all_.
    assert epd.any_are_covered_by_includes((Address("", target_name="x"), build_tgt)) is True
Пример #10
0
def test_explicitly_provided_dependencies_any_are_covered_by_includes(
) -> None:
    addr = Address("", target_name="a")
    generated_addr = Address("", target_name="b", generated_name="gen")
    epd = ExplicitlyProvidedDependencies(
        Address("", target_name="input_tgt"),
        includes=FrozenOrderedSet([addr, generated_addr]),
        ignores=FrozenOrderedSet(),
    )

    assert epd.any_are_covered_by_includes(()) is False
    assert epd.any_are_covered_by_includes((addr, )) is True
    assert epd.any_are_covered_by_includes((generated_addr, )) is True
    assert epd.any_are_covered_by_includes((addr, generated_addr)) is True
    # Generated targets are covered if their original target generator is in the includes.
    assert (epd.any_are_covered_by_includes(
        (Address("", target_name="a", generated_name="gen"), )) is True)
    assert epd.any_are_covered_by_includes(
        (Address("", target_name="x"), )) is False
    assert (epd.any_are_covered_by_includes(
        (Address("", target_name="x", generated_name="gen"), )) is False)
    # Ensure we check for _any_, not _all_.
    assert epd.any_are_covered_by_includes(
        (Address("", target_name="x"), addr)) is True
Пример #11
0
async def determine_explicitly_provided_dependencies(
    request: DependenciesRequest,
    union_membership: UnionMembership,
    registered_target_types: RegisteredTargetTypes,
    subproject_roots: SubprojectRoots,
) -> ExplicitlyProvidedDependencies:
    parse = functools.partial(
        AddressInput.parse,
        relative_to=request.field.address.spec_path,
        subproject_roots=subproject_roots,
        description_of_origin=
        (f"the `{request.field.alias}` field from the target {request.field.address}"
         ),
    )

    addresses: list[AddressInput] = []
    ignored_addresses: list[AddressInput] = []
    for v in request.field.value or ():
        is_ignore = v.startswith("!")
        if is_ignore:
            # Check if it's a transitive exclude, rather than a direct exclude.
            if v.startswith("!!"):
                if not request.field.supports_transitive_excludes:
                    raise TransitiveExcludesNotSupportedError(
                        bad_value=v,
                        address=request.field.address,
                        registered_target_types=registered_target_types.types,
                        union_membership=union_membership,
                    )
                v = v[2:]
            else:
                v = v[1:]
        result = parse(v)
        if is_ignore:
            ignored_addresses.append(result)
        else:
            addresses.append(result)

    parsed_includes = await MultiGet(
        Get(Address, AddressInput, ai) for ai in addresses)
    parsed_ignores = await MultiGet(
        Get(Address, AddressInput, ai) for ai in ignored_addresses)
    return ExplicitlyProvidedDependencies(
        request.field.address,
        FrozenOrderedSet(sorted(parsed_includes)),
        FrozenOrderedSet(sorted(parsed_ignores)),
    )