Пример #1
0
def test_source_root_suffixes() -> None:
    srs = SourceRoots(["src/python", "/"])
    assert SourceRoot("src/python") == srs.strict_find_by_path(
        "src/python/foo/bar.py")
    assert SourceRoot("src/python/foo/src/python") == srs.strict_find_by_path(
        "src/python/foo/src/python/bar.py")
    assert SourceRoot(".") == srs.strict_find_by_path("foo/bar.py")
Пример #2
0
def test_fixed_source_roots() -> None:
    srs = SourceRoots(["/root1", "/foo/root2", "/root1/root3"])
    assert SourceRoot("root1") == srs.strict_find_by_path("root1/bar.py")
    assert SourceRoot("foo/root2") == srs.strict_find_by_path(
        "foo/root2/bar/baz.py")
    assert SourceRoot("root1/root3") == srs.strict_find_by_path(
        "root1/root3/qux.py")
    assert SourceRoot("root1/root3") == srs.strict_find_by_path(
        "root1/root3/qux/quux.py")
    assert SourceRoot("root1/root3") == srs.strict_find_by_path("root1/root3")
    with pytest.raises(NoSourceRootError):
        srs.strict_find_by_path("blah/blah.py")
Пример #3
0
    def create(cls, file_content: FileContent, source_roots: SourceRoots,
               global_options: GlobalOptions) -> "BackendInfo":
        source_root = source_roots.strict_find_by_path(file_content.path)
        stripped_path = file_content.path[len(source_root.path) + 1:]
        module_name = os.path.dirname(stripped_path).replace(os.sep, ".")

        # NB: Both `build_file_aliases` and `target_types` are used by both V1 and V2.
        v1_entry_points = ("register_goals", "global_subsystems")
        v2_entry_points = ("rules", )

        def any_entry_points_registered(entry_points: Sequence[str]) -> bool:
            return any(f"def {entry_point}()" in file_content.content.decode()
                       for entry_point in entry_points)

        activated_v1_backends = {
            "pants.build_graph",
            "pants.core_tasks",
            *global_options.options.backend_packages,
        }
        activated_v2_backends = {
            "pants.core",
            "pants.backend.pants_info",
            "pants.backend.project_info",
            *global_options.options.backend_packages2,
        }

        return cls(
            name=module_name,
            description=hackily_get_module_docstring(
                file_content.content.decode()),
            is_v1=any_entry_points_registered(v1_entry_points),
            is_v2=any_entry_points_registered(v2_entry_points),
            is_v1_activated=module_name in activated_v1_backends,
            is_v2_activated=module_name in activated_v2_backends,
        )
Пример #4
0
def test_source_root_at_buildroot() -> None:
    srs = SourceRoots(["/"])
    assert SourceRoot(".") == srs.strict_find_by_path("foo/bar.py")
    assert SourceRoot(".") == srs.strict_find_by_path("foo/")
    assert SourceRoot(".") == srs.strict_find_by_path("foo")
    with pytest.raises(NoSourceRootError):
        srs.strict_find_by_path("../foo/bar.py")
Пример #5
0
def test_source_root_patterns() -> None:
    srs = SourceRoots(["src/*", "/project/*"])
    assert SourceRoot("src/python") == srs.strict_find_by_path(
        "src/python/foo/bar.py")
    assert SourceRoot("src/python/foo/src/shell") == srs.strict_find_by_path(
        "src/python/foo/src/shell/bar.sh")
    assert SourceRoot("project/python") == srs.strict_find_by_path(
        "project/python/foo/bar.py")
    with pytest.raises(NoSourceRootError):
        srs.strict_find_by_path("prefix/project/python/foo/bar.py")
Пример #6
0
    def create(cls, file_content: FileContent, source_roots: SourceRoots,
               global_options: GlobalOptions) -> "BackendInfo":
        source_root = source_roots.safe_find_by_path(file_content.path)
        if source_root is None:
            raise NoSourceRootError(
                f"Could not find a source root for `{file_content.path}`.")
        stripped_path = file_content.path[len(source_root.path) + 1:]
        module_name = os.path.dirname(stripped_path).replace(os.sep, ".")

        v1_entry_points = ("register_goals", "global_subsystems",
                           "build_file_aliases")
        # NB: We intentionally do not check for `targets2` because even V1 is expected to have
        # Target API bindings.
        v2_entry_points = ("rules", "build_file_aliases2")

        def any_entry_points_registered(entry_points: Sequence[str]) -> bool:
            return any(f"def {entry_point}()" in file_content.content.decode()
                       for entry_point in entry_points)

        activated_v1_backends = {
            "pants.build_graph",
            "pants.core_tasks",
            *global_options.options.backend_packages,
        }
        activated_v2_backends = {
            "pants.rules.core", *global_options.options.backend_packages2
        }

        return cls(
            name=module_name,
            description=hackily_get_module_docstring(
                file_content.content.decode()),
            is_v1=any_entry_points_registered(v1_entry_points),
            is_v2=any_entry_points_registered(v2_entry_points),
            is_v1_activated=module_name in activated_v1_backends,
            is_v2_activated=module_name in activated_v2_backends,
        )
