fix(schema): default is fine for pyodide too (#2951)

This commit is contained in:
Henry Schreiner
2026-08-02 07:49:12 -04:00
committed by GitHub
parent dca67ee350
commit ffd2e18240
4 changed files with 185 additions and 52 deletions
+1
View File
@@ -122,3 +122,4 @@ site/
# This file should be a symlink or contain "See @AGENTS.md" # This file should be a symlink or contain "See @AGENTS.md"
CLAUDE.md CLAUDE.md
.claude/
+52 -41
View File
@@ -62,29 +62,7 @@ properties:
default: ['*'] default: ['*']
description: Choose the Python versions to build. description: Choose the Python versions to build.
type: string_array type: string_array
build-frontend: build-frontend: {} # filled in by build_frontend_schema below
default: default
description: Set the tool to use to build, either "build" (default), "build[uv]", "uv", or "pip"
oneOf:
- enum: [pip, build, "build[uv]", uv, default]
- type: string
pattern: '^pip; ?args:'
- type: string
pattern: '^build; ?args:'
- type: string
pattern: '^build\\[uv\\]; ?args:'
- type: string
pattern: '^uv; ?args:'
- type: object
additionalProperties: false
required: [name]
properties:
name:
enum: [pip, build, "build[uv]", uv]
args:
type: array
items:
type: string
build-verbosity: build-verbosity:
type: integer type: integer
minimum: -3 minimum: -3
@@ -292,6 +270,43 @@ string_table = yaml.safe_load(
""" """
) )
FRONTENDS = ["pip", "build", "build[uv]", "uv"]
def build_frontend_schema(
names: list[str], description: str, default: str = "default"
) -> dict[str, Any]:
"""
A frontend is a name, a "name; args: ..." string, or a table with a name and args.
"""
# Only the brackets in "build[uv]" need escaping
patterns = [name.replace("[", r"\[").replace("]", r"\]") for name in names]
return {
"default": default,
"description": description,
"oneOf": [
{"enum": [*names, "default"]},
*({"type": "string", "pattern": f"^{pattern}; ?args:"} for pattern in patterns),
{
"type": "object",
"additionalProperties": False,
"required": ["name"],
"properties": {
"name": {"enum": names},
"args": {"type": "array", "items": {"type": "string"}},
},
},
],
"title": "CIBW_BUILD_FRONTEND",
}
schema["properties"]["build-frontend"] = build_frontend_schema(
[*FRONTENDS, "pyodide-build"],
'Set the tool to use to build, either "build" (default), "build[uv]", "uv", or "pip"'
' ("pyodide-build" for pyodide)',
)
for value in schema["properties"].values(): for value in schema["properties"].values():
match value: match value:
case {"type": "string_array"}: case {"type": "string_array"}:
@@ -393,24 +408,20 @@ for os_name, command in [
del oses["linux"]["properties"]["dependency-versions"] del oses["linux"]["properties"]["dependency-versions"]
oses["pyodide"]["properties"]["build-frontend"] = {
**schema["properties"]["build-frontend"], schema["$defs"]["build-frontend-no-pyodide"] = build_frontend_schema(
"default": "pyodide-build", FRONTENDS,
"description": 'On the pyodide platform, the build frontend must be "pyodide-build"', 'Set the tool to use to build, either "build" (default), "build[uv]", "uv", or "pip"',
"oneOf": [ )
{"enum": ["pyodide-build"]},
{"type": "string", "pattern": "^pyodide-build; ?args:"}, for os_val in oses.values():
{ os_val["properties"]["build-frontend"] = {"$ref": "#/$defs/build-frontend-no-pyodide"}
"type": "object",
"additionalProperties": False, oses["pyodide"]["properties"]["build-frontend"] = build_frontend_schema(
"required": ["name"], ["pyodide-build"],
"properties": { 'On the pyodide platform, the build frontend must be "pyodide-build"',
"name": {"enum": ["pyodide-build"]}, default="pyodide-build",
"args": {"type": "array", "items": {"type": "string"}}, )
},
},
],
}
schema["properties"]["overrides"] = overrides schema["properties"]["overrides"] = overrides
schema["properties"] |= oses schema["properties"] |= oses
@@ -18,9 +18,64 @@
"pyodide-prerelease", "pyodide-prerelease",
"pypy", "pypy",
"pypy-eol" "pypy-eol"
],
"description": "A Python version or flavor to enable."
},
"build-frontend-no-pyodide": {
"default": "default",
"description": "Set the tool to use to build, either \"build\" (default), \"build[uv]\", \"uv\", or \"pip\"",
"oneOf": [
{
"enum": [
"pip",
"build",
"build[uv]",
"uv",
"default"
] ]
}, },
"description": "A Python version or flavor to enable." {
"type": "string",
"pattern": "^pip; ?args:"
},
{
"type": "string",
"pattern": "^build; ?args:"
},
{
"type": "string",
"pattern": "^build\\[uv\\]; ?args:"
},
{
"type": "string",
"pattern": "^uv; ?args:"
},
{
"type": "object",
"additionalProperties": false,
"required": [
"name"
],
"properties": {
"name": {
"enum": [
"pip",
"build",
"build[uv]",
"uv"
]
},
"args": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
],
"title": "CIBW_BUILD_FRONTEND"
}
}, },
"additionalProperties": false, "additionalProperties": false,
"description": "cibuildwheel's settings.", "description": "cibuildwheel's settings.",
@@ -136,7 +191,7 @@
}, },
"build-frontend": { "build-frontend": {
"default": "default", "default": "default",
"description": "Set the tool to use to build, either \"build\" (default), \"build[uv]\", \"uv\", or \"pip\"", "description": "Set the tool to use to build, either \"build\" (default), \"build[uv]\", \"uv\", or \"pip\" (\"pyodide-build\" for pyodide)",
"oneOf": [ "oneOf": [
{ {
"enum": [ "enum": [
@@ -144,6 +199,7 @@
"build", "build",
"build[uv]", "build[uv]",
"uv", "uv",
"pyodide-build",
"default" "default"
] ]
}, },
@@ -163,6 +219,10 @@
"type": "string", "type": "string",
"pattern": "^uv; ?args:" "pattern": "^uv; ?args:"
}, },
{
"type": "string",
"pattern": "^pyodide-build; ?args:"
},
{ {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
@@ -175,7 +235,8 @@
"pip", "pip",
"build", "build",
"build[uv]", "build[uv]",
"uv" "uv",
"pyodide-build"
] ]
}, },
"args": { "args": {
@@ -894,7 +955,7 @@
"$ref": "#/properties/before-test" "$ref": "#/properties/before-test"
}, },
"build-frontend": { "build-frontend": {
"$ref": "#/properties/build-frontend" "$ref": "#/$defs/build-frontend-no-pyodide"
}, },
"build-verbosity": { "build-verbosity": {
"$ref": "#/properties/build-verbosity" "$ref": "#/properties/build-verbosity"
@@ -1033,7 +1094,7 @@
"$ref": "#/properties/before-test" "$ref": "#/properties/before-test"
}, },
"build-frontend": { "build-frontend": {
"$ref": "#/properties/build-frontend" "$ref": "#/$defs/build-frontend-no-pyodide"
}, },
"build-verbosity": { "build-verbosity": {
"$ref": "#/properties/build-verbosity" "$ref": "#/properties/build-verbosity"
@@ -1118,7 +1179,7 @@
"$ref": "#/properties/before-test" "$ref": "#/properties/before-test"
}, },
"build-frontend": { "build-frontend": {
"$ref": "#/properties/build-frontend" "$ref": "#/$defs/build-frontend-no-pyodide"
}, },
"build-verbosity": { "build-verbosity": {
"$ref": "#/properties/build-verbosity" "$ref": "#/properties/build-verbosity"
@@ -1208,7 +1269,8 @@
"oneOf": [ "oneOf": [
{ {
"enum": [ "enum": [
"pyodide-build" "pyodide-build",
"default"
] ]
}, },
{ {
@@ -1308,7 +1370,7 @@
"$ref": "#/properties/before-test" "$ref": "#/properties/before-test"
}, },
"build-frontend": { "build-frontend": {
"$ref": "#/properties/build-frontend" "$ref": "#/$defs/build-frontend-no-pyodide"
}, },
"build-verbosity": { "build-verbosity": {
"$ref": "#/properties/build-verbosity" "$ref": "#/properties/build-verbosity"
@@ -1393,7 +1455,7 @@
"$ref": "#/properties/before-test" "$ref": "#/properties/before-test"
}, },
"build-frontend": { "build-frontend": {
"$ref": "#/properties/build-frontend" "$ref": "#/$defs/build-frontend-no-pyodide"
}, },
"build-verbosity": { "build-verbosity": {
"$ref": "#/properties/build-verbosity" "$ref": "#/properties/build-verbosity"
+59
View File
@@ -143,6 +143,65 @@ def test_overrides_invalid_inherit_value(validator: validate_pyproject.api.Valid
validator(example) validator(example)
def test_pyodide_build_frontend(validator: validate_pyproject.api.Validator) -> None:
"""
pyodide-build is accepted anywhere the frontend can be set, as an override
or a global value can apply to pyodide builds only.
"""
example = tomllib.loads(
"""
[tool.cibuildwheel]
build-frontend = "pyodide-build"
[tool.cibuildwheel.pyodide]
build-frontend = { name = "pyodide-build", args = ["--some-arg"] }
[[tool.cibuildwheel.overrides]]
select = "*pyodide*"
build-frontend = "pyodide-build; args: --some-arg"
"""
)
assert validator(example) is not None
@pytest.mark.parametrize("frontend", ['"uv"', '"build; args: --some-arg"', '{ name = "pip" }'])
def test_pyodide_bad_build_frontend(
validator: validate_pyproject.api.Validator, frontend: str
) -> None:
"""
The pyodide table only accepts the pyodide-build frontend.
"""
example = tomllib.loads(
f"""
[tool.cibuildwheel.pyodide]
build-frontend = {frontend}
"""
)
with pytest.raises(validate_pyproject.error_reporting.ValidationError):
validator(example)
@pytest.mark.parametrize("platform", ["linux", "macos", "windows", "ios", "android"])
@pytest.mark.parametrize("frontend", ["pyodide-build", "pyodide-build; args: --some-arg"])
def test_bad_pyodide_build_frontend(
validator: validate_pyproject.api.Validator, platform: str, frontend: str
) -> None:
"""
Only the pyodide table accepts the pyodide-build frontend.
"""
example = tomllib.loads(
f"""
[tool.cibuildwheel.{platform}]
build-frontend = "{frontend}"
"""
)
with pytest.raises(validate_pyproject.error_reporting.ValidationError):
validator(example)
def test_docs_examples(validator: validate_pyproject.api.Validator) -> None: def test_docs_examples(validator: validate_pyproject.api.Validator) -> None:
""" """
Parse out all the configuration examples, build valid TOML out of them, and Parse out all the configuration examples, build valid TOML out of them, and