fix: add suggestions to the TOML error message (#1205)

* fix: add suggestions to the TOML error message

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Update cibuildwheel/options.py

Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>

Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
This commit is contained in:
Henry Schreiner
2022-08-02 14:43:07 -04:00
committed by GitHub
co-authored by Matthieu Darbois
parent 9d170ac0f4
commit c234ff5f36
2 changed files with 50 additions and 22 deletions
+30 -21
View File
@@ -1,5 +1,6 @@
from __future__ import annotations
import difflib
import functools
import os
import sys
@@ -188,14 +189,10 @@ class OptionsReader:
# Validate project config
for option_name in config_options:
if not self._is_valid_global_option(option_name):
raise ConfigOptionError(f'Option "{option_name}" not supported in a config file')
self._validate_global_option(option_name)
for option_name in config_platform_options:
if not self._is_valid_platform_option(option_name):
raise ConfigOptionError(
f'Option "{option_name}" not supported in the "{self.platform}" section'
)
self._validate_platform_option(option_name)
self.config_options = config_options
self.config_platform_options = config_platform_options
@@ -207,40 +204,51 @@ class OptionsReader:
if config_overrides is not None:
if not isinstance(config_overrides, list):
raise ConfigOptionError('"tool.cibuildwheel.overrides" must be a list')
raise ConfigOptionError("'tool.cibuildwheel.overrides' must be a list")
for config_override in config_overrides:
select = config_override.pop("select", None)
if not select:
raise ConfigOptionError('"select" must be set in an override')
raise ConfigOptionError("'select' must be set in an override")
if isinstance(select, list):
select = " ".join(select)
self.overrides.append(Override(select, config_override))
def _is_valid_global_option(self, name: str) -> bool:
def _validate_global_option(self, name: str) -> None:
"""
Returns True if an option with this name is allowed in the
Raises an error if an option with this name is not allowed in the
[tool.cibuildwheel] section of a config file.
"""
allowed_option_names = self.default_options.keys() | PLATFORMS | {"overrides"}
return name in allowed_option_names
if name not in allowed_option_names:
msg = f"Option {name!r} not supported in a config file."
matches = difflib.get_close_matches(name, allowed_option_names, 1, 0.7)
if matches:
msg += f" Perhaps you meant {matches[0]!r}?"
raise ConfigOptionError(msg)
def _is_valid_platform_option(self, name: str) -> bool:
def _validate_platform_option(self, name: str) -> None:
"""
Returns True if an option with this name is allowed in the
Raises an error if an option with this name is not allowed in the
[tool.cibuildwheel.<current-platform>] section of a config file.
"""
disallowed_platform_options = self.disallow.get(self.platform, set())
if name in disallowed_platform_options:
return False
msg = f"{name!r} is not allowed in {disallowed_platform_options}"
raise ConfigOptionError(msg)
allowed_option_names = self.default_options.keys() | self.default_platform_options.keys()
return name in allowed_option_names
if name not in allowed_option_names:
msg = f"Option {name!r} not supported in the {self.platform!r} section"
matches = difflib.get_close_matches(name, allowed_option_names, 1, 0.7)
if matches:
msg += f" Perhaps you meant {matches[0]!r}?"
raise ConfigOptionError(msg)
def _load_file(self, filename: Path) -> tuple[dict[str, Any], dict[str, Any]]:
"""
@@ -290,7 +298,8 @@ class OptionsReader:
"""
if name not in self.default_options and name not in self.default_platform_options:
raise ConfigOptionError(f"{name} must be in cibuildwheel/resources/defaults.toml file")
msg = f"{name!r} must be in cibuildwheel/resources/defaults.toml file to be accessed."
raise ConfigOptionError(msg)
# Environment variable form
envvar = f"CIBW_{name.upper().replace('-', '_')}"
@@ -314,12 +323,12 @@ class OptionsReader:
if isinstance(result, dict):
if table is None:
raise ConfigOptionError(f"{name} does not accept a table")
raise ConfigOptionError(f"{name!r} does not accept a table")
return table["sep"].join(table["item"].format(k=k, v=v) for k, v in result.items())
if isinstance(result, list):
if sep is None:
raise ConfigOptionError(f"{name} does not accept a list")
raise ConfigOptionError(f"{name!r} does not accept a list")
return sep.join(result)
if isinstance(result, int):
@@ -393,7 +402,7 @@ class Options:
container_engine_str = self.reader.get("container-engine")
if container_engine_str not in ["docker", "podman"]:
msg = f"cibuildwheel: Unrecognised container_engine '{container_engine_str}', only 'docker' and 'podman' are supported"
msg = f"cibuildwheel: Unrecognised container_engine {container_engine_str!r}, only 'docker' and 'podman' are supported"
print(msg, file=sys.stderr)
sys.exit(2)
@@ -437,7 +446,7 @@ class Options:
elif build_frontend_str == "pip":
build_frontend = "pip"
else:
msg = f"cibuildwheel: Unrecognised build frontend '{build_frontend_str}', only 'pip' and 'build' are supported"
msg = f"cibuildwheel: Unrecognised build frontend {build_frontend_str!r}, only 'pip' and 'build' are supported"
print(msg, file=sys.stderr)
sys.exit(2)
@@ -445,7 +454,7 @@ class Options:
environment = parse_environment(environment_config)
except (EnvironmentParseError, ValueError):
print(
f'cibuildwheel: Malformed environment option "{environment_config}"',
f"cibuildwheel: Malformed environment option {environment_config!r}",
file=sys.stderr,
)
traceback.print_exc(None, sys.stderr)
+20 -1
View File
@@ -160,9 +160,28 @@ repairs-wheel-command = "repair-project-linux"
"""
)
with pytest.raises(ConfigOptionError):
with pytest.raises(ConfigOptionError) as excinfo:
OptionsReader(pyproject_toml, platform="linux")
assert "repair-wheel-command" in str(excinfo.value)
def test_underscores_in_key(tmp_path):
# Note that platform contents are only checked when running
# for that platform.
pyproject_toml = tmp_path / "pyproject.toml"
pyproject_toml.write_text(
"""
[tool.cibuildwheel]
repair_wheel_command = "repair-project-linux"
"""
)
with pytest.raises(ConfigOptionError) as excinfo:
OptionsReader(pyproject_toml, platform="linux")
assert "repair-wheel-command" in str(excinfo.value)
def test_unexpected_table(tmp_path):
pyproject_toml = tmp_path / "pyproject.toml"