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")
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")
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, )
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")
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")
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, )
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
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
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()), )