From dce29818adf2709004b2a219738e71cf30bc7ce3 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sat, 24 May 2025 16:11:44 +0900 Subject: [PATCH] refactor(pypi): return a list from parse_requirements The modeling of the data structures returned by the `parse_requirements` function was not optimal and this was because historically there was more logic in the `extension.bzl` and more things were decided there. With the recent refactors it is possible to have a harder to misuse data structure from the `parse_requirements`. For each `package` we will return a struct which will have a `srcs` field that will contain easy to consume values. With this in place we can do the fix that is outlined in the referenced issue. Work towards #2648 --- python/private/pypi/extension.bzl | 172 +++---- python/private/pypi/parse_requirements.bzl | 92 +++- python/private/pypi/pip_repository.bzl | 6 +- .../parse_requirements_tests.bzl | 485 ++++++++++-------- 4 files changed, 424 insertions(+), 331 deletions(-) diff --git a/python/private/pypi/extension.bzl b/python/private/pypi/extension.bzl index d3a15dfc44..b79be6e038 100644 --- a/python/private/pypi/extension.bzl +++ b/python/private/pypi/extension.bzl @@ -202,8 +202,12 @@ def _create_whl_repos( logger = logger, ) - for whl_name, requirements in requirements_by_platform.items(): - group_name = whl_group_mapping.get(whl_name) + exposed_packages = {} + for whl in requirements_by_platform: + if whl.is_exposed: + exposed_packages[whl.name] = None + + group_name = whl_group_mapping.get(whl.name) group_deps = requirement_cycles.get(group_name, []) # Construct args separately so that the lock file can be smaller and does not include unused @@ -214,7 +218,7 @@ def _create_whl_repos( maybe_args = dict( # The following values are safe to omit if they have false like values add_libdir_to_library_search_path = pip_attr.add_libdir_to_library_search_path, - annotation = whl_modifications.get(whl_name), + annotation = whl_modifications.get(whl.name), download_only = pip_attr.download_only, enable_implicit_namespace_pkgs = pip_attr.enable_implicit_namespace_pkgs, environment = pip_attr.environment, @@ -226,7 +230,7 @@ def _create_whl_repos( python_interpreter_target = python_interpreter_target, whl_patches = { p: json.encode(args) - for p, args in whl_overrides.get(whl_name, {}).items() + for p, args in whl_overrides.get(whl.name, {}).items() }, ) if not enable_pipstar: @@ -245,119 +249,99 @@ def _create_whl_repos( if v != default }) - for requirement in requirements: - for repo_name, (args, config_setting) in _whl_repos( - requirement = requirement, + for src in whl.srcs: + repo = _whl_repo( + src = src, whl_library_args = whl_library_args, download_only = pip_attr.download_only, netrc = pip_attr.netrc, auth_patterns = pip_attr.auth_patterns, python_version = major_minor, - multiple_requirements_for_whl = len(requirements) > 1., + is_multiple_versions = whl.is_multiple_versions, enable_pipstar = enable_pipstar, - ).items(): - repo_name = "{}_{}".format(pip_name, repo_name) - if repo_name in whl_libraries: - fail("Attempting to creating a duplicate library {} for {}".format( - repo_name, - whl_name, - )) + ) - whl_libraries[repo_name] = args - whl_map.setdefault(whl_name, {})[config_setting] = repo_name + repo_name = "{}_{}".format(pip_name, repo.repo_name) + if repo_name in whl_libraries: + fail("Attempting to creating a duplicate library {} for {}".format( + repo_name, + whl.name, + )) + + whl_libraries[repo_name] = repo.args + whl_map.setdefault(whl.name, {})[repo.config_setting] = repo_name return struct( whl_map = whl_map, - exposed_packages = { - whl_name: None - for whl_name, requirements in requirements_by_platform.items() - if len([r for r in requirements if r.is_exposed]) > 0 - }, + exposed_packages = exposed_packages, extra_aliases = extra_aliases, whl_libraries = whl_libraries, ) -def _whl_repos(*, requirement, whl_library_args, download_only, netrc, auth_patterns, multiple_requirements_for_whl = False, python_version, enable_pipstar = False): - ret = {} - - dists = requirement.whls - if not download_only and requirement.sdist: - dists = dists + [requirement.sdist] - - for distribution in dists: - args = dict(whl_library_args) - if netrc: - args["netrc"] = netrc - if auth_patterns: - args["auth_patterns"] = auth_patterns - - if not distribution.filename.endswith(".whl"): - # pip is not used to download wheels and the python - # `whl_library` helpers are only extracting things, however - # for sdists, they will be built by `pip`, so we still - # need to pass the extra args there. - args["extra_pip_args"] = requirement.extra_pip_args - - # This is no-op because pip is not used to download the wheel. - args.pop("download_only", None) - - args["requirement"] = requirement.line - args["urls"] = [distribution.url] - args["sha256"] = distribution.sha256 - args["filename"] = distribution.filename - if not enable_pipstar: - args["experimental_target_platforms"] = [ - # Get rid of the version fot the target platforms because we are - # passing the interpreter any way. Ideally we should search of ways - # how to pass the target platforms through the hub repo. - p.partition("_")[2] - for p in requirement.target_platforms - ] - - # Pure python wheels or sdists may need to have a platform here - target_platforms = None - if distribution.filename.endswith(".whl") and not distribution.filename.endswith("-any.whl"): - pass - elif multiple_requirements_for_whl: - target_platforms = requirement.target_platforms - - repo_name = whl_repo_name( - distribution.filename, - distribution.sha256, - ) - ret[repo_name] = ( - args, - whl_config_setting( +def _whl_repo(*, src, whl_library_args, is_multiple_versions, download_only, netrc, auth_patterns, python_version, enable_pipstar = False): + args = dict(whl_library_args) + args["requirement"] = src.requirement_line + is_whl = src.filename.endswith(".whl") + + if src.extra_pip_args and not is_whl: + # pip is not used to download wheels and the python + # `whl_library` helpers are only extracting things, however + # for sdists, they will be built by `pip`, so we still + # need to pass the extra args there, so only pop this for whls + args["extra_pip_args"] = src.extra_pip_args + + if not src.url or (not is_whl and download_only): + # Fallback to a pip-installed wheel + target_platforms = src.target_platforms if is_multiple_versions else [] + return struct( + repo_name = pypi_repo_name( + normalize_name(src.distribution), + *target_platforms + ), + args = args, + config_setting = whl_config_setting( version = python_version, - filename = distribution.filename, - target_platforms = target_platforms, + target_platforms = target_platforms or None, ), ) - if ret: - return ret - - # Fallback to a pip-installed wheel - args = dict(whl_library_args) # make a copy - args["requirement"] = requirement.line - if requirement.extra_pip_args: - args["extra_pip_args"] = requirement.extra_pip_args + # This is no-op because pip is not used to download the wheel. + args.pop("download_only", None) + + if netrc: + args["netrc"] = netrc + if auth_patterns: + args["auth_patterns"] = auth_patterns + + args["urls"] = [src.url] + args["sha256"] = src.sha256 + args["filename"] = src.filename + if not enable_pipstar: + args["experimental_target_platforms"] = [ + # Get rid of the version fot the target platforms because we are + # passing the interpreter any way. Ideally we should search of ways + # how to pass the target platforms through the hub repo. + p.partition("_")[2] + for p in src.target_platforms + ] + + # Pure python wheels or sdists may need to have a platform here + target_platforms = None + if is_whl and not src.filename.endswith("-any.whl"): + pass + elif is_multiple_versions: + target_platforms = src.target_platforms - target_platforms = requirement.target_platforms if multiple_requirements_for_whl else [] - repo_name = pypi_repo_name( - normalize_name(requirement.distribution), - *target_platforms - ) - ret[repo_name] = ( - args, - whl_config_setting( + return struct( + repo_name = whl_repo_name(src.filename, src.sha256), + args = args, + config_setting = whl_config_setting( version = python_version, - target_platforms = target_platforms or None, + filename = src.filename, + target_platforms = target_platforms, ), ) - return ret - def parse_modules( module_ctx, _fail = fail, diff --git a/python/private/pypi/parse_requirements.bzl b/python/private/pypi/parse_requirements.bzl index bdfac46ed6..bd2981efc0 100644 --- a/python/private/pypi/parse_requirements.bzl +++ b/python/private/pypi/parse_requirements.bzl @@ -179,49 +179,91 @@ def parse_requirements( }), ) - ret = {} - for whl_name, reqs in sorted(requirements_by_platform.items()): + ret = [] + for name, reqs in sorted(requirements_by_platform.items()): requirement_target_platforms = {} for r in reqs.values(): target_platforms = env_marker_target_platforms.get(r.requirement_line, r.target_platforms) for p in target_platforms: requirement_target_platforms[p] = None - is_exposed = len(requirement_target_platforms) == len(requirements) - if not is_exposed and logger: + item = struct( + # Return normalized names + name = normalize_name(name), + is_exposed = len(requirement_target_platforms) == len(requirements), + is_multiple_versions = len(reqs.values()) > 1, + srcs = _package_srcs( + name = name, + reqs = reqs, + index_urls = index_urls, + env_marker_target_platforms = env_marker_target_platforms, + extract_url_srcs = extract_url_srcs, + logger = logger, + ), + ) + ret.append(item) + if not item.is_exposed and logger: logger.debug(lambda: "Package '{}' will not be exposed because it is only present on a subset of platforms: {} out of {}".format( - whl_name, + name, sorted(requirement_target_platforms), sorted(requirements), )) - # Return normalized names - ret_requirements = ret.setdefault(normalize_name(whl_name), []) + if logger: + logger.debug(lambda: "Will configure whl repos: {}".format([w.name for w in ret])) - for r in sorted(reqs.values(), key = lambda r: r.requirement_line): - whls, sdist = _add_dists( - requirement = r, - index_urls = index_urls.get(whl_name), - logger = logger, - ) + return ret - target_platforms = env_marker_target_platforms.get(r.requirement_line, r.target_platforms) - ret_requirements.append( +def _package_srcs( + *, + name, + reqs, + index_urls, + logger, + env_marker_target_platforms, + extract_url_srcs): + """A function to return sources for a particular package.""" + srcs = [] + for r in sorted(reqs.values(), key = lambda r: r.requirement_line): + whls, sdist = _add_dists( + requirement = r, + index_urls = index_urls.get(name), + logger = logger, + ) + + target_platforms = env_marker_target_platforms.get(r.requirement_line, r.target_platforms) + target_platforms = sorted(target_platforms) + + all_dists = [] + whls + if sdist: + all_dists.append(sdist) + + if extract_url_srcs and all_dists: + req_line = r.srcs.requirement + else: + all_dists = [struct( + url = "", + filename = "", + sha256 = "", + yanked = False, + )] + req_line = r.srcs.requirement_line + + for dist in all_dists: + srcs.append( struct( - distribution = r.distribution, - line = r.srcs.requirement if extract_url_srcs and (whls or sdist) else r.srcs.requirement_line, - target_platforms = sorted(target_platforms), + distribution = name, extra_pip_args = r.extra_pip_args, - whls = whls, - sdist = sdist, - is_exposed = is_exposed, + requirement_line = req_line, + target_platforms = target_platforms, + filename = dist.filename, + sha256 = dist.sha256, + url = dist.url, + yanked = dist.yanked, ), ) - if logger: - logger.debug(lambda: "Will configure whl repos: {}".format(ret.keys())) - - return ret + return srcs def select_requirement(requirements, *, platform): """A simple function to get a requirement for a particular platform. diff --git a/python/private/pypi/pip_repository.bzl b/python/private/pypi/pip_repository.bzl index c8d23f471f..724fb6ddba 100644 --- a/python/private/pypi/pip_repository.bzl +++ b/python/private/pypi/pip_repository.bzl @@ -94,15 +94,15 @@ def _pip_repository_impl(rctx): selected_requirements = {} options = None repository_platform = host_platform(rctx) - for name, requirements in requirements_by_platform.items(): + for whl in requirements_by_platform: requirement = select_requirement( - requirements, + whl.srcs, platform = None if rctx.attr.download_only else repository_platform, ) if not requirement: continue options = options or requirement.extra_pip_args - selected_requirements[name] = requirement.line + selected_requirements[whl.name] = requirement.requirement_line bzl_packages = sorted(selected_requirements.keys()) diff --git a/tests/pypi/parse_requirements/parse_requirements_tests.bzl b/tests/pypi/parse_requirements/parse_requirements_tests.bzl index 497e08361f..926a7e0c50 100644 --- a/tests/pypi/parse_requirements/parse_requirements_tests.bzl +++ b/tests/pypi/parse_requirements/parse_requirements_tests.bzl @@ -100,22 +100,28 @@ def _test_simple(env): "requirements_lock": ["linux_x86_64", "windows_x86_64"], }, ) - env.expect.that_dict(got).contains_exactly({ - "foo": [ - struct( - distribution = "foo", - extra_pip_args = [], - sdist = None, - is_exposed = True, - line = "foo[extra]==0.0.1 --hash=sha256:deadbeef", - target_platforms = [ - "linux_x86_64", - "windows_x86_64", - ], - whls = [], - ), - ], - }) + env.expect.that_collection(got).contains_exactly([ + struct( + name = "foo", + is_exposed = True, + is_multiple_versions = False, + srcs = [ + struct( + distribution = "foo", + extra_pip_args = [], + requirement_line = "foo[extra]==0.0.1 --hash=sha256:deadbeef", + target_platforms = [ + "linux_x86_64", + "windows_x86_64", + ], + url = "", + filename = "", + sha256 = "", + yanked = False, + ), + ], + ), + ]) _tests.append(_test_simple) @@ -127,24 +133,25 @@ def _test_direct_urls_integration(env): "requirements_direct": ["linux_x86_64"], }, ) - env.expect.that_dict(got).contains_exactly({ - "foo": [ - struct( - distribution = "foo", - extra_pip_args = [], - sdist = None, - is_exposed = True, - line = "foo[extra]", - target_platforms = ["linux_x86_64"], - whls = [struct( + env.expect.that_collection(got).contains_exactly([ + struct( + name = "foo", + is_exposed = True, + is_multiple_versions = False, + srcs = [ + struct( + distribution = "foo", + extra_pip_args = [], + requirement_line = "foo[extra]", + target_platforms = ["linux_x86_64"], url = "https://some-url/package.whl", filename = "package.whl", sha256 = "", yanked = False, - )], - ), - ], - }) + ), + ], + ), + ]) _tests.append(_test_direct_urls_integration) @@ -156,21 +163,27 @@ def _test_extra_pip_args(env): }, extra_pip_args = ["--trusted-host=example.org"], ) - env.expect.that_dict(got).contains_exactly({ - "foo": [ - struct( - distribution = "foo", - extra_pip_args = ["--index-url=example.org", "--trusted-host=example.org"], - sdist = None, - is_exposed = True, - line = "foo[extra]==0.0.1 --hash=sha256:deadbeef", - target_platforms = [ - "linux_x86_64", - ], - whls = [], - ), - ], - }) + env.expect.that_collection(got).contains_exactly([ + struct( + name = "foo", + is_exposed = True, + is_multiple_versions = False, + srcs = [ + struct( + distribution = "foo", + extra_pip_args = ["--index-url=example.org", "--trusted-host=example.org"], + requirement_line = "foo[extra]==0.0.1 --hash=sha256:deadbeef", + target_platforms = [ + "linux_x86_64", + ], + url = "", + filename = "", + sha256 = "", + yanked = False, + ), + ], + ), + ]) _tests.append(_test_extra_pip_args) @@ -181,19 +194,25 @@ def _test_dupe_requirements(env): "requirements_lock_dupe": ["linux_x86_64"], }, ) - env.expect.that_dict(got).contains_exactly({ - "foo": [ - struct( - distribution = "foo", - extra_pip_args = [], - sdist = None, - is_exposed = True, - line = "foo[extra,extra_2]==0.0.1 --hash=sha256:deadbeef", - target_platforms = ["linux_x86_64"], - whls = [], - ), - ], - }) + env.expect.that_collection(got).contains_exactly([ + struct( + name = "foo", + is_exposed = True, + is_multiple_versions = False, + srcs = [ + struct( + distribution = "foo", + extra_pip_args = [], + requirement_line = "foo[extra,extra_2]==0.0.1 --hash=sha256:deadbeef", + target_platforms = ["linux_x86_64"], + url = "", + filename = "", + sha256 = "", + yanked = False, + ), + ], + ), + ]) _tests.append(_test_dupe_requirements) @@ -206,44 +225,57 @@ def _test_multi_os(env): }, ) - env.expect.that_dict(got).contains_exactly({ - "bar": [ - struct( - distribution = "bar", - extra_pip_args = [], - line = "bar==0.0.1 --hash=sha256:deadb00f", - target_platforms = ["windows_x86_64"], - whls = [], - sdist = None, - is_exposed = False, - ), - ], - "foo": [ - struct( - distribution = "foo", - extra_pip_args = [], - line = "foo==0.0.3 --hash=sha256:deadbaaf", - target_platforms = ["linux_x86_64"], - whls = [], - sdist = None, - is_exposed = True, - ), - struct( - distribution = "foo", - extra_pip_args = [], - line = "foo[extra]==0.0.2 --hash=sha256:deadbeef", - target_platforms = ["windows_x86_64"], - whls = [], - sdist = None, - is_exposed = True, - ), - ], - }) + env.expect.that_collection(got).contains_exactly([ + struct( + name = "bar", + is_exposed = False, + is_multiple_versions = False, + srcs = [ + struct( + distribution = "bar", + extra_pip_args = [], + requirement_line = "bar==0.0.1 --hash=sha256:deadb00f", + target_platforms = ["windows_x86_64"], + url = "", + filename = "", + sha256 = "", + yanked = False, + ), + ], + ), + struct( + name = "foo", + is_exposed = True, + is_multiple_versions = True, + srcs = [ + struct( + distribution = "foo", + extra_pip_args = [], + requirement_line = "foo==0.0.3 --hash=sha256:deadbaaf", + target_platforms = ["linux_x86_64"], + url = "", + filename = "", + sha256 = "", + yanked = False, + ), + struct( + distribution = "foo", + extra_pip_args = [], + requirement_line = "foo[extra]==0.0.2 --hash=sha256:deadbeef", + target_platforms = ["windows_x86_64"], + url = "", + filename = "", + sha256 = "", + yanked = False, + ), + ], + ), + ]) env.expect.that_str( select_requirement( - got["foo"], + got[1].srcs, platform = "windows_x86_64", - ).line, + ).requirement_line, ).equals("foo[extra]==0.0.2 --hash=sha256:deadbeef") _tests.append(_test_multi_os) @@ -257,39 +289,52 @@ def _test_multi_os_legacy(env): }, ) - env.expect.that_dict(got).contains_exactly({ - "bar": [ - struct( - distribution = "bar", - extra_pip_args = ["--platform=manylinux_2_17_x86_64", "--python-version=39", "--implementation=cp", "--abi=cp39"], - is_exposed = False, - sdist = None, - line = "bar==0.0.1 --hash=sha256:deadb00f", - target_platforms = ["cp39_linux_x86_64"], - whls = [], - ), - ], - "foo": [ - struct( - distribution = "foo", - extra_pip_args = ["--platform=manylinux_2_17_x86_64", "--python-version=39", "--implementation=cp", "--abi=cp39"], - is_exposed = True, - sdist = None, - line = "foo==0.0.1 --hash=sha256:deadbeef", - target_platforms = ["cp39_linux_x86_64"], - whls = [], - ), - struct( - distribution = "foo", - extra_pip_args = ["--platform=macosx_10_9_arm64", "--python-version=39", "--implementation=cp", "--abi=cp39"], - is_exposed = True, - sdist = None, - line = "foo==0.0.3 --hash=sha256:deadbaaf", - target_platforms = ["cp39_osx_aarch64"], - whls = [], - ), - ], - }) + env.expect.that_collection(got).contains_exactly([ + struct( + name = "bar", + is_exposed = False, + is_multiple_versions = False, + srcs = [ + struct( + distribution = "bar", + extra_pip_args = ["--platform=manylinux_2_17_x86_64", "--python-version=39", "--implementation=cp", "--abi=cp39"], + requirement_line = "bar==0.0.1 --hash=sha256:deadb00f", + target_platforms = ["cp39_linux_x86_64"], + url = "", + filename = "", + sha256 = "", + yanked = False, + ), + ], + ), + struct( + name = "foo", + is_exposed = True, + is_multiple_versions = True, + srcs = [ + struct( + distribution = "foo", + extra_pip_args = ["--platform=manylinux_2_17_x86_64", "--python-version=39", "--implementation=cp", "--abi=cp39"], + requirement_line = "foo==0.0.1 --hash=sha256:deadbeef", + target_platforms = ["cp39_linux_x86_64"], + url = "", + filename = "", + sha256 = "", + yanked = False, + ), + struct( + distribution = "foo", + extra_pip_args = ["--platform=macosx_10_9_arm64", "--python-version=39", "--implementation=cp", "--abi=cp39"], + requirement_line = "foo==0.0.3 --hash=sha256:deadbaaf", + target_platforms = ["cp39_osx_aarch64"], + url = "", + filename = "", + sha256 = "", + yanked = False, + ), + ], + ), + ]) _tests.append(_test_multi_os_legacy) @@ -324,30 +369,42 @@ def _test_env_marker_resolution(env): }, evaluate_markers = _mock_eval_markers, ) - env.expect.that_dict(got).contains_exactly({ - "bar": [ - struct( - distribution = "bar", - extra_pip_args = [], - is_exposed = True, - sdist = None, - line = "bar==0.0.1 --hash=sha256:deadbeef", - target_platforms = ["cp311_linux_super_exotic", "cp311_windows_x86_64"], - whls = [], - ), - ], - "foo": [ - struct( - distribution = "foo", - extra_pip_args = [], - is_exposed = False, - sdist = None, - line = "foo[extra]==0.0.1 --hash=sha256:deadbeef", - target_platforms = ["cp311_windows_x86_64"], - whls = [], - ), - ], - }) + env.expect.that_collection(got).contains_exactly([ + struct( + name = "bar", + is_exposed = True, + is_multiple_versions = False, + srcs = [ + struct( + distribution = "bar", + extra_pip_args = [], + requirement_line = "bar==0.0.1 --hash=sha256:deadbeef", + target_platforms = ["cp311_linux_super_exotic", "cp311_windows_x86_64"], + url = "", + filename = "", + sha256 = "", + yanked = False, + ), + ], + ), + struct( + name = "foo", + is_exposed = False, + is_multiple_versions = False, + srcs = [ + struct( + distribution = "foo", + extra_pip_args = [], + requirement_line = "foo[extra]==0.0.1 --hash=sha256:deadbeef", + target_platforms = ["cp311_windows_x86_64"], + url = "", + filename = "", + sha256 = "", + yanked = False, + ), + ], + ), + ]) _tests.append(_test_env_marker_resolution) @@ -358,28 +415,35 @@ def _test_different_package_version(env): "requirements_different_package_version": ["linux_x86_64"], }, ) - env.expect.that_dict(got).contains_exactly({ - "foo": [ - struct( - distribution = "foo", - extra_pip_args = [], - is_exposed = True, - sdist = None, - line = "foo==0.0.1 --hash=sha256:deadb00f", - target_platforms = ["linux_x86_64"], - whls = [], - ), - struct( - distribution = "foo", - extra_pip_args = [], - is_exposed = True, - sdist = None, - line = "foo==0.0.1+local --hash=sha256:deadbeef", - target_platforms = ["linux_x86_64"], - whls = [], - ), - ], - }) + env.expect.that_collection(got).contains_exactly([ + struct( + name = "foo", + is_exposed = True, + is_multiple_versions = True, + srcs = [ + struct( + distribution = "foo", + extra_pip_args = [], + requirement_line = "foo==0.0.1 --hash=sha256:deadb00f", + target_platforms = ["linux_x86_64"], + url = "", + filename = "", + sha256 = "", + yanked = False, + ), + struct( + distribution = "foo", + extra_pip_args = [], + requirement_line = "foo==0.0.1+local --hash=sha256:deadbeef", + target_platforms = ["linux_x86_64"], + url = "", + filename = "", + sha256 = "", + yanked = False, + ), + ], + ), + ]) _tests.append(_test_different_package_version) @@ -390,38 +454,35 @@ def _test_optional_hash(env): "requirements_optional_hash": ["linux_x86_64"], }, ) - env.expect.that_dict(got).contains_exactly({ - "foo": [ - struct( - distribution = "foo", - extra_pip_args = [], - sdist = None, - is_exposed = True, - line = "foo==0.0.4", - target_platforms = ["linux_x86_64"], - whls = [struct( + env.expect.that_collection(got).contains_exactly([ + struct( + name = "foo", + is_exposed = True, + is_multiple_versions = True, + srcs = [ + struct( + distribution = "foo", + extra_pip_args = [], + requirement_line = "foo==0.0.4", + target_platforms = ["linux_x86_64"], url = "https://example.org/foo-0.0.4.whl", filename = "foo-0.0.4.whl", sha256 = "", yanked = False, - )], - ), - struct( - distribution = "foo", - extra_pip_args = [], - sdist = None, - is_exposed = True, - line = "foo==0.0.5", - target_platforms = ["linux_x86_64"], - whls = [struct( + ), + struct( + distribution = "foo", + extra_pip_args = [], + requirement_line = "foo==0.0.5", + target_platforms = ["linux_x86_64"], url = "https://example.org/foo-0.0.5.whl", filename = "foo-0.0.5.whl", sha256 = "deadbeef", yanked = False, - )], - ), - ], - }) + ), + ], + ), + ]) _tests.append(_test_optional_hash) @@ -432,19 +493,25 @@ def _test_git_sources(env): "requirements_git": ["linux_x86_64"], }, ) - env.expect.that_dict(got).contains_exactly({ - "foo": [ - struct( - distribution = "foo", - extra_pip_args = [], - is_exposed = True, - sdist = None, - line = "foo @ git+https://github.com/org/foo.git@deadbeef", - target_platforms = ["linux_x86_64"], - whls = [], - ), - ], - }) + env.expect.that_collection(got).contains_exactly([ + struct( + name = "foo", + is_exposed = True, + is_multiple_versions = False, + srcs = [ + struct( + distribution = "foo", + extra_pip_args = [], + requirement_line = "foo @ git+https://github.com/org/foo.git@deadbeef", + target_platforms = ["linux_x86_64"], + url = "", + filename = "", + sha256 = "", + yanked = False, + ), + ], + ), + ]) _tests.append(_test_git_sources)