Esempio n. 1
0
async def infer_python_dependencies(
    request: InferPythonDependencies, python_inference: PythonInference
) -> InferredDependencies:
    if not python_inference.imports:
        return InferredDependencies()

    stripped_sources = await Get(StrippedSourceFiles, SourceFilesRequest([request.sources_field]))
    modules = tuple(
        PythonModule.create_from_stripped_path(PurePath(fp))
        for fp in stripped_sources.snapshot.files
    )
    digest_contents = await Get(DigestContents, Digest, stripped_sources.snapshot.digest)
    imports_per_file = tuple(
        find_python_imports(file_content.content.decode(), module_name=module.module)
        for file_content, module in zip(digest_contents, modules)
    )
    owner_per_import = await MultiGet(
        Get(PythonModuleOwner, PythonModule(imported_module))
        for file_imports in imports_per_file
        for imported_module in file_imports.explicit_imports
        if imported_module not in combined_stdlib
    )
    return InferredDependencies(
        owner.address
        for owner in owner_per_import
        if (
            owner.address
            and owner.address.maybe_convert_to_base_target() != request.sources_field.address
        )
    )
Esempio n. 2
0
async def infer_python_dependencies(
        request: InferPythonDependencies,
        python_inference: PythonInference) -> InferredDependencies:
    if not python_inference.imports:
        return InferredDependencies([], sibling_dependencies_inferrable=False)

    stripped_sources = await Get(StrippedSourceFiles,
                                 SourceFilesRequest([request.sources_field]))
    modules = tuple(
        PythonModule.create_from_stripped_path(PurePath(fp))
        for fp in stripped_sources.snapshot.files)
    digest_contents = await Get(DigestContents, Digest,
                                stripped_sources.snapshot.digest)

    owner_requests: List[Get[PythonModuleOwner, PythonModule]] = []
    for file_content, module in zip(digest_contents, modules):
        file_imports_obj = find_python_imports(file_content.content.decode(),
                                               module_name=module.module)
        detected_imports = (file_imports_obj.all_imports
                            if python_inference.string_imports else
                            file_imports_obj.explicit_imports)
        owner_requests.extend(
            Get(PythonModuleOwner, PythonModule(imported_module))
            for imported_module in detected_imports
            if imported_module not in combined_stdlib)

    owner_per_import = await MultiGet(owner_requests)
    result = (
        owner.address for owner in owner_per_import
        if owner.address and owner.address != request.sources_field.address)
    return InferredDependencies(result, sibling_dependencies_inferrable=True)
Esempio n. 3
0
def test_module_possible_paths() -> None:
    assert set(PythonModule("typing").possible_stripped_paths()) == {
        PurePath("typing.py"),
        PurePath("typing") / "__init__.py",
    }
    assert set(PythonModule("typing.List").possible_stripped_paths()) == {
        PurePath("typing") / "List.py",
        PurePath("typing") / "List" / "__init__.py",
        PurePath("typing.py"),
        PurePath("typing") / "__init__.py",
    }
