From a2842399da15c4f52c3dc37f491955420397a995 Mon Sep 17 00:00:00 2001 From: chua Date: Thu, 18 Sep 2025 08:13:05 +0000 Subject: [PATCH 1/5] one-shot attempt --- .../pypi/generate_whl_library_build_bazel.bzl | 2 + python/private/pypi/package_annotation.bzl | 6 +- ...generate_whl_library_build_bazel_tests.bzl | 74 +++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/python/private/pypi/generate_whl_library_build_bazel.bzl b/python/private/pypi/generate_whl_library_build_bazel.bzl index 3764e720c0..050c86d740 100644 --- a/python/private/pypi/generate_whl_library_build_bazel.bzl +++ b/python/private/pypi/generate_whl_library_build_bazel.bzl @@ -109,6 +109,8 @@ def generate_whl_library_build_bazel( kwargs["copy_executables"] = annotation.copy_executables kwargs["data_exclude"] = kwargs.get("data_exclude", []) + annotation.data_exclude_glob kwargs["srcs_exclude"] = annotation.srcs_exclude_glob + if annotation.enable_implicit_namespace_pkgs != None: + kwargs["enable_implicit_namespace_pkgs"] = annotation.enable_implicit_namespace_pkgs if annotation.additive_build_content: additional_content.append(annotation.additive_build_content) if default_python_version: diff --git a/python/private/pypi/package_annotation.bzl b/python/private/pypi/package_annotation.bzl index 4a54703ac4..05e724399a 100644 --- a/python/private/pypi/package_annotation.bzl +++ b/python/private/pypi/package_annotation.bzl @@ -20,7 +20,8 @@ def package_annotation( copy_executables = {}, data = [], data_exclude_glob = [], - srcs_exclude_glob = []): + srcs_exclude_glob = [], + enable_implicit_namespace_pkgs = None): """Annotations to apply to the BUILD file content from package generated from a `pip_repository` rule. [cf]: https://github.com/bazelbuild/bazel-skylib/blob/main/docs/copy_file_doc.md @@ -35,6 +36,8 @@ def package_annotation( data_exclude_glob (list, optional): A list of exclude glob patterns to add as `data` to the generated `py_library` target. srcs_exclude_glob (list, optional): A list of labels to add as `srcs` to the generated `py_library` target. + enable_implicit_namespace_pkgs (bool, optional): Override the global setting for generating __init__.py + files for namespace packages. If None, uses the repository-level setting. Returns: str: A json encoded string of the provided content. @@ -46,4 +49,5 @@ def package_annotation( data = data, data_exclude_glob = data_exclude_glob, srcs_exclude_glob = srcs_exclude_glob, + enable_implicit_namespace_pkgs = enable_implicit_namespace_pkgs, )) diff --git a/tests/pypi/generate_whl_library_build_bazel/generate_whl_library_build_bazel_tests.bzl b/tests/pypi/generate_whl_library_build_bazel/generate_whl_library_build_bazel_tests.bzl index 225b296ebf..46e766f6b3 100644 --- a/tests/pypi/generate_whl_library_build_bazel/generate_whl_library_build_bazel_tests.bzl +++ b/tests/pypi/generate_whl_library_build_bazel/generate_whl_library_build_bazel_tests.bzl @@ -211,6 +211,80 @@ whl_library_targets_from_requires( _tests.append(_test_all_with_loads) +def _test_enable_implicit_namespace_pkgs_annotation(env): + want = """\ +load("@rules_python//python/private/pypi:whl_library_targets.bzl", "whl_library_targets") + +whl_library_targets( + dep_template = "@pypi//{name}:{target}", + enable_implicit_namespace_pkgs = True, + name = "foo.whl", + dependencies = ["foo"], + dependencies_by_platform = {"baz": ["bar"]}, + entry_points = { + "foo": "bar.py", + }, + group_deps = ["foo", "fox", "qux"], + group_name = "qux", + tags = ["tag1"], +) +""" + actual = generate_whl_library_build_bazel( + dep_template = "@pypi//{name}:{target}", + name = "foo.whl", + dependencies = ["foo"], + dependencies_by_platform = {"baz": ["bar"]}, + entry_points = { + "foo": "bar.py", + }, + annotation = struct( + enable_implicit_namespace_pkgs = True, + ), + group_name = "qux", + group_deps = ["foo", "fox", "qux"], + tags = ["tag1"], + ) + env.expect.that_str(actual.replace("@@", "@")).equals(want) + +_tests.append(_test_enable_implicit_namespace_pkgs_annotation) + +def _test_enable_implicit_namespace_pkgs_annotation_false(env): + want = """\ +load("@rules_python//python/private/pypi:whl_library_targets.bzl", "whl_library_targets") + +whl_library_targets( + dep_template = "@pypi//{name}:{target}", + enable_implicit_namespace_pkgs = False, + name = "foo.whl", + dependencies = ["foo"], + dependencies_by_platform = {"baz": ["bar"]}, + entry_points = { + "foo": "bar.py", + }, + group_deps = ["foo", "fox", "qux"], + group_name = "qux", + tags = ["tag1"], +) +""" + actual = generate_whl_library_build_bazel( + dep_template = "@pypi//{name}:{target}", + name = "foo.whl", + dependencies = ["foo"], + dependencies_by_platform = {"baz": ["bar"]}, + entry_points = { + "foo": "bar.py", + }, + annotation = struct( + enable_implicit_namespace_pkgs = False, + ), + group_name = "qux", + group_deps = ["foo", "fox", "qux"], + tags = ["tag1"], + ) + env.expect.that_str(actual.replace("@@", "@")).equals(want) + +_tests.append(_test_enable_implicit_namespace_pkgs_annotation_false) + def generate_whl_library_build_bazel_test_suite(name): """Create the test suite. From 508b6a1dd28a6d983238239850b58288d4ec841b Mon Sep 17 00:00:00 2001 From: chua Date: Thu, 18 Sep 2025 08:13:18 +0000 Subject: [PATCH 2/5] working tests --- ...generate_whl_library_build_bazel_tests.bzl | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/tests/pypi/generate_whl_library_build_bazel/generate_whl_library_build_bazel_tests.bzl b/tests/pypi/generate_whl_library_build_bazel/generate_whl_library_build_bazel_tests.bzl index 46e766f6b3..842db6e66a 100644 --- a/tests/pypi/generate_whl_library_build_bazel/generate_whl_library_build_bazel_tests.bzl +++ b/tests/pypi/generate_whl_library_build_bazel/generate_whl_library_build_bazel_tests.bzl @@ -215,17 +215,25 @@ def _test_enable_implicit_namespace_pkgs_annotation(env): want = """\ load("@rules_python//python/private/pypi:whl_library_targets.bzl", "whl_library_targets") +package(default_visibility = ["//visibility:public"]) + whl_library_targets( dep_template = "@pypi//{name}:{target}", - enable_implicit_namespace_pkgs = True, - name = "foo.whl", dependencies = ["foo"], - dependencies_by_platform = {"baz": ["bar"]}, + dependencies_by_platform = { + "baz": ["bar"], + }, + enable_implicit_namespace_pkgs = True, entry_points = { "foo": "bar.py", }, - group_deps = ["foo", "fox", "qux"], + group_deps = [ + "foo", + "fox", + "qux", + ], group_name = "qux", + name = "foo.whl", tags = ["tag1"], ) """ @@ -252,17 +260,25 @@ def _test_enable_implicit_namespace_pkgs_annotation_false(env): want = """\ load("@rules_python//python/private/pypi:whl_library_targets.bzl", "whl_library_targets") +package(default_visibility = ["//visibility:public"]) + whl_library_targets( dep_template = "@pypi//{name}:{target}", - enable_implicit_namespace_pkgs = False, - name = "foo.whl", dependencies = ["foo"], - dependencies_by_platform = {"baz": ["bar"]}, + dependencies_by_platform = { + "baz": ["bar"], + }, + enable_implicit_namespace_pkgs = False, entry_points = { "foo": "bar.py", }, - group_deps = ["foo", "fox", "qux"], + group_deps = [ + "foo", + "fox", + "qux", + ], group_name = "qux", + name = "foo.whl", tags = ["tag1"], ) """ From 59d75f435ddecbbdeeaf73ccbc60abbd7ecb1019 Mon Sep 17 00:00:00 2001 From: chua Date: Thu, 18 Sep 2025 08:22:11 +0000 Subject: [PATCH 3/5] simplify tes --- ...generate_whl_library_build_bazel_tests.bzl | 91 +------------------ 1 file changed, 2 insertions(+), 89 deletions(-) diff --git a/tests/pypi/generate_whl_library_build_bazel/generate_whl_library_build_bazel_tests.bzl b/tests/pypi/generate_whl_library_build_bazel/generate_whl_library_build_bazel_tests.bzl index 842db6e66a..6d00e6a415 100644 --- a/tests/pypi/generate_whl_library_build_bazel/generate_whl_library_build_bazel_tests.bzl +++ b/tests/pypi/generate_whl_library_build_bazel/generate_whl_library_build_bazel_tests.bzl @@ -103,6 +103,7 @@ whl_library_targets_from_requires( "data_exclude_all", ], dep_template = "@pypi//{name}:{target}", + enable_implicit_namespace_pkgs = True, entry_points = { "foo": "bar.py", }, @@ -139,6 +140,7 @@ whl_library_targets_from_requires( data_exclude_glob = ["data_exclude_all"], srcs_exclude_glob = ["srcs_exclude_all"], additive_build_content = """# SOMETHING SPECIAL AT THE END""", + enable_implicit_namespace_pkgs = True, ), group_name = "qux", group_deps = ["foo", "fox", "qux"], @@ -211,95 +213,6 @@ whl_library_targets_from_requires( _tests.append(_test_all_with_loads) -def _test_enable_implicit_namespace_pkgs_annotation(env): - want = """\ -load("@rules_python//python/private/pypi:whl_library_targets.bzl", "whl_library_targets") - -package(default_visibility = ["//visibility:public"]) - -whl_library_targets( - dep_template = "@pypi//{name}:{target}", - dependencies = ["foo"], - dependencies_by_platform = { - "baz": ["bar"], - }, - enable_implicit_namespace_pkgs = True, - entry_points = { - "foo": "bar.py", - }, - group_deps = [ - "foo", - "fox", - "qux", - ], - group_name = "qux", - name = "foo.whl", - tags = ["tag1"], -) -""" - actual = generate_whl_library_build_bazel( - dep_template = "@pypi//{name}:{target}", - name = "foo.whl", - dependencies = ["foo"], - dependencies_by_platform = {"baz": ["bar"]}, - entry_points = { - "foo": "bar.py", - }, - annotation = struct( - enable_implicit_namespace_pkgs = True, - ), - group_name = "qux", - group_deps = ["foo", "fox", "qux"], - tags = ["tag1"], - ) - env.expect.that_str(actual.replace("@@", "@")).equals(want) - -_tests.append(_test_enable_implicit_namespace_pkgs_annotation) - -def _test_enable_implicit_namespace_pkgs_annotation_false(env): - want = """\ -load("@rules_python//python/private/pypi:whl_library_targets.bzl", "whl_library_targets") - -package(default_visibility = ["//visibility:public"]) - -whl_library_targets( - dep_template = "@pypi//{name}:{target}", - dependencies = ["foo"], - dependencies_by_platform = { - "baz": ["bar"], - }, - enable_implicit_namespace_pkgs = False, - entry_points = { - "foo": "bar.py", - }, - group_deps = [ - "foo", - "fox", - "qux", - ], - group_name = "qux", - name = "foo.whl", - tags = ["tag1"], -) -""" - actual = generate_whl_library_build_bazel( - dep_template = "@pypi//{name}:{target}", - name = "foo.whl", - dependencies = ["foo"], - dependencies_by_platform = {"baz": ["bar"]}, - entry_points = { - "foo": "bar.py", - }, - annotation = struct( - enable_implicit_namespace_pkgs = False, - ), - group_name = "qux", - group_deps = ["foo", "fox", "qux"], - tags = ["tag1"], - ) - env.expect.that_str(actual.replace("@@", "@")).equals(want) - -_tests.append(_test_enable_implicit_namespace_pkgs_annotation_false) def generate_whl_library_build_bazel_test_suite(name): """Create the test suite. From 2de41535140ec3b0e1398315c497a27113bf659d Mon Sep 17 00:00:00 2001 From: chua Date: Thu, 18 Sep 2025 08:24:00 +0000 Subject: [PATCH 4/5] make tests pass --- python/private/pypi/generate_whl_library_build_bazel.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/private/pypi/generate_whl_library_build_bazel.bzl b/python/private/pypi/generate_whl_library_build_bazel.bzl index 050c86d740..ee3d84c410 100644 --- a/python/private/pypi/generate_whl_library_build_bazel.bzl +++ b/python/private/pypi/generate_whl_library_build_bazel.bzl @@ -109,7 +109,7 @@ def generate_whl_library_build_bazel( kwargs["copy_executables"] = annotation.copy_executables kwargs["data_exclude"] = kwargs.get("data_exclude", []) + annotation.data_exclude_glob kwargs["srcs_exclude"] = annotation.srcs_exclude_glob - if annotation.enable_implicit_namespace_pkgs != None: + if hasattr(annotation, "enable_implicit_namespace_pkgs") and annotation.enable_implicit_namespace_pkgs != None: kwargs["enable_implicit_namespace_pkgs"] = annotation.enable_implicit_namespace_pkgs if annotation.additive_build_content: additional_content.append(annotation.additive_build_content) From 1e187c467e7379b7b5e0139d1ec5a80d49683b44 Mon Sep 17 00:00:00 2001 From: chua Date: Fri, 19 Sep 2025 03:47:51 +0000 Subject: [PATCH 5/5] attempt to use whl_mod approach --- python/private/pypi/extension.bzl | 8 +++ tests/pypi/extension/extension_tests.bzl | 77 ++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/python/private/pypi/extension.bzl b/python/private/pypi/extension.bzl index c14912c2c9..d70340f184 100644 --- a/python/private/pypi/extension.bzl +++ b/python/private/pypi/extension.bzl @@ -49,6 +49,7 @@ def _whl_mods_impl(whl_mods_dict): data = mods.data, data_exclude_glob = mods.data_exclude_glob, srcs_exclude_glob = mods.srcs_exclude_glob, + enable_implicit_namespace_pkgs = mods.enable_implicit_namespace_pkgs, )) _whl_mods_repo( @@ -178,6 +179,7 @@ You cannot use both the additive_build_content and additive_build_content_file a data = whl_mod.data, data_exclude_glob = whl_mod.data_exclude_glob, srcs_exclude_glob = whl_mod.srcs_exclude_glob, + enable_implicit_namespace_pkgs = whl_mod.enable_implicit_namespace_pkgs, ) config = build_config(module_ctx = module_ctx, enable_pipstar = enable_pipstar) @@ -741,6 +743,12 @@ cannot have a child module that uses the same `hub_name`. doc = "The whl name that the modifications are used for.", mandatory = True, ), + "enable_implicit_namespace_pkgs": attr.bool( + doc = """\ +(bool, optional): Override the global setting for generating __init__.py +files for namespace packages. If None, uses the repository-level setting. +""", + ), } return attrs diff --git a/tests/pypi/extension/extension_tests.bzl b/tests/pypi/extension/extension_tests.bzl index 0514e1d95b..4257db704e 100644 --- a/tests/pypi/extension/extension_tests.bzl +++ b/tests/pypi/extension/extension_tests.bzl @@ -49,6 +49,20 @@ simple==0.0.1 \ ], ) +def _whl_mod(*, hub_name, whl_name, additive_build_content = None, additive_build_content_file = None, copy_executables = {}, copy_files = {}, data = [], data_exclude_glob = [], srcs_exclude_glob = [], enable_implicit_namespace_pkgs = None): + return struct( + hub_name = hub_name, + whl_name = whl_name, + additive_build_content = additive_build_content, + additive_build_content_file = additive_build_content_file, + copy_executables = copy_executables, + copy_files = copy_files, + data = data, + data_exclude_glob = data_exclude_glob, + srcs_exclude_glob = srcs_exclude_glob, + enable_implicit_namespace_pkgs = enable_implicit_namespace_pkgs, + ) + def _mod(*, name, default = [], parse = [], override = [], whl_mods = [], is_root = True): return struct( name = name, @@ -234,6 +248,69 @@ def _test_build_pipstar_platform(env): _tests.append(_test_build_pipstar_platform) +def _test_whl_mods_with_namespace_pkgs(env): + pypi = _parse_modules( + env, + module_ctx = _mock_mctx( + _mod( + name = "rules_python", + parse = [ + _parse( + hub_name = "pypi", + python_version = "3.15", + requirements_lock = "requirements.txt", + ), + ], + whl_mods = [ + _whl_mod( + hub_name = "pypi", + whl_name = "simple", + additive_build_content = "# Custom build content", + enable_implicit_namespace_pkgs = True, + ), + ], + ), + ), + available_interpreters = { + "python_3_15_host": "unit_test_interpreter_target", + }, + minor_mapping = {"3.15": "3.15.19"}, + ) + + pypi.exposed_packages().contains_exactly({"pypi": ["simple"]}) + pypi.hub_group_map().contains_exactly({"pypi": {}}) + pypi.hub_whl_map().contains_exactly({"pypi": { + "simple": { + "pypi_315_simple": [ + whl_config_setting( + version = "3.15", + ), + ], + }, + }}) + pypi.whl_libraries().contains_exactly({ + "pypi_315_simple": { + "dep_template": "@pypi//{name}:{target}", + "python_interpreter_target": "unit_test_interpreter_target", + "requirement": "simple==0.0.1 --hash=sha256:deadbeef --hash=sha256:deadbaaf", + }, + }) + pypi.whl_mods().contains_exactly({ + "pypi": { + "simple": struct( + build_content = "# Custom build content", + copy_files = {}, + copy_executables = {}, + data = [], + data_exclude_glob = [], + srcs_exclude_glob = [], + enable_implicit_namespace_pkgs = True, + ), + }, + }) + +_tests.append(_test_whl_mods_with_namespace_pkgs) + def extension_test_suite(name): """Create the test suite.