Пример #7
0
def source_root_or_raise(source_roots: SourceRoots, path: str) -> str:
    """Find the source root for the given path, or raise if none is found."""
    source_root = source_roots.find_by_path(path)
    if not source_root:
        raise NoSourceRootError(f"Found no source root for {path}")
    return source_root.path
Пример #8
0
def source_root_or_raise(source_roots: SourceRoots, path: str) -> str:
    source_root = source_roots.find_by_path(path)
    if not source_root:
        raise NoSourceRootError(f'Found no source root for {path}')
    return source_root.path
Пример #9
0
def find_packages(
    source_roots: SourceRoots,
    tgts_and_stripped_srcs: Iterable[Tuple[Target, SourceRootStrippedSources]],
    init_py_contents: FilesContent,
    py2: bool,
) -> Tuple[Tuple[str, ...], Tuple[str, ...], Tuple[PackageDatum, ...]]:
    """Analyze the package structure for the given sources.

    Returns a tuple (packages, namespace_packages, package_data), suitable for use as setup()
    kwargs.
    """
    # Find all packages implied by the sources.
    packages: Set[str] = set()
    package_data: Dict[str, List[str]] = defaultdict(list)
    for tgt, stripped_srcs in tgts_and_stripped_srcs:
        if tgt.has_field(PythonSources):
            for file in stripped_srcs.snapshot.files:
                # Python 2: An __init__.py file denotes a package.
                # Python 3: Any directory containing python source files is a package.
                if not py2 or os.path.basename(file) == "__init__.py":
                    packages.add(
                        os.path.dirname(file).replace(os.path.sep, "."))

    # Add any packages implied by ancestor __init__.py files.
    # Note that init_py_contents includes all __init__.py files, not just ancestors, but
    # that's fine - the others will already have been found in tgts_and_stripped_srcs above.
    for init_py_content in init_py_contents:
        packages.add(
            os.path.dirname(init_py_content.path).replace(os.path.sep, "."))

    # Now find all package_data.
    for tgt, stripped_srcs in tgts_and_stripped_srcs:
        if tgt.has_field(ResourcesSources):
            source_root = source_roots.strict_find_by_path(
                tgt.address.spec_path).path
            resource_dir_relpath = os.path.relpath(tgt.address.spec_path,
                                                   source_root)
            # Find the closest enclosing package, if any.  Resources will be loaded relative to that.
            package: str = resource_dir_relpath.replace(os.path.sep, ".")
            while package and package not in packages:
                package = package.rpartition(".")[0]
            # If resource is not in a package, ignore it. There's no principled way to load it anyway.
            if package:
                package_dir_relpath = package.replace(".", os.path.sep)
                package_data[package].extend(
                    os.path.relpath(file, package_dir_relpath)
                    for file in stripped_srcs.snapshot.files)

    # See which packages are pkg_resources-style namespace packages.
    # Note that implicit PEP 420 namespace packages and pkgutil-style namespace packages
    # should *not* be listed in the setup namespace_packages kwarg. That's for pkg_resources-style
    # namespace packages only. See https://github.com/pypa/sample-namespace-packages/.
    namespace_packages: Set[str] = set()
    init_py_by_path: Dict[str, bytes] = {
        ipc.path: ipc.content
        for ipc in init_py_contents
    }
    for pkg in packages:
        path = os.path.join(pkg.replace(".", os.path.sep), "__init__.py")
        if path in init_py_by_path and declares_pkg_resources_namespace_package(
                init_py_by_path[path].decode()):
            namespace_packages.add(pkg)

    return (
        tuple(sorted(packages)),
        tuple(sorted(namespace_packages)),
        tuple((pkg, tuple(sorted(files)))
              for pkg, files in package_data.items()),
    )