Esempio n. 4
0
    def test_map_module_to_targets(self) -> None:
        options_bootstrapper = create_options_bootstrapper(args=[
            "--source-root-patterns=['source_root1', 'source_root2', '/']"
        ])
        # First check that we can map 3rd-party modules.
        self.add_to_build_file(
            "3rdparty/python",
            dedent("""\
                python_requirement_library(
                  name='ansicolors',
                  requirements=[python_requirement('ansicolors==1.21', modules=['colors'])],
                )
                """),
        )
        result = self.request_single_product(
            PythonModuleOwners,
            Params(PythonModule("colors.red"), options_bootstrapper))
        assert result == PythonModuleOwners(
            [Address.parse("3rdparty/python:ansicolors")])

        # We set up the same module in two source roots to confirm we properly handle source roots.
        # The first example uses a normal module path, whereas the second uses a package path.
        self.create_file("source_root1/project/app.py")
        self.add_to_build_file("source_root1/project", "python_library()")
        self.create_file("source_root2/project/app/__init__.py")
        self.add_to_build_file("source_root2/project/app", "python_library()")
        result = self.request_single_product(
            PythonModuleOwners,
            Params(PythonModule("project.app"), options_bootstrapper))
        assert result == PythonModuleOwners([
            Address.parse("source_root1/project"),
            Address.parse("source_root2/project/app")
        ])

        # Test a module with no owner (stdlib). This also sanity checks that we can handle when
        # there is no parent module.
        result = self.request_single_product(
            PythonModuleOwners,
            Params(PythonModule("typing"), options_bootstrapper))
        assert not result

        # Test a module with a single owner with a top-level source root of ".". Also confirm we
        # can handle when the module includes a symbol (like a class name) at the end.
        self.create_file("script.py")
        self.add_to_build_file("", "python_library(name='script')")
        result = self.request_single_product(
            PythonModuleOwners,
            Params(PythonModule("script.Demo"), options_bootstrapper))
        assert result == PythonModuleOwners([Address.parse("//:script")])
Esempio n. 5
0
async def inject_pex_binary_entry_point_dependency(
        request: InjectPexBinaryEntryPointDependency,
        python_infer_subsystem: PythonInferSubsystem) -> InjectedDependencies:
    if not python_infer_subsystem.entry_points:
        return InjectedDependencies()
    original_tgt = await Get(WrappedTarget, Address,
                             request.dependencies_field.address)
    explicitly_provided_deps, entry_point = await MultiGet(
        Get(ExplicitlyProvidedDependencies,
            DependenciesRequest(original_tgt.target[Dependencies])),
        Get(
            ResolvedPexEntryPoint,
            ResolvePexEntryPointRequest(
                original_tgt.target[PexEntryPointField]),
        ),
    )
    if entry_point.val is None:
        return InjectedDependencies()
    owners = await Get(PythonModuleOwners,
                       PythonModule(entry_point.val.module))
    address = original_tgt.target.address
    explicitly_provided_deps.maybe_warn_of_ambiguous_dependency_inference(
        owners.ambiguous,
        address,
        import_reference="module",
        context=
        (f"The pex_binary target {address} has the field "
         f"`entry_point={repr(original_tgt.target[PexEntryPointField].value.spec)}`, which "
         f"maps to the Python module `{entry_point.val.module}`"),
    )
    maybe_disambiguated = explicitly_provided_deps.disambiguated_via_ignores(
        owners.ambiguous)
    unambiguous_owners = owners.unambiguous or (
        (maybe_disambiguated, ) if maybe_disambiguated else ())
    return InjectedDependencies(unambiguous_owners)
Esempio n. 6
0
 def assert_owners(module: str,
                   expected: list[Address],
                   expected_ambiguous: list[Address] | None = None) -> None:
     owners = rule_runner.request(PythonModuleOwners,
                                  [PythonModule(module)])
     assert list(owners.unambiguous) == expected
     assert list(owners.ambiguous) == (expected_ambiguous or [])
Esempio n. 7
0
async def inject_lambda_handler_dependency(
        request: InjectPythonLambdaHandlerDependency,
        python_infer_subsystem: PythonInferSubsystem) -> InjectedDependencies:
    if not python_infer_subsystem.entry_points:
        return InjectedDependencies()
    original_tgt = await Get(WrappedTarget, Address,
                             request.dependencies_field.address)
    explicitly_provided_deps, handler = await MultiGet(
        Get(ExplicitlyProvidedDependencies,
            DependenciesRequest(original_tgt.target[Dependencies])),
        Get(
            ResolvedPythonAwsHandler,
            ResolvePythonAwsHandlerRequest(
                original_tgt.target[PythonAwsLambdaHandlerField]),
        ),
    )
    module, _, _func = handler.val.partition(":")
    owners = await Get(PythonModuleOwners, PythonModule(module))
    address = original_tgt.target.address
    explicitly_provided_deps.maybe_warn_of_ambiguous_dependency_inference(
        owners.ambiguous,
        address,
        import_reference="module",
        context=
        (f"The python_awslambda target {address} has the field "
         f"`handler={repr(original_tgt.target[PythonAwsLambdaHandlerField].value)}`, which maps "
         f"to the Python module `{module}`"),
    )
    maybe_disambiguated = explicitly_provided_deps.disambiguated_via_ignores(
        owners.ambiguous)
    unambiguous_owners = owners.unambiguous or (
        (maybe_disambiguated, ) if maybe_disambiguated else ())
    return InjectedDependencies(unambiguous_owners)
