From 920f5ade4fab3169685af84254ffc6ade1695e54 Mon Sep 17 00:00:00 2001 From: Kristian Hartikainen Date: Sat, 15 Aug 2026 02:00:57 -0400 Subject: [PATCH 1/2] Support source-less wheels with dependencies `rules_python` `2.3.x` wraps source-less wheels in a `py_library` whose `srcs` target has no Python files. Forward the target only when it has valid sources, and cover the wrapper behavior with unit tests. --- news/sourceless-wheel-srcs.fixed.md | 2 + python/private/pypi/whl_library_targets.bzl | 37 ++++-- .../whl_library_targets_tests.bzl | 116 ++++++++++++++++++ 3 files changed, 147 insertions(+), 8 deletions(-) create mode 100644 news/sourceless-wheel-srcs.fixed.md diff --git a/news/sourceless-wheel-srcs.fixed.md b/news/sourceless-wheel-srcs.fixed.md new file mode 100644 index 0000000000..412c76bbf2 --- /dev/null +++ b/news/sourceless-wheel-srcs.fixed.md @@ -0,0 +1,2 @@ +(pypi) Fixed analysis failures for source-less wheels with dependencies in +per-wheel repositories. diff --git a/python/private/pypi/whl_library_targets.bzl b/python/private/pypi/whl_library_targets.bzl index b7fdbd55e9..92b463d68b 100644 --- a/python/private/pypi/whl_library_targets.bzl +++ b/python/private/pypi/whl_library_targets.bzl @@ -101,7 +101,7 @@ def whl_library_targets( **kwargs: Extra args passed to the {obj}`whl_library_deps_targets` and {obj}`whl_library_srcs`. """ create_extra_targets = bool(requires_dist or group_name) and dep_template - whl_library_srcs( + wrapper_srcs = whl_library_srcs( name = name, sdist_filename = sdist_filename, data_exclude = data_exclude, @@ -133,6 +133,7 @@ def whl_library_targets( dep_template = dep_template, # only needed if requires_dist is present repo = None, # set aliases in the same repo aliases = {}, + srcs = wrapper_srcs, **kwargs ) @@ -193,9 +194,14 @@ def whl_library_srcs( pkg_name: {type}`str` The label name to use for the py_library target. native: {type}`native` The native struct for overriding in tests. rules: {type}`struct` A struct with references to rules for creating targets. + + Returns: + The source labels attached to the generated `py_library`, or `None` when + `rules` does not define `py_library`. """ tags = sorted(tags) data = [] + data + wrapper_srcs = None bins_for_data_label = [] @@ -301,6 +307,7 @@ def whl_library_srcs( # pure-Python code, e.g. pymssql, which is written in Cython. allow_empty = True, ) + wrapper_srcs = [pkg_name] if srcs else [] # NOTE: pyi files should probably be excluded because they're carried # by the pyi_srcs attribute. However, historical behavior included @@ -328,13 +335,19 @@ def whl_library_srcs( ) if not enable_implicit_namespace_pkgs: + generated_namespace_package_files = 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. + root = "site-packages", + ) + if not wrapper_srcs and generated_namespace_package_files: + wrapper_srcs = select({ + _IS_VENV_SITE_PACKAGES_YES: [], + "//conditions:default": [pkg_name], + }) generated_namespace_package_files = select({ _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. - root = "site-packages", - ), + "//conditions:default": generated_namespace_package_files, }) namespace_package_files += generated_namespace_package_files srcs = srcs + generated_namespace_package_files @@ -356,6 +369,7 @@ def whl_library_srcs( experimental_venvs_site_packages = _VENV_SITE_PACKAGES_FLAG, namespace_package_files = namespace_package_files, ) + return wrapper_srcs def whl_library_deps_targets( *, @@ -369,6 +383,7 @@ def whl_library_deps_targets( group_deps = [], group_name = None, dep_template, + srcs = None, tags = [], visibility = ["//visibility:public"], native = native, @@ -396,6 +411,9 @@ def whl_library_deps_targets( include: {type}`list[str]` The list of packages to include. group_name: {type}`str | None` name of the dependency group (if any). dep_template: {type}`str | None` The dep_template to use. + srcs: {type}`list[Label] | None` or a configurable expression with the + source labels attached to the wrapper `py_library`. If `None`, the + source library target is used. tags: {type}`list[str]` The tags set on the targets. repo: {type}`str | Label | None` The BUILD.bazel label to the parent repo that has the sources. If none, then will take the targets from the current dir. @@ -501,10 +519,13 @@ def whl_library_deps_targets( ) if hasattr(rules, "py_library"): + if srcs == None: + srcs = [repo_label(PY_SRCS_LABEL)] rules.py_library( name = py_library_label, - # We include as srcs to ensure that the (locations :pkg) works as expected. - srcs = [repo_label(PY_SRCS_LABEL)], + # Forward source-producing targets through `srcs` so downstream + # `$(locations :pkg)` expansion does not reject source-less wheels. + srcs = srcs, deps = _deps( # We include as deps, so that `PyInfo` and friends (e.g. `pyi_srcs`) get # propagated. Just passing the target as `srcs` is not enough to propagate 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 3fe1b99768..3484f0b8e1 100644 --- a/tests/pypi/whl_library_targets/whl_library_targets_tests.bzl +++ b/tests/pypi/whl_library_targets/whl_library_targets_tests.bzl @@ -19,11 +19,46 @@ load( "//python/private/pypi:whl_library_targets.bzl", "whl_library_deps_targets", "whl_library_srcs", + "whl_library_targets", ) # buildifier: disable=bzl-visibility load("//tests/support/mocks:mocks.bzl", "mocks") _tests = [] +def _make_whl_library_targets_py_library_calls( + *, + srcs, + data, + generated_inits, + enable_implicit_namespace_pkgs = False): + py_library_calls = [] + m_glob = mocks.glob() + m_glob.results.append([]) # bin + m_glob.results.append([]) # rewrite-bin + m_glob.results.append([]) # rewrite-record + m_glob.results.append(srcs) + m_glob.results.append(data) + m_glob.results.append([]) # pyi + + whl_library_targets( + name = "foo-0-py3-none-any.whl", + metadata_name = "Foo", + requires_dist = ["bar"], + dep_template = "@pypi//{name}:{target}", + enable_implicit_namespace_pkgs = enable_implicit_namespace_pkgs, + filegroups = {}, + native = struct(glob = m_glob.glob), + rules = struct( + create_inits = lambda **_: generated_inits, + env_marker_setting = lambda **_: None, + gen_wheel_record = lambda **_: None, + py_library = lambda **kwargs: py_library_calls.append(kwargs), + venv_rewrite_shebang = lambda **_: None, + ), + ) + + return py_library_calls + def _test_filegroups(env): calls = [] @@ -191,6 +226,87 @@ def _test_whl_library_deps_targets(env): _tests.append(_test_whl_library_deps_targets) +def _test_whl_library_targets_sourceless(env): + for enable_implicit_namespace_pkgs, expected_leaf_srcs in [ + (False, [] + select({ + Label("//python/config_settings:_is_venvs_site_packages_yes"): [], + "//conditions:default": [], + })), + (True, []), + ]: + py_library_calls = _make_whl_library_targets_py_library_calls( + srcs = [], + data = [], + generated_inits = [], + enable_implicit_namespace_pkgs = enable_implicit_namespace_pkgs, + ) + + env.expect.that_collection(py_library_calls).has_size(2) + if len(py_library_calls) != 2: + return + + env.expect.that_dict(py_library_calls[0]).contains_at_least({ + "srcs": expected_leaf_srcs, + }) + env.expect.that_dict(py_library_calls[1]).contains_exactly({ + "name": "pkg", + "srcs": [], + "deps": ["srcs", "@pypi//bar:pkg"], + "tags": [], + "visibility": ["//visibility:public"], + }) # buildifier: @unsorted-dict-items + +_tests.append(_test_whl_library_targets_sourceless) + +def _test_whl_library_targets_sourceful(env): + for srcs, data, generated_inits, expected_leaf_srcs, expected_wrapper_srcs in [ + ( + ["site-packages/foo.py"], + [], + [], + ["site-packages/foo.py"] + select({ + Label("//python/config_settings:_is_venvs_site_packages_yes"): [], + "//conditions:default": [], + }), + ["srcs"], + ), + ( + [], + ["site-packages/ext/mod.so"], + ["site-packages/ext/__init__.py"], + [] + select({ + Label("//python/config_settings:_is_venvs_site_packages_yes"): [], + "//conditions:default": ["site-packages/ext/__init__.py"], + }), + select({ + Label("//python/config_settings:_is_venvs_site_packages_yes"): [], + "//conditions:default": ["srcs"], + }), + ), + ]: + py_library_calls = _make_whl_library_targets_py_library_calls( + srcs = srcs, + data = data, + generated_inits = generated_inits, + ) + + env.expect.that_collection(py_library_calls).has_size(2) + if len(py_library_calls) != 2: + return + + env.expect.that_dict(py_library_calls[0]).contains_at_least({ + "srcs": expected_leaf_srcs, + }) + env.expect.that_dict(py_library_calls[1]).contains_exactly({ + "name": "pkg", + "srcs": expected_wrapper_srcs, + "deps": ["srcs", "@pypi//bar:pkg"], + "tags": [], + "visibility": ["//visibility:public"], + }) # buildifier: @unsorted-dict-items + +_tests.append(_test_whl_library_targets_sourceful) + def _test_whl_library_deps_targets_no_deps(env): alias_calls = [] filegroup_calls = [] From 74616c517cac4754eb966de4ad2a88a8f44b80c1 Mon Sep 17 00:00:00 2001 From: Kristian Hartikainen Date: Sun, 16 Aug 2026 11:09:41 -0400 Subject: [PATCH 2/2] fixup! Support source-less wheels with dependencies Source-less wheel wrappers can reference an empty `py_library` from srcs, which Starlark validation rejected during analysis. This change allows empty PyInfo-providing source targets while preserving validation for non-Python outputs and split source/dependency repositories. --- news/4054.fixed.md | 3 + news/sourceless-wheel-srcs.fixed.md | 2 - python/private/py_library.bzl | 35 +++++ python/private/pypi/whl_library_targets.bzl | 37 ++--- .../py_library/py_library_tests.bzl | 133 ++++++++++++++++++ .../whl_library_targets_tests.bzl | 116 --------------- 6 files changed, 179 insertions(+), 147 deletions(-) create mode 100644 news/4054.fixed.md delete mode 100644 news/sourceless-wheel-srcs.fixed.md diff --git a/news/4054.fixed.md b/news/4054.fixed.md new file mode 100644 index 0000000000..e4b259b889 --- /dev/null +++ b/news/4054.fixed.md @@ -0,0 +1,3 @@ +(pypi) Fixed analysis failures in {obj}`pip.parse` for source-less wheels with +dependencies. +([#4053](https://github.com/bazel-contrib/rules_python/issues/4053)) diff --git a/news/sourceless-wheel-srcs.fixed.md b/news/sourceless-wheel-srcs.fixed.md deleted file mode 100644 index 412c76bbf2..0000000000 --- a/news/sourceless-wheel-srcs.fixed.md +++ /dev/null @@ -1,2 +0,0 @@ -(pypi) Fixed analysis failures for source-less wheels with dependencies in -per-wheel repositories. diff --git a/python/private/py_library.bzl b/python/private/py_library.bzl index 3d8ba4aa71..de99840c5c 100644 --- a/python/private/py_library.bzl +++ b/python/private/py_library.bzl @@ -118,6 +118,31 @@ This allows optimizing the generation of symlinks to be cheaper at analysis time }, ) +def _validate_srcs(ctx): + """Validate that srcs targets provide Python sources or Python metadata.""" + for target in ctx.attr.srcs: + files = target[DefaultInfo].files.to_list() + if not files and ( + PyInfo in target or + (BuiltinPyInfo != None and BuiltinPyInfo in target) + ): + continue + + if any([ + file.is_directory or file.extension in ["py", "py3"] + for file in files + ]): + continue + + fail( + ("{} does not produce any py_library srcs files " + + "(expected .py or .py3) and is not an empty target providing " + + "PyInfo").format( + target.label, + ), + attr = "srcs", + ) + def py_library_impl(ctx): """Abstract implementation of py_library rule. @@ -127,6 +152,7 @@ def py_library_impl(ctx): Returns: A list of modern providers to propagate. """ + _validate_srcs(ctx) direct_sources = filter_to_py_srcs(ctx.files.srcs) precompile_result = maybe_precompile(ctx, direct_sources) @@ -297,4 +323,13 @@ def create_py_library_rule_builder(): ruleb.ToolchainType(EXEC_TOOLS_TOOLCHAIN_TYPE, mandatory = False), ], ) + srcs_attr = builder.attrs.get("srcs") + srcs_attr.set_allow_files(True) + srcs_attr.set_doc(srcs_attr.doc() + """ + +:::{versionchanged} VERSION_NEXT_FEATURE +As an exception, empty targets in `srcs` that provide {obj}`PyInfo` are +allowed. Ordinary library dependencies should remain in `deps`. +::: +""") return builder diff --git a/python/private/pypi/whl_library_targets.bzl b/python/private/pypi/whl_library_targets.bzl index 92b463d68b..b7fdbd55e9 100644 --- a/python/private/pypi/whl_library_targets.bzl +++ b/python/private/pypi/whl_library_targets.bzl @@ -101,7 +101,7 @@ def whl_library_targets( **kwargs: Extra args passed to the {obj}`whl_library_deps_targets` and {obj}`whl_library_srcs`. """ create_extra_targets = bool(requires_dist or group_name) and dep_template - wrapper_srcs = whl_library_srcs( + whl_library_srcs( name = name, sdist_filename = sdist_filename, data_exclude = data_exclude, @@ -133,7 +133,6 @@ def whl_library_targets( dep_template = dep_template, # only needed if requires_dist is present repo = None, # set aliases in the same repo aliases = {}, - srcs = wrapper_srcs, **kwargs ) @@ -194,14 +193,9 @@ def whl_library_srcs( pkg_name: {type}`str` The label name to use for the py_library target. native: {type}`native` The native struct for overriding in tests. rules: {type}`struct` A struct with references to rules for creating targets. - - Returns: - The source labels attached to the generated `py_library`, or `None` when - `rules` does not define `py_library`. """ tags = sorted(tags) data = [] + data - wrapper_srcs = None bins_for_data_label = [] @@ -307,7 +301,6 @@ def whl_library_srcs( # pure-Python code, e.g. pymssql, which is written in Cython. allow_empty = True, ) - wrapper_srcs = [pkg_name] if srcs else [] # NOTE: pyi files should probably be excluded because they're carried # by the pyi_srcs attribute. However, historical behavior included @@ -335,19 +328,13 @@ def whl_library_srcs( ) if not enable_implicit_namespace_pkgs: - generated_namespace_package_files = 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. - root = "site-packages", - ) - if not wrapper_srcs and generated_namespace_package_files: - wrapper_srcs = select({ - _IS_VENV_SITE_PACKAGES_YES: [], - "//conditions:default": [pkg_name], - }) generated_namespace_package_files = select({ _IS_VENV_SITE_PACKAGES_YES: [], - "//conditions:default": generated_namespace_package_files, + "//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. + root = "site-packages", + ), }) namespace_package_files += generated_namespace_package_files srcs = srcs + generated_namespace_package_files @@ -369,7 +356,6 @@ def whl_library_srcs( experimental_venvs_site_packages = _VENV_SITE_PACKAGES_FLAG, namespace_package_files = namespace_package_files, ) - return wrapper_srcs def whl_library_deps_targets( *, @@ -383,7 +369,6 @@ def whl_library_deps_targets( group_deps = [], group_name = None, dep_template, - srcs = None, tags = [], visibility = ["//visibility:public"], native = native, @@ -411,9 +396,6 @@ def whl_library_deps_targets( include: {type}`list[str]` The list of packages to include. group_name: {type}`str | None` name of the dependency group (if any). dep_template: {type}`str | None` The dep_template to use. - srcs: {type}`list[Label] | None` or a configurable expression with the - source labels attached to the wrapper `py_library`. If `None`, the - source library target is used. tags: {type}`list[str]` The tags set on the targets. repo: {type}`str | Label | None` The BUILD.bazel label to the parent repo that has the sources. If none, then will take the targets from the current dir. @@ -519,13 +501,10 @@ def whl_library_deps_targets( ) if hasattr(rules, "py_library"): - if srcs == None: - srcs = [repo_label(PY_SRCS_LABEL)] rules.py_library( name = py_library_label, - # Forward source-producing targets through `srcs` so downstream - # `$(locations :pkg)` expansion does not reject source-less wheels. - srcs = srcs, + # We include as srcs to ensure that the (locations :pkg) works as expected. + srcs = [repo_label(PY_SRCS_LABEL)], deps = _deps( # We include as deps, so that `PyInfo` and friends (e.g. `pyi_srcs`) get # propagated. Just passing the target as `srcs` is not enough to propagate diff --git a/tests/base_rules/py_library/py_library_tests.bzl b/tests/base_rules/py_library/py_library_tests.bzl index c375e72794..a3be7c4bcf 100644 --- a/tests/base_rules/py_library/py_library_tests.bzl +++ b/tests/base_rules/py_library/py_library_tests.bzl @@ -12,6 +12,28 @@ load("//tests/support:py_info_subject.bzl", "py_info_subject") _tests = [] +def _tree_artifact_impl(ctx): + out = ctx.actions.declare_directory(ctx.label.name + ".dir") + ctx.actions.run_shell( + outputs = [out], + command = "mkdir -p \"$1\"", + arguments = [out.path], + ) + return [DefaultInfo(files = depset([out]))] + +_tree_artifact = rule(implementation = _tree_artifact_impl) + +def _non_py_py_info_impl(ctx): + return [ + DefaultInfo(files = depset(ctx.files.srcs)), + PyInfo(transitive_sources = depset()), + ] + +_non_py_py_info = rule( + implementation = _non_py_py_info_impl, + attrs = {"srcs": attr.label_list(allow_files = True)}, +) + def _test_py_runtime_info_not_present(name, config): rt_util.helper_target( config.rule, @@ -74,6 +96,117 @@ def _test_srcs_can_contain_rule_generating_py_and_nonpy_files_impl(env, target): _tests.append(_test_srcs_can_contain_rule_generating_py_and_nonpy_files) +def _test_srcs_can_contain_empty_py_library(name, config): + rt_util.helper_target( + config.rule, + name = name + "_empty", + ) + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = [name + "_empty"], + ) + analysis_test( + name = name, + target = name + "_subject", + impl = _test_srcs_can_contain_empty_py_library_impl, + ) + +def _test_srcs_can_contain_empty_py_library_impl(env, target): + env.expect.that_target(target).default_outputs().contains_exactly([]) + +_tests.append(_test_srcs_can_contain_empty_py_library) + +def _test_srcs_can_contain_tree_artifact(name, config): + rt_util.helper_target( + _tree_artifact, + name = name + "_tree", + ) + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = [name + "_tree"], + ) + analysis_test( + name = name, + target = name + "_subject", + impl = _test_srcs_can_contain_tree_artifact_impl, + ) + +def _test_srcs_can_contain_tree_artifact_impl(env, target): + env.expect.that_target(target).default_outputs().contains_exactly([]) + +_tests.append(_test_srcs_can_contain_tree_artifact) + +def _test_srcs_direct_non_py_file_is_error(name, config): + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = [rt_util.empty_file(name + ".txt")], + ) + analysis_test( + name = name, + target = name + "_subject", + impl = _test_srcs_direct_non_py_file_is_error_impl, + expect_failure = True, + ) + +def _test_srcs_direct_non_py_file_is_error_impl(env, target): + env.expect.that_target(target).failures().contains_predicate( + matching.str_matches("does not produce*srcs files"), + ) + +_tests.append(_test_srcs_direct_non_py_file_is_error) + +def _test_srcs_empty_filegroup_is_error(name, config): + rt_util.helper_target( + native.filegroup, + name = name + "_empty", + ) + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = [name + "_empty"], + ) + analysis_test( + name = name, + target = name + "_subject", + impl = _test_srcs_empty_filegroup_is_error_impl, + expect_failure = True, + ) + +def _test_srcs_empty_filegroup_is_error_impl(env, target): + env.expect.that_target(target).failures().contains_predicate( + matching.str_matches("does not produce*srcs files"), + ) + +_tests.append(_test_srcs_empty_filegroup_is_error) + +def _test_srcs_py_info_with_only_non_py_files_is_error(name, config): + rt_util.helper_target( + _non_py_py_info, + name = name + "_non_py", + srcs = [rt_util.empty_file(name + ".txt")], + ) + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = [name + "_non_py"], + ) + analysis_test( + name = name, + target = name + "_subject", + impl = _test_srcs_py_info_with_only_non_py_files_is_error_impl, + expect_failure = True, + ) + +def _test_srcs_py_info_with_only_non_py_files_is_error_impl(env, target): + env.expect.that_target(target).failures().contains_predicate( + matching.str_matches("does not produce*srcs files"), + ) + +_tests.append(_test_srcs_py_info_with_only_non_py_files_is_error) + def _test_srcs_generating_no_py_files_is_error(name, config): rt_util.helper_target( config.rule, 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 3484f0b8e1..3fe1b99768 100644 --- a/tests/pypi/whl_library_targets/whl_library_targets_tests.bzl +++ b/tests/pypi/whl_library_targets/whl_library_targets_tests.bzl @@ -19,46 +19,11 @@ load( "//python/private/pypi:whl_library_targets.bzl", "whl_library_deps_targets", "whl_library_srcs", - "whl_library_targets", ) # buildifier: disable=bzl-visibility load("//tests/support/mocks:mocks.bzl", "mocks") _tests = [] -def _make_whl_library_targets_py_library_calls( - *, - srcs, - data, - generated_inits, - enable_implicit_namespace_pkgs = False): - py_library_calls = [] - m_glob = mocks.glob() - m_glob.results.append([]) # bin - m_glob.results.append([]) # rewrite-bin - m_glob.results.append([]) # rewrite-record - m_glob.results.append(srcs) - m_glob.results.append(data) - m_glob.results.append([]) # pyi - - whl_library_targets( - name = "foo-0-py3-none-any.whl", - metadata_name = "Foo", - requires_dist = ["bar"], - dep_template = "@pypi//{name}:{target}", - enable_implicit_namespace_pkgs = enable_implicit_namespace_pkgs, - filegroups = {}, - native = struct(glob = m_glob.glob), - rules = struct( - create_inits = lambda **_: generated_inits, - env_marker_setting = lambda **_: None, - gen_wheel_record = lambda **_: None, - py_library = lambda **kwargs: py_library_calls.append(kwargs), - venv_rewrite_shebang = lambda **_: None, - ), - ) - - return py_library_calls - def _test_filegroups(env): calls = [] @@ -226,87 +191,6 @@ def _test_whl_library_deps_targets(env): _tests.append(_test_whl_library_deps_targets) -def _test_whl_library_targets_sourceless(env): - for enable_implicit_namespace_pkgs, expected_leaf_srcs in [ - (False, [] + select({ - Label("//python/config_settings:_is_venvs_site_packages_yes"): [], - "//conditions:default": [], - })), - (True, []), - ]: - py_library_calls = _make_whl_library_targets_py_library_calls( - srcs = [], - data = [], - generated_inits = [], - enable_implicit_namespace_pkgs = enable_implicit_namespace_pkgs, - ) - - env.expect.that_collection(py_library_calls).has_size(2) - if len(py_library_calls) != 2: - return - - env.expect.that_dict(py_library_calls[0]).contains_at_least({ - "srcs": expected_leaf_srcs, - }) - env.expect.that_dict(py_library_calls[1]).contains_exactly({ - "name": "pkg", - "srcs": [], - "deps": ["srcs", "@pypi//bar:pkg"], - "tags": [], - "visibility": ["//visibility:public"], - }) # buildifier: @unsorted-dict-items - -_tests.append(_test_whl_library_targets_sourceless) - -def _test_whl_library_targets_sourceful(env): - for srcs, data, generated_inits, expected_leaf_srcs, expected_wrapper_srcs in [ - ( - ["site-packages/foo.py"], - [], - [], - ["site-packages/foo.py"] + select({ - Label("//python/config_settings:_is_venvs_site_packages_yes"): [], - "//conditions:default": [], - }), - ["srcs"], - ), - ( - [], - ["site-packages/ext/mod.so"], - ["site-packages/ext/__init__.py"], - [] + select({ - Label("//python/config_settings:_is_venvs_site_packages_yes"): [], - "//conditions:default": ["site-packages/ext/__init__.py"], - }), - select({ - Label("//python/config_settings:_is_venvs_site_packages_yes"): [], - "//conditions:default": ["srcs"], - }), - ), - ]: - py_library_calls = _make_whl_library_targets_py_library_calls( - srcs = srcs, - data = data, - generated_inits = generated_inits, - ) - - env.expect.that_collection(py_library_calls).has_size(2) - if len(py_library_calls) != 2: - return - - env.expect.that_dict(py_library_calls[0]).contains_at_least({ - "srcs": expected_leaf_srcs, - }) - env.expect.that_dict(py_library_calls[1]).contains_exactly({ - "name": "pkg", - "srcs": expected_wrapper_srcs, - "deps": ["srcs", "@pypi//bar:pkg"], - "tags": [], - "visibility": ["//visibility:public"], - }) # buildifier: @unsorted-dict-items - -_tests.append(_test_whl_library_targets_sourceful) - def _test_whl_library_deps_targets_no_deps(env): alias_calls = [] filegroup_calls = []