Skip to content

Commit 7a9ccf2

Browse files
committed
handle versioned config settings correctly
1 parent 3558f28 commit 7a9ccf2

File tree

2 files changed

+130
-58
lines changed

2 files changed

+130
-58
lines changed

python/private/render_pkg_aliases.bzl

+66-33
Original file line numberDiff line numberDiff line change
@@ -327,17 +327,14 @@ def multiplatform_whl_aliases(*, aliases, default_version, **kwargs):
327327
A dict with aliases to be used in the hub repo.
328328
"""
329329

330-
# TODO @aignas 2024-05-27: add unit tests
331330
ret = []
332-
defaults = {}
331+
versioned_additions = {}
333332
for alias in aliases:
334333
if not alias.filename:
335334
ret.append(alias)
336335
continue
337336

338-
# TODO @aignas 2024-05-30: we need to segment the versions so that we
339-
# don't have duplicates
340-
config_settings, filename_defaults = get_filename_config_settings(
337+
config_settings, all_versioned_settings = get_filename_config_settings(
341338
# TODO @aignas 2024-05-27: pass the parsed whl to reduce the
342339
# number of duplicate operations
343340
filename = alias.filename,
@@ -353,20 +350,53 @@ def multiplatform_whl_aliases(*, aliases, default_version, **kwargs):
353350
version = alias.version,
354351
config_setting = "//_config" + setting,
355352
))
356-
for setting, version in filename_defaults.items():
357-
a = whl_alias(
353+
354+
# Now for the versioned platform config settings, we need to select one
355+
# that best fits the bill and if there are multiple wheels, e.g.
356+
# manylinux_2_17_x86_64 and manylinux_2_28_x86_64, then we need to select
357+
# the former when the glibc is in the range of [2.17, 2.28) and then chose
358+
# the later if it is [2.28, ...). If the 2.28 wheel was not present in
359+
# the hub, then we would need to use 2.17 for all the glibc version
360+
# configurations.
361+
#
362+
# Here we add the version settings to a dict where we key the range of
363+
# versions that the whl spans. If the wheel supports musl and glibc at
364+
# the same time, we do this for each supported platform, hence the
365+
# double dict.
366+
for default_setting, versioned in all_versioned_settings.items():
367+
versions = sorted(versioned)
368+
min_version = versions[0]
369+
max_version = versions[-1]
370+
371+
versioned_additions.setdefault(default_setting, {})[(min_version, max_version)] = struct(
358372
repo = alias.repo,
359-
version = alias.version,
360-
config_setting = "//_config" + setting,
373+
python_version = alias.version,
374+
settings = versioned,
361375
)
362-
_, has_version = defaults.setdefault(setting, (a, version))
363-
if has_version > version:
364-
defaults[a] = (a, version)
365-
366-
ret.extend([
367-
a
368-
for (a, _) in defaults.values()
369-
])
376+
377+
versioned = {}
378+
for default_setting, candidates in versioned_additions.items():
379+
# Sort the candidates by the range of versions the span, so that we
380+
# start with the lowest version.
381+
for _, candidate in sorted(candidates.items()):
382+
# Set the default with the first candidate, which gives us the highest
383+
# compatibility. If the users want to use a higher-version than the default
384+
# they can configure the glibc_version flag.
385+
versioned.setdefault(default_setting, whl_alias(
386+
version = candidate.python_version,
387+
config_setting = "//_config" + default_setting,
388+
repo = candidate.repo,
389+
))
390+
391+
# We will be overwriting previously added entries, but that is intended.
392+
for _, setting in sorted(candidate.settings.items()):
393+
versioned[setting] = whl_alias(
394+
version = candidate.python_version,
395+
config_setting = "//_config" + setting,
396+
repo = candidate.repo,
397+
)
398+
399+
ret.extend(versioned.values())
370400
return ret
371401

372402
def _render_pip_config_settings(python_versions, target_platforms = [], osx_versions = [], glibc_versions = [], muslc_versions = []):
@@ -496,7 +526,7 @@ def get_filename_config_settings(
496526
glibc_versions = sorted(glibc_versions)
497527
muslc_versions = sorted(muslc_versions)
498528
osx_versions = sorted(osx_versions)
499-
default_version_settings = {}
529+
setting_supported_versions = {}
500530

501531
if filename.endswith(".whl"):
502532
parsed = parse_whl_name(filename)
@@ -522,7 +552,7 @@ def get_filename_config_settings(
522552
glibc_versions = glibc_versions,
523553
muslc_versions = muslc_versions,
524554
osx_versions = osx_versions,
525-
default_version_settings = default_version_settings,
555+
setting_supported_versions = setting_supported_versions,
526556
)
527557
else:
528558
prefixes = ["sdist"]
@@ -537,21 +567,26 @@ def get_filename_config_settings(
537567
else:
538568
fail("BUG: got no python_version and it is not default")
539569

540-
if suffixes:
541-
return [":is_{}_{}".format(p, s) for p in prefixes for s in suffixes], {
542-
":is_{}_{}".format(p, suffix): version
543-
for p in prefixes
544-
for suffix, version in default_version_settings.items()
570+
versioned = {
571+
":is_{}_{}".format(p, suffix): {
572+
version: ":is_{}_{}".format(p, setting)
573+
for version, setting in versions.items()
545574
}
575+
for p in prefixes
576+
for suffix, versions in setting_supported_versions.items()
577+
}
578+
579+
if suffixes or versioned:
580+
return [":is_{}_{}".format(p, s) for p in prefixes for s in suffixes], versioned
546581
else:
547-
return [":is_{}".format(p) for p in prefixes], default_version_settings
582+
return [":is_{}".format(p) for p in prefixes], setting_supported_versions
548583

549584
def _whl_config_setting_sufixes(
550585
platform_tag,
551586
glibc_versions,
552587
muslc_versions,
553588
osx_versions,
554-
default_version_settings):
589+
setting_supported_versions):
555590
suffixes = []
556591
for platform_tag in platform_tag.split("."):
557592
for p in whl_target_platforms(platform_tag):
@@ -573,20 +608,18 @@ def _whl_config_setting_sufixes(
573608
fail("Unsupported whl os: {}".format(p.os))
574609

575610
default_version_setting = "{}_{}".format(prefix, suffix)
576-
min_version = default_version_settings.get(default_version_setting)
611+
supported_versions = {}
577612
for v in versions:
578613
if v == (0, 0):
579614
suffixes.append(default_version_setting)
580615
elif v >= p.version:
581-
if not min_version or min_version > v:
582-
min_version = v
583-
suffixes.append("{}_{}_{}_{}".format(
616+
supported_versions[v] = "{}_{}_{}_{}".format(
584617
prefix,
585618
v[0],
586619
v[1],
587620
suffix,
588-
))
589-
if min_version:
590-
default_version_settings[default_version_setting] = min_version
621+
)
622+
if supported_versions:
623+
setting_supported_versions[default_version_setting] = supported_versions
591624

592625
return suffixes

tests/pip_hub_repository/render_pkg_aliases/render_pkg_aliases_test.bzl

+64-25
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ def _test_config_settings(
495495
*,
496496
filename,
497497
want,
498-
want_default_versions = {},
498+
want_versions = {},
499499
target_platforms = [],
500500
glibc_versions = [],
501501
muslc_versions = [],
@@ -512,7 +512,7 @@ def _test_config_settings(
512512
python_default = python_default,
513513
)
514514
env.expect.that_collection(got).contains_exactly(want)
515-
env.expect.that_dict(got_default_version_settings).contains_exactly(want_default_versions)
515+
env.expect.that_dict(got_default_version_settings).contains_exactly(want_versions)
516516

517517
def _test_sdist(env):
518518
# Do the first test for multiple extensions
@@ -628,15 +628,16 @@ def _test_py3_none_macosx_10_9_universal2(env):
628628
(10, 9),
629629
(11, 0),
630630
],
631-
want = [
632-
":is_py3_none_osx_11_0_aarch64_universal2",
633-
":is_py3_none_osx_11_0_x86_64_universal2",
634-
":is_py3_none_osx_10_9_aarch64_universal2",
635-
":is_py3_none_osx_10_9_x86_64_universal2",
636-
],
637-
want_default_versions = {
638-
":is_py3_none_osx_aarch64_universal2": (10, 9),
639-
":is_py3_none_osx_x86_64_universal2": (10, 9),
631+
want = [],
632+
want_versions = {
633+
":is_py3_none_osx_aarch64_universal2": {
634+
(10, 9): ":is_py3_none_osx_10_9_aarch64_universal2",
635+
(11, 0): ":is_py3_none_osx_11_0_aarch64_universal2",
636+
},
637+
":is_py3_none_osx_x86_64_universal2": {
638+
(10, 9): ":is_py3_none_osx_10_9_x86_64_universal2",
639+
(11, 0): ":is_py3_none_osx_11_0_x86_64_universal2",
640+
},
640641
},
641642
)
642643

@@ -685,12 +686,12 @@ def _test_cp37_abi3_manylinux_2_17_x86_64(env):
685686
(2, 17),
686687
(2, 18),
687688
],
688-
want = [
689-
":is_cp_abi3_manylinux_2_17_x86_64",
690-
":is_cp_abi3_manylinux_2_18_x86_64",
691-
],
692-
want_default_versions = {
693-
":is_cp_abi3_manylinux_x86_64": (2, 17),
689+
want = [],
690+
want_versions = {
691+
":is_cp_abi3_manylinux_x86_64": {
692+
(2, 17): ":is_cp_abi3_manylinux_2_17_x86_64",
693+
(2, 18): ":is_cp_abi3_manylinux_2_18_x86_64",
694+
},
694695
},
695696
)
696697

@@ -709,14 +710,15 @@ def _test_cp37_abi3_manylinux_2_17_musllinux_1_1_aarch64(env):
709710
muslc_versions = [
710711
(1, 1),
711712
],
712-
want = [
713-
":is_cp_cp_manylinux_2_17_aarch64",
714-
":is_cp_cp_manylinux_2_18_aarch64",
715-
":is_cp_cp_musllinux_1_1_aarch64",
716-
],
717-
want_default_versions = {
718-
":is_cp_cp_manylinux_aarch64": (2, 17),
719-
":is_cp_cp_musllinux_aarch64": (1, 1),
713+
want = [],
714+
want_versions = {
715+
":is_cp_cp_manylinux_aarch64": {
716+
(2, 17): ":is_cp_cp_manylinux_2_17_aarch64",
717+
(2, 18): ":is_cp_cp_manylinux_2_18_aarch64",
718+
},
719+
":is_cp_cp_musllinux_aarch64": {
720+
(1, 1): ":is_cp_cp_musllinux_1_1_aarch64",
721+
},
720722
},
721723
)
722724

@@ -783,6 +785,43 @@ def _test_multiplatform_whl_aliases_filename(env):
783785

784786
_tests.append(_test_multiplatform_whl_aliases_filename)
785787

788+
def _test_multiplatform_whl_aliases_filename_versioned(env):
789+
aliases = [
790+
whl_alias(
791+
repo = "glibc-2.17",
792+
filename = "foo-0.0.1-py3-none-manylinux_2_17_x86_64.whl",
793+
version = "3.1",
794+
),
795+
whl_alias(
796+
repo = "glibc-2.18",
797+
filename = "foo-0.0.1-py3-none-manylinux_2_18_x86_64.whl",
798+
version = "3.1",
799+
),
800+
whl_alias(
801+
repo = "musl",
802+
filename = "foo-0.0.1-py3-none-musllinux_1_1_x86_64.whl",
803+
version = "3.1",
804+
),
805+
]
806+
got = multiplatform_whl_aliases(
807+
aliases = aliases,
808+
default_version = None,
809+
glibc_versions = [(2, 17), (2, 18)],
810+
muslc_versions = [(1, 1), (1, 2)],
811+
osx_versions = [],
812+
)
813+
want = [
814+
whl_alias(config_setting = "//_config:is_cp3.1_py3_none_manylinux_2_17_x86_64", repo = "glibc-2.17", version = "3.1"),
815+
whl_alias(config_setting = "//_config:is_cp3.1_py3_none_manylinux_2_18_x86_64", repo = "glibc-2.18", version = "3.1"),
816+
whl_alias(config_setting = "//_config:is_cp3.1_py3_none_manylinux_x86_64", repo = "glibc-2.17", version = "3.1"),
817+
whl_alias(config_setting = "//_config:is_cp3.1_py3_none_musllinux_1_1_x86_64", repo = "musl", version = "3.1"),
818+
whl_alias(config_setting = "//_config:is_cp3.1_py3_none_musllinux_1_2_x86_64", repo = "musl", version = "3.1"),
819+
whl_alias(config_setting = "//_config:is_cp3.1_py3_none_musllinux_x86_64", repo = "musl", version = "3.1"),
820+
]
821+
env.expect.that_collection(got).contains_exactly(want)
822+
823+
_tests.append(_test_multiplatform_whl_aliases_filename_versioned)
824+
786825
def render_pkg_aliases_test_suite(name):
787826
"""Create the test suite.
788827

0 commit comments

Comments
 (0)