Esempio n. 8
0
async def infer_python_dependencies_via_imports(
    request: InferPythonImportDependencies,
    python_infer_subsystem: PythonInferSubsystem,
    python_setup: PythonSetup,
) -> InferredDependencies:
    if not python_infer_subsystem.imports:
        return InferredDependencies([], sibling_dependencies_inferrable=False)

    wrapped_tgt = await Get(WrappedTarget, Address,
                            request.sources_field.address)
    detected_imports = await Get(
        ParsedPythonImports,
        ParsePythonImportsRequest(
            request.sources_field,
            PexInterpreterConstraints.create_from_targets([wrapped_tgt.target],
                                                          python_setup),
        ),
    )
    relevant_imports = (detected_imports.all_imports
                        if python_infer_subsystem.string_imports else
                        detected_imports.explicit_imports)

    owners_per_import = await MultiGet(
        Get(PythonModuleOwners, PythonModule(imported_module))
        for imported_module in relevant_imports
        if imported_module not in combined_stdlib)
    merged_result = sorted(
        set(itertools.chain.from_iterable(owners_per_import)))
    return InferredDependencies(merged_result,
                                sibling_dependencies_inferrable=True)
Esempio n. 9
0
async def inject_python_distribution_dependencies(
        request: InjectPythonDistributionDependencies,
        python_infer_subsystem: PythonInferSubsystem) -> InjectedDependencies:
    """Inject dependencies that we can infer from entry points in the distribution."""
    if not python_infer_subsystem.entry_points:
        return InjectedDependencies()

    original_tgt = await Get(WrappedTarget, Address,
                             request.dependencies_field.address)
    explicitly_provided_deps, distribution_entry_points, provides_entry_points = await MultiGet(
        Get(ExplicitlyProvidedDependencies,
            DependenciesRequest(original_tgt.target[Dependencies])),
        Get(
            ResolvedPythonDistributionEntryPoints,
            ResolvePythonDistributionEntryPointsRequest(
                entry_points_field=original_tgt.
                target[PythonDistributionEntryPointsField]),
        ),
        Get(
            ResolvedPythonDistributionEntryPoints,
            ResolvePythonDistributionEntryPointsRequest(
                provides_field=original_tgt.target[PythonProvidesField]),
        ),
    )

    address = original_tgt.target.address
    all_module_entry_points = [
        (category, name, entry_point) for category, entry_points in chain(
            distribution_entry_points.explicit_modules.items(),
            provides_entry_points.explicit_modules.items(),
        ) for name, entry_point in entry_points.items()
    ]
    all_module_owners = iter(await MultiGet(
        Get(PythonModuleOwners, PythonModule(entry_point.module))
        for _, _, entry_point in all_module_entry_points))
    module_owners: OrderedSet[Address] = OrderedSet()
    for (category, name, entry_point), owners in zip(all_module_entry_points,
                                                     all_module_owners):
        field_str = repr({category: {name: entry_point.spec}})
        explicitly_provided_deps.maybe_warn_of_ambiguous_dependency_inference(
            owners.ambiguous,
            address,
            import_reference="module",
            context=(
                f"The python_distribution target {address} has the field "
                f"`entry_points={field_str}`, which maps to the Python module"
                f"`{entry_point.module}`"),
        )
        maybe_disambiguated = explicitly_provided_deps.disambiguated(
            owners.ambiguous)
        unambiguous_owners = owners.unambiguous or (
            (maybe_disambiguated, ) if maybe_disambiguated else ())
        module_owners.update(unambiguous_owners)

    return InjectedDependencies(
        Addresses(module_owners) +
        distribution_entry_points.pex_binary_addresses +
        provides_entry_points.pex_binary_addresses)
