From 1c894ea23ccf2e67c9685fedb47652dc0db84c1e Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Fri, 1 May 2026 15:30:39 +0000 Subject: [PATCH 1/4] fix(pypi): include RECORD file in installed wheel targets Currently, `importlib.metadata.files()` returns `None` for packages installed via `rules_python`'s wheel rules. This happens because the `RECORD` file in the `.dist-info` directory is explicitly excluded from the `data` attribute of the generated `py_library` targets. To fix this, remove `**/*.dist-info/RECORD` from the `_data_exclude` list in `whl_library_targets.bzl`. This ensures that the `RECORD` file is preserved in the runfiles and available at runtime, enabling `importlib.metadata.files()` to correctly list the files in the package. --- python/private/pypi/whl_library_targets.bzl | 4 ---- tests/venv_site_packages_libs/BUILD.bazel | 14 +++++++++++ .../importlib_metadata_test.py | 24 +++++++++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 tests/venv_site_packages_libs/importlib_metadata_test.py diff --git a/python/private/pypi/whl_library_targets.bzl b/python/private/pypi/whl_library_targets.bzl index b3a52cd18c..7cab016890 100644 --- a/python/private/pypi/whl_library_targets.bzl +++ b/python/private/pypi/whl_library_targets.bzl @@ -374,10 +374,6 @@ def whl_library_targets( "**/*.py", "**/*.pyc", "**/*.pyc.*", # During pyc creation, temp files named *.pyc.NNNN are created - # RECORD is known to contain sha256 checksums of files which might include the checksums - # of generated files produced when wheels are installed. The file is ignored to avoid - # Bazel caching issues. - "**/*.dist-info/RECORD", ] for item in data_exclude: if item not in _data_exclude: diff --git a/tests/venv_site_packages_libs/BUILD.bazel b/tests/venv_site_packages_libs/BUILD.bazel index d44bbcbb63..6a7b3b9e12 100644 --- a/tests/venv_site_packages_libs/BUILD.bazel +++ b/tests/venv_site_packages_libs/BUILD.bazel @@ -85,3 +85,17 @@ py_reconfig_test( "@whl_with_data2//:pkg", ], ) + +py_reconfig_test( + name = "importlib_metadata_test", + srcs = ["importlib_metadata_test.py"], + bootstrap_impl = select({ + "@platforms//os:windows": "system_python", + "//conditions:default": "script", + }), + main = "importlib_metadata_test.py", + venvs_site_packages = "yes", + deps = [ + "@whl_with_data1//:pkg", + ], +) diff --git a/tests/venv_site_packages_libs/importlib_metadata_test.py b/tests/venv_site_packages_libs/importlib_metadata_test.py new file mode 100644 index 0000000000..178ff14c50 --- /dev/null +++ b/tests/venv_site_packages_libs/importlib_metadata_test.py @@ -0,0 +1,24 @@ +import importlib.metadata +import unittest + + +class ImportlibMetadataTest(unittest.TestCase): + + def test_importlib_metadata_files(self): + files = importlib.metadata.files("whl-with-data1") + self.assertIsNotNone(files, "importlib.metadata.files returned None") + self.assertGreater( + len(files), 0, "importlib.metadata.files returned empty list" + ) + + # Verify it contains some expected files. + # The RECORD file lists paths relative to the installation root (site-packages). + # whl_with_data1-1.0.data/purelib/data_overlap.py should be installed as data_overlap.py + # whl_with_data1-1.0.data/platlib/whl_with_data1/platlib_file.txt should be whl_with_data1/platlib_file.txt + + file_names = [f.name for f in files] + self.assertIn("data_overlap.py", file_names) + + +if __name__ == "__main__": + unittest.main() From 641bfce370dcbf1cfc3337b79abc35f5b4977b81 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Sun, 3 May 2026 21:41:16 +0000 Subject: [PATCH 2/4] test: update expectations to include RECORD --- tests/pypi/whl_library_targets/whl_library_targets_tests.bzl | 2 -- .../app_files_building/app_files_building_tests.bzl | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/pypi/whl_library_targets/whl_library_targets_tests.bzl b/tests/pypi/whl_library_targets/whl_library_targets_tests.bzl index ec28bfbb39..117c2954a5 100644 --- a/tests/pypi/whl_library_targets/whl_library_targets_tests.bzl +++ b/tests/pypi/whl_library_targets/whl_library_targets_tests.bzl @@ -272,7 +272,6 @@ def _test_whl_and_library_deps_from_requires(env): "**/*.py", "**/*.pyc", "**/*.pyc.*", - "**/*.dist-info/RECORD", ], allow_empty = True, ), @@ -471,7 +470,6 @@ def _test_group(env): "**/*.py", "**/*.pyc", "**/*.pyc.*", - "**/*.dist-info/RECORD", ], allow_empty = True), mocks.glob_call(["site-packages/**/*.pyi"], allow_empty = True), ]) diff --git a/tests/venv_site_packages_libs/app_files_building/app_files_building_tests.bzl b/tests/venv_site_packages_libs/app_files_building/app_files_building_tests.bzl index 66ba7076a9..65124076c6 100644 --- a/tests/venv_site_packages_libs/app_files_building/app_files_building_tests.bzl +++ b/tests/venv_site_packages_libs/app_files_building/app_files_building_tests.bzl @@ -471,6 +471,7 @@ def _test_optimized_grouping_pkgutil_whls_impl(env, target): files = [ "../+internal_dev_deps+pkgutil_nspkg1/site-packages/pkgutil_nspkg1-1.0.dist-info/INSTALLER", "../+internal_dev_deps+pkgutil_nspkg1/site-packages/pkgutil_nspkg1-1.0.dist-info/METADATA", + "../+internal_dev_deps+pkgutil_nspkg1/site-packages/pkgutil_nspkg1-1.0.dist-info/RECORD", "../+internal_dev_deps+pkgutil_nspkg1/site-packages/pkgutil_nspkg1-1.0.dist-info/WHEEL", ], ), @@ -495,6 +496,7 @@ def _test_optimized_grouping_pkgutil_whls_impl(env, target): files = [ "../+internal_dev_deps+pkgutil_nspkg2/site-packages/pkgutil_nspkg2-1.0.dist-info/INSTALLER", "../+internal_dev_deps+pkgutil_nspkg2/site-packages/pkgutil_nspkg2-1.0.dist-info/METADATA", + "../+internal_dev_deps+pkgutil_nspkg2/site-packages/pkgutil_nspkg2-1.0.dist-info/RECORD", "../+internal_dev_deps+pkgutil_nspkg2/site-packages/pkgutil_nspkg2-1.0.dist-info/WHEEL", ], ), From b16f05316cb4110723293f59fec31e928492bfde Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Sun, 3 May 2026 21:51:21 +0000 Subject: [PATCH 3/4] fix(pypi): conditionally exclude RECORD only if sdist_filename is set Only exclude `RECORD` from the `data` attribute of `py_library` targets if the wheel was built from an sdist (`sdist_filename` is set). This ensures `RECORD` is available for standard wheels, enabling `importlib.metadata.files` to work. * Adds `test_sdist_excludes_record` to verify the conditional behavior. --- python/private/pypi/whl_library_targets.bzl | 2 + .../whl_library_targets_tests.bzl | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/python/private/pypi/whl_library_targets.bzl b/python/private/pypi/whl_library_targets.bzl index 7cab016890..01a89aadcc 100644 --- a/python/private/pypi/whl_library_targets.bzl +++ b/python/private/pypi/whl_library_targets.bzl @@ -375,6 +375,8 @@ def whl_library_targets( "**/*.pyc", "**/*.pyc.*", # During pyc creation, temp files named *.pyc.NNNN are created ] + if sdist_filename: + _data_exclude.append("**/*.dist-info/RECORD") for item in data_exclude: if item not in _data_exclude: _data_exclude.append(item) diff --git a/tests/pypi/whl_library_targets/whl_library_targets_tests.bzl b/tests/pypi/whl_library_targets/whl_library_targets_tests.bzl index 117c2954a5..91db15f296 100644 --- a/tests/pypi/whl_library_targets/whl_library_targets_tests.bzl +++ b/tests/pypi/whl_library_targets/whl_library_targets_tests.bzl @@ -476,6 +476,47 @@ def _test_group(env): _tests.append(_test_group) +def _test_sdist_excludes_record(env): + py_library_calls = [] + m_glob = mocks.glob() + m_glob.results.append([]) # bin + m_glob.results.append([]) # rewrite-bin + m_glob.results.append([]) # srcs + m_glob.results.append([]) # data + m_glob.results.append([]) # pyi + + whl_library_targets( + name = "foo.whl", + dep_template = "@pypi_{name}//:{target}", + sdist_filename = "foo.tar.gz", + filegroups = {}, + native = struct( + filegroup = lambda **_: None, + config_setting = lambda **_: None, + glob = m_glob.glob, + ), + rules = struct( + py_library = lambda **kwargs: py_library_calls.append(kwargs), + create_inits = lambda **kwargs: [], + venv_rewrite_shebang = lambda **kwargs: None, + ), + ) + + env.expect.that_collection(m_glob.calls).contains_at_least([ + mocks.glob_call( + ["site-packages/**/*"], + exclude = [ + "**/*.py", + "**/*.pyc", + "**/*.pyc.*", + "**/*.dist-info/RECORD", + ], + allow_empty = True, + ), + ]) + +_tests.append(_test_sdist_excludes_record) + def whl_library_targets_test_suite(name): """create the test suite. From d88140a7d78e30ee0507135a3dae38aa0546bb3f Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Sun, 3 May 2026 22:52:26 +0000 Subject: [PATCH 4/4] docs: update CHANGELOG.md for importlib fix --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc45d2e95e..70f30ac67b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,10 @@ END_UNRELEASED_TEMPLATE * (uv) use the astral.sh mirror as the preferred url for binary downloads, with github.com as a fallback; for uv >= 0.11.0, read the checksums directly from the dist-manifest contents. +* (pypi) Fix `importlib.metadata.files` by ensuring `RECORD` is included in + installed wheel targets, except when built from sdist + ([#3024](https://github.com/bazel-contrib/rules_python/issues/3024)). + {#v0-0-0-added} ### Added