Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b4e6496
basic impl
rickeylev Apr 23, 2026
c676d6e
fix tests
rickeylev Apr 23, 2026
10ace3b
dont pass select to repo-generated init creation
rickeylev Apr 25, 2026
0fd7c6d
fix testing on workspace
rickeylev Apr 25, 2026
7d2893f
Merge branch 'main' of https://github.com/bazel-contrib/rules_python …
rickeylev Apr 25, 2026
16c5100
format, lint fix
rickeylev Apr 25, 2026
9e53c53
fix pip example
rickeylev Apr 25, 2026
76cc0e0
format
rickeylev Apr 25, 2026
9470aac
test: fix windows paths for whl_with_data test
rickeylev Apr 25, 2026
5b57de2
fix: prevent grouping of top-level bin, include, and data in venvs
rickeylev Apr 25, 2026
103ec9a
fix: add alias for is_venvs_site_packages and add whl_with_data to wo…
rickeylev Apr 25, 2026
f41bfda
fix: add WORKSPACE file and export all files in whl_with_data repo
rickeylev Apr 25, 2026
fe1ad5f
revert: rename of is_venvs_site_packages and whl_with_data workspace …
rickeylev Apr 25, 2026
b35951a
fix: add bazel_skylib to pip_parse_vendored example
rickeylev Apr 25, 2026
52dfac5
fix: add bazel_skylib to multi_python_versions and pip_parse examples
rickeylev Apr 25, 2026
45611a5
docs: add DATA field to VenvSymlinkKind docstring
rickeylev Apr 25, 2026
fd6cda6
fix: undefined data_arg in whl_library_targets.bzl
rickeylev Apr 25, 2026
958e61e
fix: robustness in venvs_site_packages_libs_test for Windows paths
rickeylev Apr 25, 2026
194693c
fix: make bin/s3cmd expectation conditional in pip_parse_test.py
rickeylev Apr 25, 2026
82b9acc
refactor: rename is_venvs_site_packages to _is_venvs_site_packages
rickeylev Apr 25, 2026
dd2b0b3
pass create_inits() non-selet value, add select value afterwards
rickeylev Apr 25, 2026
3ed727e
change test to enforce windows uses Scripts/Include in venv
rickeylev Apr 25, 2026
7561c72
make pip_parse_test.py work with venv_site_packages
rickeylev Apr 25, 2026
24a94a2
add second wheel with data to test merging/overlap. rename first
rickeylev Apr 25, 2026
1fabeea
format
rickeylev Apr 25, 2026
473966b
revert passing bzlmod/venv_site_packages to pip_parse_test. not neede…
rickeylev Apr 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ use_repo(
"somepkg_with_build_files",
"whl_library_extras_direct_dep",
"whl_with_build_files",
"whl_with_data1",
"whl_with_data2",
)