Esempio n. 10
0
async def infer_python_dependencies(
        request: InferPythonDependencies) -> InferredDependencies:
    stripped_sources = await Get[SourceRootStrippedSources](
        StripSourcesFieldRequest(request.sources_field))
    modules = tuple(
        PythonModule.create_from_stripped_path(PurePath(fp))
        for fp in stripped_sources.snapshot.files)
    files_content = await Get[FilesContent](Digest,
                                            stripped_sources.snapshot.digest)
    imports_per_file = tuple(
        find_python_imports(fc.content.decode(), module_name=module.module)
        for fc, module in zip(files_content, modules))
    owner_per_import = await MultiGet(
        Get[PythonModuleOwner](PythonModule(imported_module))
        for file_imports in imports_per_file
        for imported_module in file_imports.all_imports
        if imported_module not in combined_stdlib)
    return InferredDependencies(
        owner.address for owner in owner_per_import
        if owner.address and owner.address != request.sources_field.address)
Esempio n. 11
0
async def infer_python_dependencies(
        request: InferPythonDependencies) -> InferredDependencies:
    stripped_sources = await Get[SourceRootStrippedSources](
        StripSourcesFieldRequest(request.sources_field))
    modules = tuple(
        PythonModule.create_from_stripped_path(PurePath(fp))
        for fp in stripped_sources.snapshot.files)
    files_content = await Get[FilesContent](Digest,
                                            stripped_sources.snapshot.digest)
    imports_per_file = tuple(
        find_python_imports(fc.content.decode(), module_name=module.module)
        for fc, module in zip(files_content, modules))
    owners_per_import = await MultiGet(
        Get[PythonModuleOwners](PythonModule(imported_module))
        for file_imports in imports_per_file
        for imported_module in file_imports.all_imports
        if imported_module not in combined_stdlib)
    # We conservatively only use dep inference if there is exactly one owner for an import.
    return InferredDependencies(
        itertools.chain.from_iterable(
            owners for owners in owners_per_import if len(owners) == 1
            and tuple(owners)[0] != request.sources_field.address))
Esempio n. 12
0
async def inject_lambda_handler_dependency(
    request: InjectPythonLambdaHandlerDependency, python_infer_subsystem: PythonInferSubsystem
) -> InjectedDependencies:
    if not python_infer_subsystem.entry_points:
        return InjectedDependencies()
    original_tgt = await Get(WrappedTarget, Address, request.dependencies_field.address)
    handler = await Get(
        ResolvedPythonAwsHandler,
        ResolvePythonAwsHandlerRequest(original_tgt.target[PythonAwsLambdaHandlerField]),
    )
    module, _, _func = handler.val.partition(":")
    owners = await Get(PythonModuleOwners, PythonModule(module))
    return InjectedDependencies(owners)
Esempio n. 13
0
async def inject_pex_binary_entry_point_dependency(
    request: InjectPexBinaryEntryPointDependency, python_infer_subsystem: PythonInferSubsystem
) -> InjectedDependencies:
    if not python_infer_subsystem.entry_points:
        return InjectedDependencies()
    original_tgt = await Get(WrappedTarget, Address, request.dependencies_field.address)
    entry_point = await Get(
        ResolvedPexEntryPoint,
        ResolvePexEntryPointRequest(original_tgt.target[PexEntryPointField]),
    )
    if entry_point.val is None:
        return InjectedDependencies()
    module, _, _func = entry_point.val.partition(":")
    owners = await Get(PythonModuleOwners, PythonModule(module))
    return InjectedDependencies(owners)
