From f327a00d46a6659d4fa71c36f557d59abbdca1a8 Mon Sep 17 00:00:00 2001 From: Brandon White Date: Thu, 1 Oct 2026 09:21:06 -0500 Subject: [PATCH 1/3] fix: include legacy manylinux aliases in --platform tags create_supported_manylinux_platforms normalized the --platform value through LEGACY_MANYLINUX_ALIASES, then emitted only PEP 600 spellings (manylinux_2_N_ARCH). Poetry's chooser matches wheel tags against env.supported_tags by exact string, so a wheel tagged only with a legacy alias never matched: duckdb-extension-httpfs 1.5.5, whose aarch64 wheel is tagged only manylinux2014_aarch64, could not be bundled for --platform manylinux_2_34_aarch64, nor even for --platform manylinux2014_aarch64. Emit each PEP 600 legacy alias (manylinux2014, manylinux2010, manylinux1) right after the tag for the same glibc version, so a wheel built for a newer glibc still ranks first, and at the same glibc version the alias ranks just below the PEP 600 spelling. ai-generated: true Co-Authored-By: Claude Opus 5.5 --- src/poetry_plugin_bundle/utils/platforms.py | 20 ++++++++--- tests/utils/test_platforms.py | 37 +++++++++++++++++++++ 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/poetry_plugin_bundle/utils/platforms.py b/src/poetry_plugin_bundle/utils/platforms.py index 0ee69ae..51d153c 100644 --- a/src/poetry_plugin_bundle/utils/platforms.py +++ b/src/poetry_plugin_bundle/utils/platforms.py @@ -97,16 +97,24 @@ def create_supported_manylinux_platforms(platform: str) -> list[str]: For now, only GLIBCMAJOR "2" is supported. It is unclear if there will be a need to support a future major version like "3" and if specified, how generate the compatible 2.x version tags. + + The legacy aliases (manylinux1, manylinux2010, manylinux2014) are included as well, since wheels may be + tagged with only the alias. See https://peps.python.org/pep-0600/#legacy-manylinux-tags """ # Implementation based on https://peps.python.org/pep-0600/#package-installers tag = normalize_legacy_manylinux_alias(platform) parsed = PlatformTagParseResult.parse(tag) - return [ - f"{parsed.platform}_{parsed.version_major}_{tag_minor}_{parsed.arch}" - for tag_minor in range(parsed.version_minor, -1, -1) - ] + platforms: list[str] = [] + for tag_minor in range(parsed.version_minor, -1, -1): + glibc_tag = f"{parsed.platform}_{parsed.version_major}_{tag_minor}" + platforms.append(f"{glibc_tag}_{parsed.arch}") + # A legacy alias ranks just below the tag for the same glibc version. + legacy_alias = LEGACY_MANYLINUX_ALIASES_BY_GLIBC_TAG.get(glibc_tag) + if legacy_alias: + platforms.append(f"{legacy_alias}_{parsed.arch}") + return platforms LEGACY_MANYLINUX_ALIASES = { @@ -115,6 +123,10 @@ def create_supported_manylinux_platforms(platform: str) -> list[str]: "manylinux2014": "manylinux_2_17", } +LEGACY_MANYLINUX_ALIASES_BY_GLIBC_TAG = { + glibc_tag: alias for alias, glibc_tag in LEGACY_MANYLINUX_ALIASES.items() +} + def normalize_legacy_manylinux_alias(tag: str) -> str: tag_os_index_end = tag.index("_") diff --git a/tests/utils/test_platforms.py b/tests/utils/test_platforms.py index bd2b67e..1b7b547 100644 --- a/tests/utils/test_platforms.py +++ b/tests/utils/test_platforms.py @@ -54,10 +54,12 @@ def test_create_supported_tags_legacy_manylinux_aliases() -> None: python_version_info=(3, 10, 2), expected_tags={ "cp310-cp310-manylinux_2_5_x86_64", + "cp310-cp310-manylinux1_x86_64", "cp310-cp310-manylinux_2_1_x86_64", }, unexpected_tags={ "cp310-cp310-manylinux_2_6_x86_64", + "cp310-cp310-manylinux2010_x86_64", }, ) @@ -66,10 +68,13 @@ def test_create_supported_tags_legacy_manylinux_aliases() -> None: python_version_info=(3, 10, 2), expected_tags={ "cp310-cp310-manylinux_2_12_x86_64", + "cp310-cp310-manylinux2010_x86_64", + "cp310-cp310-manylinux1_x86_64", "cp310-cp310-manylinux_2_1_x86_64", }, unexpected_tags={ "cp310-cp310-manylinux_2_13_x86_64", + "cp310-cp310-manylinux2014_x86_64", }, ) @@ -78,6 +83,9 @@ def test_create_supported_tags_legacy_manylinux_aliases() -> None: python_version_info=(3, 11, 9), expected_tags={ "cp311-cp311-manylinux_2_17_x86_64", + "cp311-cp311-manylinux2014_x86_64", + "cp311-cp311-manylinux2010_x86_64", + "cp311-cp311-manylinux1_x86_64", "cp311-cp311-manylinux_2_1_x86_64", }, unexpected_tags={ @@ -86,6 +94,35 @@ def test_create_supported_tags_legacy_manylinux_aliases() -> None: ) +def test_create_supported_tags_manylinux_includes_legacy_aliases() -> None: + _test_create_supported_tags( + platform="manylinux_2_34_aarch64", + python_version_info=(3, 14, 0), + expected_tags={ + "cp314-cp314-manylinux_2_34_aarch64", + "cp314-cp314-manylinux_2_17_aarch64", + "cp314-cp314-manylinux2014_aarch64", + "cp314-abi3-manylinux2014_aarch64", + "py3-none-manylinux2014_aarch64", + }, + unexpected_tags={ + "cp314-cp314-manylinux_2_35_aarch64", + }, + ) + + +def test_create_supported_tags_ranks_legacy_alias_with_its_glibc_version() -> None: + env = MockEnv(version_info=(3, 14, 0)) + tags = [ + str(tag) + for tag in platforms.create_supported_tags("manylinux_2_34_aarch64", env) + ] + + index = tags.index("cp314-cp314-manylinux2014_aarch64") + assert tags[index - 1] == "cp314-cp314-manylinux_2_17_aarch64" + assert tags[index + 1] == "cp314-cp314-manylinux_2_16_aarch64" + + def test_create_supported_tags_macosx() -> None: _test_create_supported_tags( platform="macosx_11_0_arm64", From 1bf76853bbf63781c0670898e2c09acb6812f8e8 Mon Sep 17 00:00:00 2001 From: Brandon White Date: Fri, 9 Oct 2026 16:37:35 -0500 Subject: [PATCH 2/3] test: bundle a wheel tagged only with legacy manylinux aliases Add a project_with_legacy_manylinux_wheel fixture that depends on cmake 3.18.4.post1, whose Linux wheels are tagged only manylinux1_x86_64, manylinux1_i686 and manylinux2014_aarch64, alongside an sdist. The new bundler test checks that Poetry's chooser picks those wheels for --platform manylinux_2_34_aarch64, manylinux2014_aarch64 and manylinux_2_28_x86_64. Without the alias fix it silently picks the sdist, which would be built on the host. Move the mocks from test_bundler_platform_override into a shared _mock_platform_installs helper; that test's assertions are unchanged. ai-generated: true Co-Authored-By: Claude Opus 5.5 --- tests/bundlers/test_venv_bundler.py | 66 +++++++++++++++++-- .../README.md | 0 .../poetry.lock | 28 ++++++++ .../__init__.py | 0 .../pyproject.toml | 15 +++++ 5 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 tests/fixtures/project_with_legacy_manylinux_wheel/README.md create mode 100644 tests/fixtures/project_with_legacy_manylinux_wheel/poetry.lock create mode 100644 tests/fixtures/project_with_legacy_manylinux_wheel/project_with_legacy_manylinux_wheel/__init__.py create mode 100644 tests/fixtures/project_with_legacy_manylinux_wheel/pyproject.toml diff --git a/tests/bundlers/test_venv_bundler.py b/tests/bundlers/test_venv_bundler.py index 4a0a992..2140afe 100644 --- a/tests/bundlers/test_venv_bundler.py +++ b/tests/bundlers/test_venv_bundler.py @@ -26,6 +26,8 @@ if TYPE_CHECKING: + from collections.abc import Callable + from poetry.config.config import Config from poetry.poetry import Poetry from pytest_mock import MockerFixture @@ -433,13 +435,12 @@ def test_bundler_non_package_mode( assert expected == io.fetch_output() -def test_bundler_platform_override( - io: BufferedIO, tmpdir: str, mocker: MockerFixture, config: Config -) -> None: - poetry = Factory().create_poetry( - Path(__file__).parent.parent / "fixtures" / "project_with_binary_wheel" - ) - poetry.set_config(config) +def _mock_platform_installs(mocker: MockerFixture) -> Callable[[], dict[str, str]]: + """ + Mock the installer's downloads and installs, serving each locked package's + files as its links. Returns a function mapping each package name to the + filename of the link most recently chosen for it. + """ def find_links_for_package_fake(package: Package) -> list[Link]: return [ @@ -467,6 +468,18 @@ def get_installed_links() -> dict[str, str]: for call in mock_download_link.call_args_list } + return get_installed_links + + +def test_bundler_platform_override( + io: BufferedIO, tmpdir: str, mocker: MockerFixture, config: Config +) -> None: + poetry = Factory().create_poetry( + Path(__file__).parent.parent / "fixtures" / "project_with_binary_wheel" + ) + poetry.set_config(config) + get_installed_links = _mock_platform_installs(mocker) + bundler = VenvBundler() bundler.set_path(Path(tmpdir)) bundler.set_remove(True) @@ -505,3 +518,42 @@ def get_installed_links() -> dict[str, str]: assert "musllinux_1_2_aarch64" in installed_link_by_package["cryptography"] assert "musllinux_1_2_aarch64" in installed_link_by_package["cffi"] assert "py3-none-any.whl" in installed_link_by_package["pycparser"] + + +def test_bundler_platform_override_selects_legacy_manylinux_wheel( + io: BufferedIO, tmpdir: str, mocker: MockerFixture, config: Config +) -> None: + # cmake 3.18.4.post1 tags its Linux wheels only with legacy manylinux aliases, + # alongside an sdist that would otherwise be chosen and built on the host. + poetry = Factory().create_poetry( + Path(__file__).parent.parent + / "fixtures" + / "project_with_legacy_manylinux_wheel" + ) + poetry.set_config(config) + get_installed_links = _mock_platform_installs(mocker) + + bundler = VenvBundler() + bundler.set_path(Path(tmpdir)) + bundler.set_remove(True) + + bundler.set_platform("manylinux_2_34_aarch64") + bundler.bundle(poetry, io) + assert ( + get_installed_links()["cmake"] + == "cmake-3.18.4.post1-py3-none-manylinux2014_aarch64.whl" + ) + + bundler.set_platform("manylinux2014_aarch64") + bundler.bundle(poetry, io) + assert ( + get_installed_links()["cmake"] + == "cmake-3.18.4.post1-py3-none-manylinux2014_aarch64.whl" + ) + + bundler.set_platform("manylinux_2_28_x86_64") + bundler.bundle(poetry, io) + assert ( + get_installed_links()["cmake"] + == "cmake-3.18.4.post1-py3-none-manylinux1_x86_64.whl" + ) diff --git a/tests/fixtures/project_with_legacy_manylinux_wheel/README.md b/tests/fixtures/project_with_legacy_manylinux_wheel/README.md new file mode 100644 index 0000000..e69de29 diff --git a/tests/fixtures/project_with_legacy_manylinux_wheel/poetry.lock b/tests/fixtures/project_with_legacy_manylinux_wheel/poetry.lock new file mode 100644 index 0000000..c87183c --- /dev/null +++ b/tests/fixtures/project_with_legacy_manylinux_wheel/poetry.lock @@ -0,0 +1,28 @@ +# This file is automatically @generated by Poetry 2.5.1 and should not be changed by hand. + +[[package]] +name = "cmake" +version = "3.18.4.post1" +description = "CMake is an open-source, cross-platform family of tools designed to build, test and package software" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "cmake-3.18.4.post1-py2-none-macosx_10_6_x86_64.whl", hash = "sha256:10c46b0fd2c087b0cae611d1e734f065a1a8169d0b54ec834a9dff005c1857ca"}, + {file = "cmake-3.18.4.post1-py2-none-manylinux1_i686.whl", hash = "sha256:65cd763dd232973a0deedf1f349e229fa3bf1357e0e2576da65ad118ff53b070"}, + {file = "cmake-3.18.4.post1-py2-none-manylinux1_x86_64.whl", hash = "sha256:1c900642859c5970d81ae8821ae05a2af93d2630cd1c0f2bffc80e7abdbc087d"}, + {file = "cmake-3.18.4.post1-py2-none-win32.whl", hash = "sha256:605c2a07c9ebf332319106bffb11941463d18e586902e3659c315cae9f0caaeb"}, + {file = "cmake-3.18.4.post1-py2-none-win_amd64.whl", hash = "sha256:c1b14b302d3def2672968cd675031793e193382d0e4a00e2121af4b333d62ece"}, + {file = "cmake-3.18.4.post1-py3-none-macosx_10_6_x86_64.whl", hash = "sha256:6dd3abb1afdd9a986a55977ef85a0d245ebf289cc704b687f061294c48c126ec"}, + {file = "cmake-3.18.4.post1-py3-none-manylinux1_i686.whl", hash = "sha256:1c86369700f74363ee46de64e4167ac2d292a7c7f1606e372b8dcaf3108d0cc7"}, + {file = "cmake-3.18.4.post1-py3-none-manylinux1_x86_64.whl", hash = "sha256:34f7ee67cef21b178a793fe760c979608d4ac66a1697cae6b382dbcc5d1ec485"}, + {file = "cmake-3.18.4.post1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:e8ef8dab578e8ca85724b8506f230a5a5017ead67cb9da60fe1240fc9ab24135"}, + {file = "cmake-3.18.4.post1-py3-none-win32.whl", hash = "sha256:5096f5d4541b5d0040bae9dbc364bb1c8cd9211e273c481baf9a1a3635be1d00"}, + {file = "cmake-3.18.4.post1-py3-none-win_amd64.whl", hash = "sha256:ac062ac13591e4acbb6e919e5b1196a3b04f8d1022eb3ab4dbd20779ade9d5ab"}, + {file = "cmake-3.18.4.post1.tar.gz", hash = "sha256:d7981ac85f1abb75c24eb14936d56dafbd327e7ba371d91007e38704af7b52b5"}, +] + +[metadata] +lock-version = "2.1" +python-versions = "^3.10" +content-hash = "da774ecf1351268cbdbb858033c53c2a38a579301558e1c015c2f2dd52198cae" diff --git a/tests/fixtures/project_with_legacy_manylinux_wheel/project_with_legacy_manylinux_wheel/__init__.py b/tests/fixtures/project_with_legacy_manylinux_wheel/project_with_legacy_manylinux_wheel/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/fixtures/project_with_legacy_manylinux_wheel/pyproject.toml b/tests/fixtures/project_with_legacy_manylinux_wheel/pyproject.toml new file mode 100644 index 0000000..bb5101e --- /dev/null +++ b/tests/fixtures/project_with_legacy_manylinux_wheel/pyproject.toml @@ -0,0 +1,15 @@ +[tool.poetry] +name = "project-with-legacy-manylinux-wheel" +version = "0.1.0" +description = "" +authors = ["BrandonLWhite "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.10" +cmake = "==3.18.4.post1" + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" From 0e6ad3f07a44922339ff0530dd7d806a42b3f306 Mon Sep 17 00:00:00 2001 From: Brandon White Date: Fri, 9 Oct 2026 16:37:35 -0500 Subject: [PATCH 3/3] docs: say which wheels a --platform value accepts The README did not say that a platform value also accepts wheels built for older versions of it, or, for manylinux, wheels tagged only with the legacy manylinux2014, manylinux2010 or manylinux1 aliases. ai-generated: true Co-Authored-By: Claude Opus 5.5 --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 14a7a38..4c8b11e 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,13 @@ This is not a comprehensive list, but illustrates typical examples. `macosx_10_9_x86_64`, `macosx_10_9_intel`, `macosx_11_1_universal2`, `macosx_11_0_arm64` +#### Compatible wheels +The specified platform is treated as the newest one the target system supports, so wheels built for older versions of +it are selected as well. For example, `manylinux_2_34_aarch64` also accepts wheels built for any older glibc version, +including wheels tagged only with one of the legacy aliases `manylinux2014`, `manylinux2010` or `manylinux1` (see +[PEP 600](https://peps.python.org/pep-0600/#legacy-manylinux-tags)). Among otherwise equivalent wheels, the one built +for the newest platform version is preferred. + #### Example use case for AWS Lambda As an example of one motivating use case for this option, consider the AWS Lambda "serverless" execution environment. Depending upon which Python version you configure for your runtime, you may get different versions of the Linux system