refactor: reimplement writing namespace pkgs in Starlark (#2882)
With this PR I would like to facilitate the implementation of the venv
layouts because we can in theory take the `srcs` and the `data` within
the `py_library` and then use the `expand_template` to write the extra
Python files if the namespace_pkgs flag is enabled.
The old Python code has been removed and the extra generated files are
written out with `bazel_skylib` `copy_file`.
The implicit `namespace_pkg` init files are included to `py_library`
if the `site-packages` config flag is set to false and I think this
may help with continuing the implementation, but it currently is still
not working as expected (see comment).
Work towards #2156
diff --git a/python/config_settings/BUILD.bazel b/python/config_settings/BUILD.bazel
index 1772a34..ee15828 100644
--- a/python/config_settings/BUILD.bazel
+++ b/python/config_settings/BUILD.bazel
@@ -217,6 +217,15 @@
visibility = ["//visibility:public"],
)
+config_setting(
+ name = "is_venvs_site_packages",
+ flag_values = {
+ ":venvs_site_packages": VenvsSitePackages.YES,
+ },
+ # NOTE: Only public because it is used in whl_library repos.
+ visibility = ["//visibility:public"],
+)
+
define_pypi_internal_flags(
name = "define_pypi_internal_flags",
)
diff --git a/python/private/pypi/BUILD.bazel b/python/private/pypi/BUILD.bazel
index 84e0535..e9036c3 100644
--- a/python/private/pypi/BUILD.bazel
+++ b/python/private/pypi/BUILD.bazel
@@ -18,6 +18,11 @@
licenses(["notice"])
+exports_files(
+ srcs = ["namespace_pkg_tmpl.py"],
+ visibility = ["//visibility:public"],
+)
+
filegroup(
name = "distribution",
srcs = glob(
diff --git a/python/private/pypi/namespace_pkg_tmpl.py b/python/private/pypi/namespace_pkg_tmpl.py
new file mode 100644
index 0000000..a21b846
--- /dev/null
+++ b/python/private/pypi/namespace_pkg_tmpl.py
@@ -0,0 +1,2 @@
+# __path__ manipulation added by bazel-contrib/rules_python to support namespace pkgs.
+__path__ = __import__("pkgutil").extend_path(__path__, __name__)
diff --git a/python/private/pypi/namespace_pkgs.bzl b/python/private/pypi/namespace_pkgs.bzl
new file mode 100644
index 0000000..bf4689a
--- /dev/null
+++ b/python/private/pypi/namespace_pkgs.bzl
@@ -0,0 +1,83 @@
+"""Utilities to get where we should write namespace pkg paths."""
+
+load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
+
+_ext = struct(
+ py = ".py",
+ pyd = ".pyd",
+ so = ".so",
+ pyc = ".pyc",
+)
+
+_TEMPLATE = Label("//python/private/pypi:namespace_pkg_tmpl.py")
+
+def _add_all(dirname, dirs):
+ dir_path = "."
+ for dir_name in dirname.split("/"):
+ dir_path = "{}/{}".format(dir_path, dir_name)
+ dirs[dir_path[2:]] = None
+
+def get_files(*, srcs, ignored_dirnames = [], root = None):
+ """Get the list of filenames to write the namespace pkg files.
+
+ Args:
+ srcs: {type}`src` a list of files to be passed to {bzl:obj}`py_library`
+ as `srcs` and `data`. This is usually a result of a {obj}`glob`.
+ ignored_dirnames: {type}`str` a list of patterns to ignore.
+ root: {type}`str` the prefix to use as the root.
+
+ Returns:
+ {type}`src` a list of paths to write the namespace pkg `__init__.py` file.
+ """
+ dirs = {}
+ ignored = {i: None for i in ignored_dirnames}
+
+ if root:
+ _add_all(root, ignored)
+
+ for file in srcs:
+ dirname, _, filename = file.rpartition("/")
+
+ if filename == "__init__.py":
+ ignored[dirname] = None
+ dirname, _, _ = dirname.rpartition("/")
+ elif filename.endswith(_ext.py):
+ pass
+ elif filename.endswith(_ext.pyc):
+ pass
+ elif filename.endswith(_ext.pyd):
+ pass
+ elif filename.endswith(_ext.so):
+ pass
+ else:
+ continue
+
+ if dirname in dirs or not dirname:
+ continue
+
+ _add_all(dirname, dirs)
+
+ return sorted([d for d in dirs if d not in ignored])
+
+def create_inits(**kwargs):
+ """Create init files and return the list to be included `py_library` srcs.
+
+ Args:
+ **kwargs: passed to {obj}`get_files`.
+
+ Returns:
+ {type}`list[str]` to be included as part of `py_library`.
+ """
+ srcs = []
+ for out in get_files(**kwargs):
+ src = "{}/__init__.py".format(out)
+ srcs.append(srcs)
+
+ copy_file(
+ name = "_cp_{}_namespace".format(out),
+ src = _TEMPLATE,
+ out = src,
+ **kwargs
+ )
+
+ return srcs
diff --git a/python/private/pypi/whl_installer/arguments.py b/python/private/pypi/whl_installer/arguments.py
index ea609be..57dae45 100644
--- a/python/private/pypi/whl_installer/arguments.py
+++ b/python/private/pypi/whl_installer/arguments.py
@@ -58,11 +58,6 @@
help="Additional data exclusion parameters to add to the pip packages BUILD file.",
)
parser.add_argument(
- "--enable_implicit_namespace_pkgs",
- action="store_true",
- help="Disables conversion of implicit namespace packages into pkg-util style packages.",
- )
- parser.add_argument(
"--environment",
action="store",
help="Extra environment variables to set on the pip environment.",
diff --git a/python/private/pypi/whl_installer/wheel_installer.py b/python/private/pypi/whl_installer/wheel_installer.py
index 600d45f..a6a9dd0 100644
--- a/python/private/pypi/whl_installer/wheel_installer.py
+++ b/python/private/pypi/whl_installer/wheel_installer.py
@@ -27,7 +27,7 @@
from pip._vendor.packaging.utils import canonicalize_name
-from python.private.pypi.whl_installer import arguments, namespace_pkgs, wheel
+from python.private.pypi.whl_installer import arguments, wheel
def _configure_reproducible_wheels() -> None:
@@ -77,35 +77,10 @@
return None, None
-def _setup_namespace_pkg_compatibility(wheel_dir: str) -> None:
- """Converts native namespace packages to pkgutil-style packages
-
- Namespace packages can be created in one of three ways. They are detailed here:
- https://packaging.python.org/guides/packaging-namespace-packages/#creating-a-namespace-package
-
- 'pkgutil-style namespace packages' (2) and 'pkg_resources-style namespace packages' (3) works in Bazel, but
- 'native namespace packages' (1) do not.
-
- We ensure compatibility with Bazel of method 1 by converting them into method 2.
-
- Args:
- wheel_dir: the directory of the wheel to convert
- """
-
- namespace_pkg_dirs = namespace_pkgs.implicit_namespace_packages(
- wheel_dir,
- ignored_dirnames=["%s/bin" % wheel_dir],
- )
-
- for ns_pkg_dir in namespace_pkg_dirs:
- namespace_pkgs.add_pkgutil_style_namespace_pkg_init(ns_pkg_dir)
-
-
def _extract_wheel(
wheel_file: str,
extras: Dict[str, Set[str]],
enable_pipstar: bool,
- enable_implicit_namespace_pkgs: bool,
platforms: List[wheel.Platform],
installation_dir: Path = Path("."),
) -> None:
@@ -116,15 +91,11 @@
installation_dir: the destination directory for installation of the wheel.
extras: a list of extras to add as dependencies for the installed wheel
enable_pipstar: if true, turns off certain operations.
- enable_implicit_namespace_pkgs: if true, disables conversion of implicit namespace packages and will unzip as-is
"""
whl = wheel.Wheel(wheel_file)
whl.unzip(installation_dir)
- if not enable_implicit_namespace_pkgs:
- _setup_namespace_pkg_compatibility(installation_dir)
-
metadata = {
"entry_points": [
{
@@ -168,7 +139,6 @@
wheel_file=whl,
extras=extras,
enable_pipstar=args.enable_pipstar,
- enable_implicit_namespace_pkgs=args.enable_implicit_namespace_pkgs,
platforms=arguments.get_platforms(args),
)
return
diff --git a/python/private/pypi/whl_library.bzl b/python/private/pypi/whl_library.bzl
index 17ee3d3..c271449 100644
--- a/python/private/pypi/whl_library.bzl
+++ b/python/private/pypi/whl_library.bzl
@@ -173,9 +173,6 @@
json.encode(struct(arg = rctx.attr.pip_data_exclude)),
]
- if rctx.attr.enable_implicit_namespace_pkgs:
- args.append("--enable_implicit_namespace_pkgs")
-
env = {}
if rctx.attr.environment != None:
for key, value in rctx.attr.environment.items():
@@ -389,6 +386,8 @@
metadata_name = metadata.name,
metadata_version = metadata.version,
requires_dist = metadata.requires_dist,
+ # TODO @aignas 2025-05-17: maybe have a build flag for this instead
+ enable_implicit_namespace_pkgs = rctx.attr.enable_implicit_namespace_pkgs,
# TODO @aignas 2025-04-14: load through the hub:
annotation = None if not rctx.attr.annotation else struct(**json.decode(rctx.read(rctx.attr.annotation))),
data_exclude = rctx.attr.pip_data_exclude,
@@ -457,6 +456,8 @@
name = whl_path.basename,
dep_template = rctx.attr.dep_template or "@{}{{name}}//:{{target}}".format(rctx.attr.repo_prefix),
entry_points = entry_points,
+ # TODO @aignas 2025-05-17: maybe have a build flag for this instead
+ enable_implicit_namespace_pkgs = rctx.attr.enable_implicit_namespace_pkgs,
# TODO @aignas 2025-04-14: load through the hub:
dependencies = metadata["deps"],
dependencies_by_platform = metadata["deps_by_platform"],
@@ -580,7 +581,6 @@
Label("//python/private/pypi/whl_installer:wheel.py"),
Label("//python/private/pypi/whl_installer:wheel_installer.py"),
Label("//python/private/pypi/whl_installer:arguments.py"),
- Label("//python/private/pypi/whl_installer:namespace_pkgs.py"),
] + record_files.values(),
),
"_rule_name": attr.string(default = "whl_library"),
diff --git a/python/private/pypi/whl_library_targets.bzl b/python/private/pypi/whl_library_targets.bzl
index e0c03a1..3529566 100644
--- a/python/private/pypi/whl_library_targets.bzl
+++ b/python/private/pypi/whl_library_targets.bzl
@@ -30,6 +30,7 @@
"WHEEL_FILE_IMPL_LABEL",
"WHEEL_FILE_PUBLIC_LABEL",
)
+load(":namespace_pkgs.bzl", "create_inits")
load(":pep508_deps.bzl", "deps")
def whl_library_targets_from_requires(
@@ -113,6 +114,7 @@
copy_executables = {},
entry_points = {},
native = native,
+ enable_implicit_namespace_pkgs = False,
rules = struct(
copy_file = copy_file,
py_binary = py_binary,
@@ -153,6 +155,8 @@
data: {type}`list[str]` A list of labels to include as part of the `data` attribute in `py_library`.
entry_points: {type}`dict[str, str]` The mapping between the script
name and the python file to use. DEPRECATED.
+ enable_implicit_namespace_pkgs: {type}`boolean` generate __init__.py
+ files for namespace pkgs.
native: {type}`native` The native struct for overriding in tests.
rules: {type}`struct` A struct with references to rules for creating targets.
"""
@@ -293,6 +297,14 @@
)
if hasattr(rules, "py_library"):
+ srcs = native.glob(
+ ["site-packages/**/*.py"],
+ exclude = srcs_exclude,
+ # Empty sources are allowed to support wheels that don't have any
+ # pure-Python code, e.g. pymssql, which is written in Cython.
+ allow_empty = True,
+ )
+
# NOTE: pyi files should probably be excluded because they're carried
# by the pyi_srcs attribute. However, historical behavior included
# them in data and some tools currently rely on that.
@@ -309,23 +321,31 @@
if item not in _data_exclude:
_data_exclude.append(item)
+ data = data + native.glob(
+ ["site-packages/**/*"],
+ exclude = _data_exclude,
+ )
+
+ pyi_srcs = native.glob(
+ ["site-packages/**/*.pyi"],
+ allow_empty = True,
+ )
+
+ if enable_implicit_namespace_pkgs:
+ srcs = srcs + getattr(native, "select", select)({
+ Label("//python/config_settings:is_venvs_site_packages"): [],
+ "//conditions:default": create_inits(
+ srcs = srcs + data + pyi_srcs,
+ ignore_dirnames = [], # If you need to ignore certain folders, you can patch rules_python here to do so.
+ root = "site-packages",
+ ),
+ })
+
rules.py_library(
name = py_library_label,
- srcs = native.glob(
- ["site-packages/**/*.py"],
- exclude = srcs_exclude,
- # Empty sources are allowed to support wheels that don't have any
- # pure-Python code, e.g. pymssql, which is written in Cython.
- allow_empty = True,
- ),
- pyi_srcs = native.glob(
- ["site-packages/**/*.pyi"],
- allow_empty = True,
- ),
- data = data + native.glob(
- ["site-packages/**/*"],
- exclude = _data_exclude,
- ),
+ srcs = srcs,
+ pyi_srcs = pyi_srcs,
+ data = data,
# This makes this directory a top-level in the python import
# search path for anything that depends on this.
imports = ["site-packages"],
diff --git a/tests/pypi/namespace_pkgs/BUILD.bazel b/tests/pypi/namespace_pkgs/BUILD.bazel
new file mode 100644
index 0000000..57f7962
--- /dev/null
+++ b/tests/pypi/namespace_pkgs/BUILD.bazel
@@ -0,0 +1,5 @@
+load(":namespace_pkgs_tests.bzl", "namespace_pkgs_test_suite")
+
+namespace_pkgs_test_suite(
+ name = "namespace_pkgs_tests",
+)
diff --git a/tests/pypi/namespace_pkgs/namespace_pkgs_tests.bzl b/tests/pypi/namespace_pkgs/namespace_pkgs_tests.bzl
new file mode 100644
index 0000000..7ac938f
--- /dev/null
+++ b/tests/pypi/namespace_pkgs/namespace_pkgs_tests.bzl
@@ -0,0 +1,167 @@
+""
+
+load("@rules_testing//lib:analysis_test.bzl", "test_suite")
+load("//python/private/pypi:namespace_pkgs.bzl", "get_files") # buildifier: disable=bzl-visibility
+
+_tests = []
+
+def test_in_current_dir(env):
+ srcs = [
+ "foo/bar/biz.py",
+ "foo/bee/boo.py",
+ "foo/buu/__init__.py",
+ "foo/buu/bii.py",
+ ]
+ got = get_files(srcs = srcs)
+ expected = [
+ "foo",
+ "foo/bar",
+ "foo/bee",
+ ]
+ env.expect.that_collection(got).contains_exactly(expected)
+
+_tests.append(test_in_current_dir)
+
+def test_find_correct_namespace_packages(env):
+ srcs = [
+ "nested/root/foo/bar/biz.py",
+ "nested/root/foo/bee/boo.py",
+ "nested/root/foo/buu/__init__.py",
+ "nested/root/foo/buu/bii.py",
+ ]
+
+ got = get_files(srcs = srcs, root = "nested/root")
+ expected = [
+ "nested/root/foo",
+ "nested/root/foo/bar",
+ "nested/root/foo/bee",
+ ]
+ env.expect.that_collection(got).contains_exactly(expected)
+
+_tests.append(test_find_correct_namespace_packages)
+
+def test_ignores_empty_directories(_):
+ # because globs do not add directories, this test is not needed
+ pass
+
+_tests.append(test_ignores_empty_directories)
+
+def test_empty_case(env):
+ srcs = [
+ "foo/__init__.py",
+ "foo/bar/__init__.py",
+ "foo/bar/biz.py",
+ ]
+
+ got = get_files(srcs = srcs)
+ expected = []
+ env.expect.that_collection(got).contains_exactly(expected)
+
+_tests.append(test_empty_case)
+
+def test_ignores_non_module_files_in_directories(env):
+ srcs = [
+ "foo/__init__.pyi",
+ "foo/py.typed",
+ ]
+
+ got = get_files(srcs = srcs)
+ expected = []
+ env.expect.that_collection(got).contains_exactly(expected)
+
+_tests.append(test_ignores_non_module_files_in_directories)
+
+def test_parent_child_relationship_of_namespace_pkgs(env):
+ srcs = [
+ "foo/bar/biff/my_module.py",
+ "foo/bar/biff/another_module.py",
+ ]
+
+ got = get_files(srcs = srcs)
+ expected = [
+ "foo",
+ "foo/bar",
+ "foo/bar/biff",
+ ]
+ env.expect.that_collection(got).contains_exactly(expected)
+
+_tests.append(test_parent_child_relationship_of_namespace_pkgs)
+
+def test_parent_child_relationship_of_namespace_and_standard_pkgs(env):
+ srcs = [
+ "foo/bar/biff/__init__.py",
+ "foo/bar/biff/another_module.py",
+ ]
+
+ got = get_files(srcs = srcs)
+ expected = [
+ "foo",
+ "foo/bar",
+ ]
+ env.expect.that_collection(got).contains_exactly(expected)
+
+_tests.append(test_parent_child_relationship_of_namespace_and_standard_pkgs)
+
+def test_parent_child_relationship_of_namespace_and_nested_standard_pkgs(env):
+ srcs = [
+ "foo/bar/__init__.py",
+ "foo/bar/biff/another_module.py",
+ "foo/bar/biff/__init__.py",
+ "foo/bar/boof/big_module.py",
+ "foo/bar/boof/__init__.py",
+ "fim/in_a_ns_pkg.py",
+ ]
+
+ got = get_files(srcs = srcs)
+ expected = [
+ "foo",
+ "fim",
+ ]
+ env.expect.that_collection(got).contains_exactly(expected)
+
+_tests.append(test_parent_child_relationship_of_namespace_and_nested_standard_pkgs)
+
+def test_recognized_all_nonstandard_module_types(env):
+ srcs = [
+ "ayy/my_module.pyc",
+ "bee/ccc/dee/eee.so",
+ "eff/jee/aych.pyd",
+ ]
+
+ expected = [
+ "ayy",
+ "bee",
+ "bee/ccc",
+ "bee/ccc/dee",
+ "eff",
+ "eff/jee",
+ ]
+ got = get_files(srcs = srcs)
+ env.expect.that_collection(got).contains_exactly(expected)
+
+_tests.append(test_recognized_all_nonstandard_module_types)
+
+def test_skips_ignored_directories(env):
+ srcs = [
+ "root/foo/boo/my_module.py",
+ "root/foo/bar/another_module.py",
+ ]
+
+ expected = [
+ "root/foo",
+ "root/foo/bar",
+ ]
+ got = get_files(
+ srcs = srcs,
+ ignored_dirnames = ["root/foo/boo"],
+ root = "root",
+ )
+ env.expect.that_collection(got).contains_exactly(expected)
+
+_tests.append(test_skips_ignored_directories)
+
+def namespace_pkgs_test_suite(name):
+ test_suite(
+ name = name,
+ basic_tests = _tests,
+ )
diff --git a/tests/pypi/whl_installer/BUILD.bazel b/tests/pypi/whl_installer/BUILD.bazel
index 040e4d7..060d2bc 100644
--- a/tests/pypi/whl_installer/BUILD.bazel
+++ b/tests/pypi/whl_installer/BUILD.bazel
@@ -17,17 +17,6 @@
)
py_test(
- name = "namespace_pkgs_test",
- size = "small",
- srcs = [
- "namespace_pkgs_test.py",
- ],
- deps = [
- ":lib",
- ],
-)
-
-py_test(
name = "platform_test",
size = "small",
srcs = [
diff --git a/tests/pypi/whl_installer/arguments_test.py b/tests/pypi/whl_installer/arguments_test.py
index 5538054..2352d8e 100644
--- a/tests/pypi/whl_installer/arguments_test.py
+++ b/tests/pypi/whl_installer/arguments_test.py
@@ -36,7 +36,6 @@
self.assertIn("requirement", args_dict)
self.assertIn("extra_pip_args", args_dict)
self.assertEqual(args_dict["pip_data_exclude"], [])
- self.assertEqual(args_dict["enable_implicit_namespace_pkgs"], False)
self.assertEqual(args_dict["extra_pip_args"], extra_pip_args)
def test_deserialize_structured_args(self) -> None:
diff --git a/tests/pypi/whl_installer/namespace_pkgs_test.py b/tests/pypi/whl_installer/namespace_pkgs_test.py
deleted file mode 100644
index fbbd509..0000000
--- a/tests/pypi/whl_installer/namespace_pkgs_test.py
+++ /dev/null
@@ -1,192 +0,0 @@
-# Copyright 2023 The Bazel Authors. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import os
-import pathlib
-import shutil
-import tempfile
-import unittest
-from typing import Optional, Set
-
-from python.private.pypi.whl_installer import namespace_pkgs
-
-
-class TempDir:
- def __init__(self) -> None:
- self.dir = tempfile.mkdtemp()
-
- def root(self) -> str:
- return self.dir
-
- def add_dir(self, rel_path: str) -> None:
- d = pathlib.Path(self.dir, rel_path)
- d.mkdir(parents=True)
-
- def add_file(self, rel_path: str, contents: Optional[str] = None) -> None:
- f = pathlib.Path(self.dir, rel_path)
- f.parent.mkdir(parents=True, exist_ok=True)
- if contents:
- with open(str(f), "w") as writeable_f:
- writeable_f.write(contents)
- else:
- f.touch()
-
- def remove(self) -> None:
- shutil.rmtree(self.dir)
-
-
-class TestImplicitNamespacePackages(unittest.TestCase):
- def assertPathsEqual(self, actual: Set[pathlib.Path], expected: Set[str]) -> None:
- self.assertEqual(actual, {pathlib.Path(p) for p in expected})
-
- def test_in_current_directory(self) -> None:
- directory = TempDir()
- directory.add_file("foo/bar/biz.py")
- directory.add_file("foo/bee/boo.py")
- directory.add_file("foo/buu/__init__.py")
- directory.add_file("foo/buu/bii.py")
- cwd = os.getcwd()
- os.chdir(directory.root())
- expected = {
- "foo",
- "foo/bar",
- "foo/bee",
- }
- try:
- actual = namespace_pkgs.implicit_namespace_packages(".")
- self.assertPathsEqual(actual, expected)
- finally:
- os.chdir(cwd)
- directory.remove()
-
- def test_finds_correct_namespace_packages(self) -> None:
- directory = TempDir()
- directory.add_file("foo/bar/biz.py")
- directory.add_file("foo/bee/boo.py")
- directory.add_file("foo/buu/__init__.py")
- directory.add_file("foo/buu/bii.py")
-
- expected = {
- directory.root() + "/foo",
- directory.root() + "/foo/bar",
- directory.root() + "/foo/bee",
- }
- actual = namespace_pkgs.implicit_namespace_packages(directory.root())
- self.assertPathsEqual(actual, expected)
-
- def test_ignores_empty_directories(self) -> None:
- directory = TempDir()
- directory.add_file("foo/bar/biz.py")
- directory.add_dir("foo/cat")
-
- expected = {
- directory.root() + "/foo",
- directory.root() + "/foo/bar",
- }
- actual = namespace_pkgs.implicit_namespace_packages(directory.root())
- self.assertPathsEqual(actual, expected)
-
- def test_empty_case(self) -> None:
- directory = TempDir()
- directory.add_file("foo/__init__.py")
- directory.add_file("foo/bar/__init__.py")
- directory.add_file("foo/bar/biz.py")
-
- actual = namespace_pkgs.implicit_namespace_packages(directory.root())
- self.assertEqual(actual, set())
-
- def test_ignores_non_module_files_in_directories(self) -> None:
- directory = TempDir()
- directory.add_file("foo/__init__.pyi")
- directory.add_file("foo/py.typed")
-
- actual = namespace_pkgs.implicit_namespace_packages(directory.root())
- self.assertEqual(actual, set())
-
- def test_parent_child_relationship_of_namespace_pkgs(self):
- directory = TempDir()
- directory.add_file("foo/bar/biff/my_module.py")
- directory.add_file("foo/bar/biff/another_module.py")
-
- expected = {
- directory.root() + "/foo",
- directory.root() + "/foo/bar",
- directory.root() + "/foo/bar/biff",
- }
- actual = namespace_pkgs.implicit_namespace_packages(directory.root())
- self.assertPathsEqual(actual, expected)
-
- def test_parent_child_relationship_of_namespace_and_standard_pkgs(self):
- directory = TempDir()
- directory.add_file("foo/bar/biff/__init__.py")
- directory.add_file("foo/bar/biff/another_module.py")
-
- expected = {
- directory.root() + "/foo",
- directory.root() + "/foo/bar",
- }
- actual = namespace_pkgs.implicit_namespace_packages(directory.root())
- self.assertPathsEqual(actual, expected)
-
- def test_parent_child_relationship_of_namespace_and_nested_standard_pkgs(self):
- directory = TempDir()
- directory.add_file("foo/bar/__init__.py")
- directory.add_file("foo/bar/biff/another_module.py")
- directory.add_file("foo/bar/biff/__init__.py")
- directory.add_file("foo/bar/boof/big_module.py")
- directory.add_file("foo/bar/boof/__init__.py")
- directory.add_file("fim/in_a_ns_pkg.py")
-
- expected = {
- directory.root() + "/foo",
- directory.root() + "/fim",
- }
- actual = namespace_pkgs.implicit_namespace_packages(directory.root())
- self.assertPathsEqual(actual, expected)
-
- def test_recognized_all_nonstandard_module_types(self):
- directory = TempDir()
- directory.add_file("ayy/my_module.pyc")
- directory.add_file("bee/ccc/dee/eee.so")
- directory.add_file("eff/jee/aych.pyd")
-
- expected = {
- directory.root() + "/ayy",
- directory.root() + "/bee",
- directory.root() + "/bee/ccc",
- directory.root() + "/bee/ccc/dee",
- directory.root() + "/eff",
- directory.root() + "/eff/jee",
- }
- actual = namespace_pkgs.implicit_namespace_packages(directory.root())
- self.assertPathsEqual(actual, expected)
-
- def test_skips_ignored_directories(self):
- directory = TempDir()
- directory.add_file("foo/boo/my_module.py")
- directory.add_file("foo/bar/another_module.py")
-
- expected = {
- directory.root() + "/foo",
- directory.root() + "/foo/bar",
- }
- actual = namespace_pkgs.implicit_namespace_packages(
- directory.root(),
- ignored_dirnames=[directory.root() + "/foo/boo"],
- )
- self.assertPathsEqual(actual, expected)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/tests/pypi/whl_installer/wheel_installer_test.py b/tests/pypi/whl_installer/wheel_installer_test.py
index ef5a248..7040b0c 100644
--- a/tests/pypi/whl_installer/wheel_installer_test.py
+++ b/tests/pypi/whl_installer/wheel_installer_test.py
@@ -70,7 +70,6 @@
Path(self.wheel_path),
installation_dir=Path(self.wheel_dir),
extras={},
- enable_implicit_namespace_pkgs=False,
platforms=[],
enable_pipstar=False,
)