Esempio n. 14
0
async def infer_python_dependencies(
        request: InferPythonDependencies,
        python_inference: PythonInference) -> InferredDependencies:
    if not python_inference.imports:
        return InferredDependencies([], sibling_dependencies_inferrable=False)

    stripped_sources = await Get(StrippedSourceFiles,
                                 SourceFilesRequest([request.sources_field]))
    modules = tuple(
        PythonModule.create_from_stripped_path(PurePath(fp))
        for fp in stripped_sources.snapshot.files)
    digest_contents = await Get(DigestContents, Digest,
                                stripped_sources.snapshot.digest)

    owners_requests: List[Get[PythonModuleOwners, PythonModule]] = []
    for file_content, module in zip(digest_contents, modules):
        file_imports_obj = find_python_imports(
            filename=file_content.path,
            content=file_content.content.decode(),
            module_name=module.module,
        )
        detected_imports = (file_imports_obj.all_imports
                            if python_inference.string_imports else
                            file_imports_obj.explicit_imports)
        owners_requests.extend(
            Get(PythonModuleOwners, PythonModule(imported_module))
            for imported_module in detected_imports
            if imported_module not in combined_stdlib)

    owners_per_import = await MultiGet(owners_requests)
    # We remove the request's address so that we don't infer dependencies on self.
    merged_result = sorted(
        set(itertools.chain.from_iterable(owners_per_import)) -
        {request.sources_field.address})
    return InferredDependencies(merged_result,
                                sibling_dependencies_inferrable=True)
Esempio n. 15
0
async def infer_python_dependencies_via_imports(
    request: InferPythonImportDependencies,
    python_infer_subsystem: PythonInferSubsystem,
    python_setup: PythonSetup,
) -> InferredDependencies:
    if not python_infer_subsystem.imports:
        return InferredDependencies([], sibling_dependencies_inferrable=False)

    wrapped_tgt = await Get(WrappedTarget, Address,
                            request.sources_field.address)
    explicitly_provided_deps, detected_imports = await MultiGet(
        Get(ExplicitlyProvidedDependencies,
            DependenciesRequest(wrapped_tgt.target[Dependencies])),
        Get(
            ParsedPythonImports,
            ParsePythonImportsRequest(
                request.sources_field,
                PexInterpreterConstraints.create_from_targets(
                    [wrapped_tgt.target], python_setup),
            ),
        ),
    )

    relevant_imports = tuple(
        imp for imp in (detected_imports.all_imports if python_infer_subsystem.
                        string_imports else detected_imports.explicit_imports)
        if imp not in combined_stdlib)

    owners_per_import = await MultiGet(
        Get(PythonModuleOwners, PythonModule(imported_module))
        for imported_module in relevant_imports)
    merged_result: set[Address] = set()
    for owners, imp in zip(owners_per_import, relevant_imports):
        merged_result.update(owners.unambiguous)
        address = wrapped_tgt.target.address
        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_via_ignores(
            owners.ambiguous)
        if maybe_disambiguated:
            merged_result.add(maybe_disambiguated)

    return InferredDependencies(sorted(merged_result),
                                sibling_dependencies_inferrable=True)
Esempio n. 16
0
async def inject_pex_binary_entry_point_dependency(
        request: InjectPexBinaryEntryPointDependency,
        python_infer_subsystem: PythonInferSubsystem) -> InjectedDependencies:
    if not python_infer_subsystem.entry_points:
        return InjectedDependencies()
    original_tgt = await Get(WrappedTarget, Address,
                             request.dependencies_field.address)
    entry_point = await Get(
        ResolvedPexEntryPoint,
        ResolvePexEntryPointRequest(
            original_tgt.target[PexEntryPointField],
            original_tgt.target[DeprecatedPexBinarySources]),
    )
    if entry_point.val is None:
        return InjectedDependencies()
    module, _, _func = entry_point.val.partition(":")
    owners = await Get(PythonModuleOwners, PythonModule(module))
    # TODO: remove the check for == self once the `sources` field is removed.
    return InjectedDependencies(owner for owner in owners
                                if owner != request.dependencies_field.address)