dev_rules_python_config = use_extension(
Expand Down
20 changes: 10 additions & 10 deletions examples/pip_parse/pip_parse_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,16 @@ def test_data(self):
self.assertIsNotNone(actual)
actual = self._remove_leading_dirs(actual.split(" "))

self.assertListEqual(
actual,
[
"data/share/doc/packages/s3cmd/INSTALL.md",
"data/share/doc/packages/s3cmd/LICENSE",
"data/share/doc/packages/s3cmd/NEWS",
"data/share/doc/packages/s3cmd/README.md",
"data/share/man/man1/s3cmd.1",
],
)
expected = [
"bin/s3cmd",
"data/share/doc/packages/s3cmd/INSTALL.md",
"data/share/doc/packages/s3cmd/LICENSE",
"data/share/doc/packages/s3cmd/NEWS",
"data/share/doc/packages/s3cmd/README.md",
"data/share/man/man1/s3cmd.1",
]

self.assertListEqual(actual, expected)

def test_dist_info(self):
actual = os.environ.get("WHEEL_DIST_INFO_CONTENTS")
Expand Down
2 changes: 1 addition & 1 deletion python/config_settings/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ string_flag(
)

config_setting(
name = "is_venvs_site_packages",
name = "_is_venvs_site_packages",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is different from the PR description and the constant elsevhere.

flag_values = {
":venvs_site_packages": VenvsSitePackages.YES,
},
Expand Down
22 changes: 22 additions & 0 deletions python/private/internal_dev_deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,28 @@ def _internal_dev_deps_impl(mctx):
enable_implicit_namespace_pkgs = False,
)

whl_from_dir_repo(
name = "whl_with_data1_whl",
root = "//tests/repos/whl_with_data1:BUILD.bazel",
output = "whl_with_data1-1.0-any-none-any.whl",
)
whl_library(
name = "whl_with_data1",
whl_file = "@whl_with_data1_whl//:whl_with_data1-1.0-any-none-any.whl",
requirement = "whl-with-data1",
)

whl_from_dir_repo(
name = "whl_with_data2_whl",
root = "//tests/repos/whl_with_data2:BUILD.bazel",
output = "whl_with_data2-1.0-any-none-any.whl",
)
whl_library(
name = "whl_with_data2",
whl_file = "@whl_with_data2_whl//:whl_with_data2-1.0-any-none-any.whl",
requirement = "whl-with-data2",
)

_whl_library_from_dir(
name = "whl_library_extras_direct_dep",
root = "//tests/pypi/whl_library/testdata/pkg:BUILD.bazel",
Expand Down
23 changes: 15 additions & 8 deletions python/private/py_executable.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -576,8 +576,10 @@ def _create_venv(ctx, output_prefix, imports, runtime_details, add_runfiles_root
)

venv_dir_map = {
VenvSymlinkKind.BIN: venv_details.bin_dir,
VenvSymlinkKind.BIN: "{}/{}".format(venv_ctx_rel_root, venv_details.bin_dir),
VenvSymlinkKind.LIB: site_packages,
VenvSymlinkKind.INCLUDE: "{}/{}".format(venv_ctx_rel_root, venv_details.include_dir),
VenvSymlinkKind.DATA: "{}/data".format(venv_ctx_rel_root),
}
venv_app_files = create_venv_app_files(
ctx,
Expand Down Expand Up @@ -659,7 +661,7 @@ def _create_venv_unixy(ctx, *, venv_ctx_rel_root, runtime, interpreter_actual_pa

recreate_venv_at_runtime = False

bin_dir = "{}/bin".format(venv_ctx_rel_root)
venv_bin_ctx_rel_path = "{}/bin".format(venv_ctx_rel_root)
if create_full_venv:
# Some wrappers around the interpreter (e.g. pyenv) use the program
# name to decide what to do, so preserve the name.
Expand All @@ -671,15 +673,15 @@ def _create_venv_unixy(ctx, *, venv_ctx_rel_root, runtime, interpreter_actual_pa
# When the venv symlinks are disabled, the $venv/bin/python3 file isn't
# needed or used at runtime. However, the zip code uses the interpreter
# File object to figure out some paths.
interpreter = ctx.actions.declare_file("{}/{}".format(bin_dir, py_exe_basename))
interpreter = ctx.actions.declare_file("{}/{}".format(venv_bin_ctx_rel_path, py_exe_basename))
ctx.actions.write(interpreter, "actual:{}".format(interpreter_actual_path))

elif runtime.interpreter:
# Even though ctx.actions.symlink() is used, using
# declare_symlink() is required to ensure that the resulting file
# in runfiles is always a symlink. An RBE implementation, for example,
# may choose to write what symlink() points to instead.
interpreter = ctx.actions.declare_symlink("{}/{}".format(bin_dir, py_exe_basename))
interpreter = ctx.actions.declare_symlink("{}/{}".format(venv_bin_ctx_rel_path, py_exe_basename))
interpreter_runfiles.add(interpreter)

rel_path = relative_path(
Expand All @@ -690,7 +692,7 @@ def _create_venv_unixy(ctx, *, venv_ctx_rel_root, runtime, interpreter_actual_pa
)
ctx.actions.symlink(output = interpreter, target_path = rel_path)
else:
interpreter = ctx.actions.declare_symlink("{}/{}".format(bin_dir, py_exe_basename))
interpreter = ctx.actions.declare_symlink("{}/{}".format(venv_bin_ctx_rel_path, py_exe_basename))
interpreter_runfiles.add(interpreter)
ctx.actions.symlink(output = interpreter, target_path = runtime.interpreter_path)
else:
Expand All @@ -715,7 +717,8 @@ def _create_venv_unixy(ctx, *, venv_ctx_rel_root, runtime, interpreter_actual_pa
interpreter = interpreter,
pyvenv_cfg = pyvenv_cfg,
site_packages = site_packages,
bin_dir = bin_dir,
bin_dir = "bin",
include_dir = "include",
recreate_venv_at_runtime = recreate_venv_at_runtime,
interpreter_runfiles = interpreter_runfiles.build(ctx),
interpreter_symlinks = depset(),
Expand Down Expand Up @@ -777,7 +780,8 @@ def _create_venv_windows(ctx, *, venv_ctx_rel_root, runtime, interpreter_actual_
interpreter = interpreter,
pyvenv_cfg = None,
site_packages = site_packages,
bin_dir = venv_bin_ctx_rel_path,
bin_dir = venv_bin_rel_path,
include_dir = "Include",
recreate_venv_at_runtime = True,
interpreter_runfiles = interpreter_runfiles.build(ctx),
interpreter_symlinks = interpreter_symlinks.build(),
Expand All @@ -789,6 +793,7 @@ def _venv_details(
pyvenv_cfg,
site_packages,
bin_dir,
include_dir,
recreate_venv_at_runtime,
interpreter_runfiles,
interpreter_symlinks):
Expand All @@ -801,8 +806,10 @@ def _venv_details(
pyvenv_cfg = pyvenv_cfg,
# str; venv-relative path to the site-packages directory
site_packages = site_packages,
# str; ctx-relative path to the venv's bin directory.
# str; venv-relative path to the venv's bin directory.
bin_dir = bin_dir,
# str; venv-relative-path to the venv's include directory.
include_dir = include_dir,
# bool; True if the venv needs to be recreated at runtime (because the
# build-time construction isn't sufficient). False if the build-time
# constructed venv is sufficient.
Expand Down
7 changes: 7 additions & 0 deletions python/private/py_info.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ def _VenvSymlinkKind_typedef():
Indicates to create paths under the venv's include directory.
:::
:::{field} DATA
:type: object
Indicates to create paths under the venv's data directory.
:::
"""

# buildifier: disable=name-conventions
Expand All @@ -45,6 +51,7 @@ VenvSymlinkKind = struct(
BIN = "BIN",
LIB = "LIB",
INCLUDE = "INCLUDE",
DATA = "DATA",
)

def _VenvSymlinkEntry_init(**kwargs):
Expand Down
15 changes: 13 additions & 2 deletions python/private/pypi/whl_library_targets.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ _BAZEL_REPO_FILE_GLOBS = [
"WORKSPACE.bazel",
]

_IS_VENV_SITE_PACKAGES_YES = Label("//python/config_settings:_is_venvs_site_packages")

def whl_library_targets_from_requires(
*,
name,
Expand Down Expand Up @@ -194,8 +196,10 @@ def whl_library_targets(
DIST_INFO_LABEL: dict(
include = ["site-packages/*.dist-info/**"],
),

## TO CHECK: should bin/ and include/ be part of the data target?
Comment thread
rickeylev marked this conversation as resolved.
DATA_LABEL: dict(
include = ["data/**"],
include = ["data/**", "bin/**", "include/**"],
),
}

Expand Down Expand Up @@ -369,7 +373,7 @@ def whl_library_targets(

if not enable_implicit_namespace_pkgs:
generated_namespace_package_files = select({
Label("//python/config_settings:is_venvs_site_packages"): [],
_IS_VENV_SITE_PACKAGES_YES: [],
"//conditions:default": rules.create_inits(
srcs = srcs + data + pyi_srcs,
ignored_dirnames = [], # If you need to ignore certain folders, you can patch rules_python here to do so.
Expand All @@ -379,6 +383,13 @@ def whl_library_targets(
namespace_package_files += generated_namespace_package_files
srcs = srcs + generated_namespace_package_files

# This must be doe after the above because create_inits() is macro-phase,
# so can't handle select() values.
data = data + select({
_IS_VENV_SITE_PACKAGES_YES: [DATA_LABEL],
"//conditions:default": [],
})

rules.py_library(
name = py_library_label,
srcs = srcs,
Expand Down
36 changes: 32 additions & 4 deletions python/private/venv_runfiles.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,16 @@ def _get_file_venv_path(ctx, f, site_packages_root):

Returns:
A tuple `(venv_path, rf_root_path)` if the file is under
`site_packages_root`, otherwise `(None, None)`.
`site_packages_root` or data/, bin/, include/ otherwise `(None, None)`.
"""
rf_root_path = runfiles_root_path(ctx, f.short_path)
_, _, repo_rel_path = rf_root_path.partition("/")

# Check for wheel data/bin/include folders first
for prefix in ["data/", "bin/", "include/"]:
if repo_rel_path.startswith(prefix):
return (repo_rel_path, rf_root_path)

head, found_sp_root, venv_path = repo_rel_path.partition(site_packages_root)
if head or not found_sp_root:
# If head is set, then the path didn't start with site_packages_root
Expand Down Expand Up @@ -412,6 +418,11 @@ def get_venv_symlinks(
if not cannot_be_linked_directly.get(dirname, False):
cannot_be_linked_directly[dirname] = True

# bin, include, and data are also shared across wheels, so we cannot link
# them directly if they are at the top level.
for dirname in ["bin", "include", "data"]:
cannot_be_linked_directly[dirname] = True

# At this point, venv_symlinks has entries for the shared libraries
# and cannot_be_linked_directly has the directories that cannot be
# directly linked. Next, we loop over the remaining files and group
Expand Down Expand Up @@ -452,19 +463,36 @@ def get_venv_symlinks(

# Finally, for each group, we create the VenvSymlinkEntry objects
for venv_path, files in optimized_groups.items():
if venv_path.startswith("data/"):
out_venv_path = venv_path[len("data/"):]
kind = VenvSymlinkKind.DATA
prefix = ""
elif venv_path.startswith("include/"):
out_venv_path = venv_path[len("include/"):]
kind = VenvSymlinkKind.INCLUDE
prefix = ""
elif venv_path.startswith("bin/"):
out_venv_path = venv_path[len("bin/"):]
kind = VenvSymlinkKind.BIN
prefix = ""
Comment thread
rickeylev marked this conversation as resolved.
else:
out_venv_path = venv_path
kind = VenvSymlinkKind.LIB
prefix = site_packages_root

link_to_path = (
_get_label_runfiles_repo(ctx, files[0].owner) +
"/" +
site_packages_root +
prefix +
venv_path
)
venv_symlinks[venv_path] = VenvSymlinkEntry(
kind = VenvSymlinkKind.LIB,
kind = kind,
link_to_path = link_to_path,
link_to_file = None,
package = package,
version = version_str,
venv_path = venv_path,
venv_path = out_venv_path,
files = depset(files),
)

Expand Down
20 changes: 10 additions & 10 deletions tests/pypi/whl_library_targets/whl_library_targets_tests.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def _test_filegroups(env):
},
{
"name": "data",
"srcs": ["data/**"],
"srcs": ["data/**", "bin/**", "include/**"],
"visibility": ["//visibility:public"],
},
{
Expand Down Expand Up @@ -245,11 +245,11 @@ def _test_whl_and_library_deps_from_requires(env):
env.expect.that_dict(py_library_call).contains_exactly({
"name": "pkg",
"srcs": ["site-packages/foo/SRCS.py"] + select({
Label("//python/config_settings:is_venvs_site_packages"): [],
Label("//python/config_settings:_is_venvs_site_packages"): [],
"//conditions:default": ["_create_inits_target"],
}),
"pyi_srcs": ["site-packages/foo/PYI.pyi"],
"data": ["site-packages/foo/DATA.txt"],
"data": ["site-packages/foo/DATA.txt"] + select({Label("//python/config_settings:_is_venvs_site_packages"): ["data"], "//conditions:default": []}),
"imports": ["site-packages"],
"deps": ["@pypi//bar:pkg"] + select({
":is_include_bar_baz_true": ["@pypi//bar_baz:pkg"],
Expand All @@ -259,7 +259,7 @@ def _test_whl_and_library_deps_from_requires(env):
"visibility": ["//visibility:public"],
"experimental_venvs_site_packages": Label("//python/config_settings:venvs_site_packages"),
"namespace_package_files": [] + select({
Label("//python/config_settings:is_venvs_site_packages"): [],
Label("//python/config_settings:_is_venvs_site_packages"): [],
"//conditions:default": ["_create_inits_target"],
}),
}) # buildifier: @unsorted-dict-items
Expand Down Expand Up @@ -361,11 +361,11 @@ def _test_whl_and_library_deps(env):
env.expect.that_dict(py_library_calls[0]).contains_exactly({
"name": "pkg",
"srcs": ["site-packages/foo/SRCS.py"] + select({
Label("//python/config_settings:is_venvs_site_packages"): [],
Label("//python/config_settings:_is_venvs_site_packages"): [],
"//conditions:default": ["_create_inits_target"],
}),
"pyi_srcs": ["site-packages/foo/PYI.pyi"],
"data": ["site-packages/foo/DATA.txt"],
"data": ["site-packages/foo/DATA.txt"] + select({Label("//python/config_settings:_is_venvs_site_packages"): ["data"], "//conditions:default": []}),
"imports": ["site-packages"],
"deps": [
"@pypi_bar_baz//:pkg",
Expand All @@ -386,7 +386,7 @@ def _test_whl_and_library_deps(env):
"visibility": ["//visibility:public"],
"experimental_venvs_site_packages": Label("//python/config_settings:venvs_site_packages"),
"namespace_package_files": [] + select({
Label("//python/config_settings:is_venvs_site_packages"): [],
Label("//python/config_settings:_is_venvs_site_packages"): [],
"//conditions:default": ["_create_inits_target"],
}),
}) # buildifier: @unsorted-dict-items
Expand Down Expand Up @@ -444,11 +444,11 @@ def _test_group(env):
).contains_exactly({
"name": "_pkg",
"srcs": ["site-packages/foo/srcs.py"] + select({
Label("//python/config_settings:is_venvs_site_packages"): [],
Label("//python/config_settings:_is_venvs_site_packages"): [],
"//conditions:default": ["_create_inits_target"],
}),
"pyi_srcs": ["site-packages/foo/pyi.pyi"],
"data": ["site-packages/foo/data.txt"],
"data": ["site-packages/foo/data.txt"] + select({Label("//python/config_settings:_is_venvs_site_packages"): ["data"], "//conditions:default": []}),
"imports": ["site-packages"],
"deps": ["@pypi_bar_baz//:pkg"] + select({
"@platforms//os:linux": ["@pypi_box//:pkg"],
Expand All @@ -459,7 +459,7 @@ def _test_group(env):
"visibility": ["@pypi__config//_groups:__pkg__"],
"experimental_venvs_site_packages": Label("//python/config_settings:venvs_site_packages"),
"namespace_package_files": [] + select({
Label("//python/config_settings:is_venvs_site_packages"): [],
Label("//python/config_settings:_is_venvs_site_packages"): [],
"//conditions:default": ["_create_inits_target"],
}),
}) # buildifier: @unsorted-dict-items
Expand Down
1 change: 1 addition & 0 deletions tests/repos/whl_with_data1/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exports_files(glob(["*"]))
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .data/data
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .data/headers
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .data/platlib
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .data
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#!/bin/sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Metadata-Version: 2.1
Name: whl-with-data1
Version: 1.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
whl_with_data1-1.0.data/platlib/whl_with_data1/platlib_file.txt,sha256=123,123
whl_with_data1-1.0.data/scripts/whl_script.sh,sha256=123,123
whl_with_data1-1.0.data/headers/whl_with_data1/header_file.h,sha256=123,123
whl_with_data1-1.0.data/purelib/whl_with_data1/data_file.txt,sha256=123,123
whl_with_data1-1.0.data/data/whl_with_data1/data_data_file.txt,sha256=123,123
whl_with_data1-1.0.data/data/whl_with_data1/data_data_file.txt,sha256=123,123
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Wheel-Version: 1.0
Loading
Loading