fix: make CIBW_ARCHS parsing case-insensitive and platform-aware (#2920)
Architecture name lookup in `parse_config` was a case-sensitive StrEnum
lookup. Since the enum carries both lowercase `arm64` (macOS/Android) and
uppercase `ARM64` (Windows), `parse_config("arm64", "windows")` silently
returned the macOS member and later failed with a confusing "Invalid archs
option" error, while lowercase `amd64` failed to parse at all.
Resolve names case-insensitively, preferring a member valid for the target
platform before falling back to any case-insensitive match (which keeps the
clear ConfigurationError for genuinely invalid names).
Closes #2373
Assisted-by: ClaudeCode:claude-opus-4.8
This commit is contained in:
@@ -107,13 +107,29 @@ class Architecture(StrEnum):
|
|||||||
case "auto32":
|
case "auto32":
|
||||||
result |= cls.bitness_archs(platform=platform, bitness="32")
|
result |= cls.bitness_archs(platform=platform, bitness="32")
|
||||||
case _:
|
case _:
|
||||||
try:
|
if arch := cls._parse_arch_name(arch_str, platform=platform):
|
||||||
result.add(cls(arch_str))
|
result.add(arch)
|
||||||
except ValueError as e:
|
else:
|
||||||
msg = f"Invalid architecture '{arch_str}'"
|
msg = f"Invalid architecture '{arch_str}'"
|
||||||
raise errors.ConfigurationError(msg) from e
|
raise errors.ConfigurationError(msg)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _parse_arch_name(cls, arch_str: str, platform: PlatformName) -> Self | None:
|
||||||
|
"""Resolve an architecture name case-insensitively.
|
||||||
|
|
||||||
|
The same value (e.g. "arm64"/"ARM64") can map to different members
|
||||||
|
depending on the platform, so prefer a member valid for ``platform``
|
||||||
|
before falling back to any case-insensitive match.
|
||||||
|
"""
|
||||||
|
for arch in cls.all_archs(platform):
|
||||||
|
if arch.value.lower() == arch_str.lower():
|
||||||
|
return arch
|
||||||
|
for arch in cls:
|
||||||
|
if arch.value.lower() == arch_str.lower():
|
||||||
|
return arch
|
||||||
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def native_arch(cls, platform: PlatformName) -> Self | None:
|
def native_arch(cls, platform: PlatformName) -> Self | None:
|
||||||
native_machine = platform_module.machine()
|
native_machine = platform_module.machine()
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import sys
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import cibuildwheel.architecture
|
import cibuildwheel.architecture
|
||||||
|
import cibuildwheel.errors
|
||||||
from cibuildwheel.architecture import Architecture, arch_synonym
|
from cibuildwheel.architecture import Architecture, arch_synonym
|
||||||
|
|
||||||
TYPE_CHECKING = False
|
TYPE_CHECKING = False
|
||||||
@@ -142,3 +143,33 @@ def test_arch_synonym(
|
|||||||
arch: str, from_platform: PlatformName, to_platform: PlatformName, expected: str | None
|
arch: str, from_platform: PlatformName, to_platform: PlatformName, expected: str | None
|
||||||
) -> None:
|
) -> None:
|
||||||
assert arch_synonym(arch, from_platform, to_platform) == expected
|
assert arch_synonym(arch, from_platform, to_platform) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("config", "platform", "expected"),
|
||||||
|
[
|
||||||
|
# Case-insensitive, platform-resolved names (issue #2373).
|
||||||
|
("arm64", "windows", {Architecture.ARM64}),
|
||||||
|
("ARM64", "windows", {Architecture.ARM64}),
|
||||||
|
("amd64", "windows", {Architecture.AMD64}),
|
||||||
|
("AMD64", "windows", {Architecture.AMD64}),
|
||||||
|
("X86", "windows", {Architecture.x86}),
|
||||||
|
("x86", "windows", {Architecture.x86}),
|
||||||
|
# macOS resolves the lowercase arm64 even when given uppercase.
|
||||||
|
("ARM64", "macos", {Architecture.arm64}),
|
||||||
|
("arm64", "macos", {Architecture.arm64}),
|
||||||
|
("X86_64", "macos", {Architecture.x86_64}),
|
||||||
|
# Multiple, mixed-case archs in one config string.
|
||||||
|
("arm64 amd64", "windows", {Architecture.ARM64, Architecture.AMD64}),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_arch_parse_config_case_insensitive(
|
||||||
|
config: str, platform: PlatformName, expected: set[Architecture]
|
||||||
|
) -> None:
|
||||||
|
assert Architecture.parse_config(config, platform) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("platform", ["windows", "macos", "linux"])
|
||||||
|
def test_arch_parse_config_invalid(platform: PlatformName) -> None:
|
||||||
|
with pytest.raises(cibuildwheel.errors.ConfigurationError):
|
||||||
|
Architecture.parse_config("nonexistent", platform)
|
||||||
|
|||||||
Reference in New Issue
Block a user