Esempio n. 17
0
async def inject_pex_binary_entry_point_dependency(
    request: InjectPexBinaryEntryPointDependency, python_infer_subsystem: PythonInferSubsystem
) -> InjectedDependencies:
    if not python_infer_subsystem.entry_points:
        return InjectedDependencies()
    original_tgt = await Get(WrappedTarget, Address, request.dependencies_field.address)
    entry_point_field = original_tgt.target[PexEntryPointField]
    if entry_point_field.value is None:
        return InjectedDependencies()

    explicitly_provided_deps, entry_point = await MultiGet(
        Get(ExplicitlyProvidedDependencies, DependenciesRequest(original_tgt.target[Dependencies])),
        Get(ResolvedPexEntryPoint, ResolvePexEntryPointRequest(entry_point_field)),
    )
    if entry_point.val is None:
        return InjectedDependencies()
    owners = await Get(PythonModuleOwners, PythonModule(entry_point.val.module))
    address = original_tgt.target.address
    explicitly_provided_deps.maybe_warn_of_ambiguous_dependency_inference(
        owners.ambiguous,
        address,
        # If the entry point was specified as a file, like `app.py`, we know the module must
        # live in the pex_binary's directory or subdirectory, so the owners must be ancestors.
        owners_must_be_ancestors=entry_point.file_name_used,
        import_reference="module",
        context=(
            f"The pex_binary target {address} has the field "
            f"`entry_point={repr(entry_point_field.value.spec)}`, which "
            f"maps to the Python module `{entry_point.val.module}`"
        ),
    )
    maybe_disambiguated = explicitly_provided_deps.disambiguated(
        owners.ambiguous, owners_must_be_ancestors=entry_point.file_name_used
    )
    unambiguous_owners = owners.unambiguous or (
        (maybe_disambiguated,) if maybe_disambiguated else ()
    )
    return InjectedDependencies(unambiguous_owners)
Esempio n. 18
0
async def inject_cloud_function_handler_dependency(
    request: InjectPythonCloudFunctionHandlerDependency,
    python_infer_subsystem: PythonInferSubsystem,
) -> InjectedDependencies:
    if not python_infer_subsystem.entry_points:
        return InjectedDependencies()
    original_tgt = await Get(WrappedTarget, Address,
                             request.dependencies_field.address)
    explicitly_provided_deps, handler = await MultiGet(
        Get(ExplicitlyProvidedDependencies,
            DependenciesRequest(original_tgt.target[Dependencies])),
        Get(
            ResolvedPythonGoogleHandler,
            ResolvePythonGoogleHandlerRequest(
                original_tgt.target[PythonGoogleCloudFunctionHandlerField]),
        ),
    )
    module, _, _func = handler.val.partition(":")
    owners = await Get(PythonModuleOwners, PythonModule(module))
    address = original_tgt.target.address
    explicitly_provided_deps.maybe_warn_of_ambiguous_dependency_inference(
        owners.ambiguous,
        address,
        # If the handler was specified as a file, like `app.py`, we know the module must
        # live in the python_google_cloud_function's directory or subdirectory, so the owners must be ancestors.
        owners_must_be_ancestors=handler.file_name_used,
        import_reference="module",
        context=
        (f"The python_google_cloud_function target {address} has the field "
         f"`handler={repr(original_tgt.target[PythonGoogleCloudFunctionHandlerField].value)}`, which maps "
         f"to the Python module `{module}`"),
    )
    maybe_disambiguated = explicitly_provided_deps.disambiguated(
        owners.ambiguous, owners_must_be_ancestors=handler.file_name_used)
    unambiguous_owners = owners.unambiguous or (
        (maybe_disambiguated, ) if maybe_disambiguated else ())
    return InjectedDependencies(unambiguous_owners)
Esempio n. 19
0
def test_create_module_from_path(stripped_path: PurePath,
                                 expected: str) -> None:
    assert PythonModule.create_from_stripped_path(
        stripped_path) == PythonModule(expected)
Esempio n. 20
0
 def get_owner(module: str) -> Optional[Address]:
     return self.request_single_product(
         PythonModuleOwner,
         Params(PythonModule(module), options_bootstrapper)).address
Esempio n. 21
0
async def find_putative_targets(
    req: PutativePythonTargetsRequest,
    all_owned_sources: AllOwnedSources,
    python_setup: PythonSetup,
) -> PutativeTargets:
    # Find library/test/test_util targets.

    all_py_files_globs: PathGlobs = req.search_paths.path_globs("*.py")
    all_py_files = await Get(Paths, PathGlobs, all_py_files_globs)
    unowned_py_files = set(all_py_files.files) - set(all_owned_sources)
    classified_unowned_py_files = classify_source_files(unowned_py_files)
    pts = []
    for tgt_type, paths in classified_unowned_py_files.items():
        for dirname, filenames in group_by_dir(paths).items():
            if issubclass(tgt_type, PythonTestsGeneratorTarget):
                name = "tests"
                kwargs = {"name": name}
            elif issubclass(tgt_type, PythonTestUtilsGeneratorTarget):
                name = "test_utils"
                kwargs = {"name": name}
            else:
                name = os.path.basename(dirname)
                kwargs = {}
            if (
                python_setup.tailor_ignore_solitary_init_files
                and tgt_type == PythonSourcesGeneratorTarget
                and filenames == {"__init__.py"}
            ):
                continue
            pts.append(
                PutativeTarget.for_target_type(
                    tgt_type, dirname, name, sorted(filenames), kwargs=kwargs
                )
            )

    if python_setup.tailor_requirements_targets:
        # Find requirements files.
        all_requirements_files_globs: PathGlobs = req.search_paths.path_globs("*requirements*.txt")
        all_requirements_files = await Get(Paths, PathGlobs, all_requirements_files_globs)
        unowned_requirements_files = set(all_requirements_files.files) - set(all_owned_sources)
        for req_file in unowned_requirements_files:
            path, name = os.path.split(req_file)
            pts.append(
                PutativeTarget(
                    path=path,
                    # python_requirements is a macro and doesn't take a name argument, but the
                    # PutativeTarget still needs a name for display purposes.
                    name=name,
                    type_alias="python_requirements",
                    triggering_sources=[req_file],
                    owned_sources=[req_file],
                    addressable=False,
                    kwargs={} if name == "requirements.txt" else {"requirements_relpath": name},
                )
            )

    if python_setup.tailor_pex_binary_targets:
        # Find binary targets.

        # Get all files whose content indicates that they are entry points.
        digest_contents = await Get(DigestContents, PathGlobs, all_py_files_globs)
        entry_points = [
            file_content.path
            for file_content in digest_contents
            if is_entry_point(file_content.content)
        ]

        # Get the modules for these entry points.
        src_roots = await Get(
            SourceRootsResult, SourceRootsRequest, SourceRootsRequest.for_files(entry_points)
        )
        module_to_entry_point = {}
        for entry_point in entry_points:
            entry_point_path = PurePath(entry_point)
            src_root = src_roots.path_to_root[entry_point_path]
            stripped_entry_point = entry_point_path.relative_to(src_root.path)
            module = PythonModule.create_from_stripped_path(stripped_entry_point)
            module_to_entry_point[module.module] = entry_point

        # Get existing binary targets for these entry points.
        entry_point_dirs = {os.path.dirname(entry_point) for entry_point in entry_points}
        possible_existing_binary_targets = await Get(
            UnexpandedTargets, AddressSpecs(AscendantAddresses(d) for d in entry_point_dirs)
        )
        possible_existing_binary_entry_points = await MultiGet(
            Get(ResolvedPexEntryPoint, ResolvePexEntryPointRequest(t[PexEntryPointField]))
            for t in possible_existing_binary_targets
            if t.has_field(PexEntryPointField)
        )
        possible_existing_entry_point_modules = {
            rep.val.module for rep in possible_existing_binary_entry_points if rep.val
        }
        unowned_entry_point_modules = (
            module_to_entry_point.keys() - possible_existing_entry_point_modules
        )

        # Generate new targets for entry points that don't already have one.
        for entry_point_module in unowned_entry_point_modules:
            entry_point = module_to_entry_point[entry_point_module]
            path, fname = os.path.split(entry_point)
            name = os.path.splitext(fname)[0]
            pts.append(
                PutativeTarget.for_target_type(
                    target_type=PexBinary,
                    path=path,
                    name=name,
                    triggering_sources=tuple(),
                    kwargs={"name": name, "entry_point": fname},
                )
            )

    return PutativeTargets(pts)
Esempio n. 22
0
async def infer_python_dependencies_via_imports(
    request: InferPythonImportDependencies,
    python_infer_subsystem: PythonInferSubsystem,
    python_setup: PythonSetup,
) -> InferredDependencies:
    if not python_infer_subsystem.imports:
        return InferredDependencies([])

    wrapped_tgt = await Get(WrappedTarget, Address,
                            request.sources_field.address)
    explicitly_provided_deps, detected_imports = await MultiGet(
        Get(ExplicitlyProvidedDependencies,
            DependenciesRequest(wrapped_tgt.target[Dependencies])),
        Get(
            ParsedPythonImports,
            ParsePythonImportsRequest(
                cast(PythonSourceField, request.sources_field),
                InterpreterConstraints.create_from_targets(
                    [wrapped_tgt.target], python_setup),
                string_imports=python_infer_subsystem.string_imports,
                string_imports_min_dots=python_infer_subsystem.
                string_imports_min_dots,
            ),
        ),
    )

    owners_per_import = await MultiGet(
        Get(PythonModuleOwners, PythonModule(imported_module))
        for imported_module in detected_imports)

    merged_result: set[Address] = set()
    unowned_imports: set[str] = set()
    address = wrapped_tgt.target.address
    for owners, imp in zip(owners_per_import, detected_imports):
        merged_result.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:
            merged_result.add(maybe_disambiguated)

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

    unowned_dependency_behavior = python_infer_subsystem.unowned_dependency_behavior
    if unowned_imports and unowned_dependency_behavior is not UnownedDependencyUsage.DoNothing:
        raise_error = unowned_dependency_behavior is UnownedDependencyUsage.RaiseError
        log = logger.error if raise_error else logger.warning
        log(f"The following imports in {address} have no owners:\n\n{bullet_list(unowned_imports)}\n\n"
            "If you are expecting this import to be provided by your own firstparty code, ensure that it is contained within a source root. "
            "Otherwise if you are using a requirements file, consider adding the relevant package.\n"
            "Otherwise consider declaring a `python_requirement_library` target, which can then be inferred.\n"
            f"See {doc_url('python-third-party-dependencies')}")

        if raise_error:
            raise UnownedDependencyError(
                "One or more unowned dependencies detected. Check logs for more details."
            )

    return InferredDependencies(sorted(merged_result))
Esempio n. 23
0
 def get_owners(module: str) -> List[Address]:
     return list(
         rule_runner.request(PythonModuleOwners, [PythonModule(module)]))
Esempio n. 24
0
 def get_owner(module: str) -> Optional[Address]:
     return self.request_product(
         PythonModuleOwner,
         [PythonModule(module), options_bootstrapper]).address
Esempio n. 25
0
def test_module_address_spec() -> None:
    assert PythonModule("helloworld.app").address_spec(
        source_root=".") == AscendantAddresses(directory="helloworld/app")
    assert PythonModule("helloworld.app").address_spec(
        source_root="src/python") == AscendantAddresses(
            directory="src/python/helloworld/app")