Skip to content

Commit 1947798

Browse files
committed
MAINT: consolidate validation of options from build frontend
PEP 517 specifies that the options are received as a dictionary with string keys and string values, thus there is no need to verify that.
1 parent cac7daa commit 1947798

File tree

2 files changed

+46
-52
lines changed

2 files changed

+46
-52
lines changed

mesonpy/__init__.py

Lines changed: 44 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import argparse
1515
import collections
1616
import contextlib
17+
import difflib
1718
import functools
1819
import importlib.machinery
1920
import io
@@ -685,6 +686,44 @@ def _strings(value: Any, name: str) -> List[str]:
685686
return scheme(table, 'tool.meson-python')
686687

687688

689+
def _validate_config_settings(config_settings: Dict[str, Any]) -> Dict[str, Any]:
690+
"""Validate options received from build frontend."""
691+
692+
def _string(value: Any, name: str) -> str:
693+
if not isinstance(value, str):
694+
raise ConfigError(f'only one value for "{name}" can be specified')
695+
return value
696+
697+
def _bool(value: Any, name: str) -> bool:
698+
return True
699+
700+
def _string_or_strings(value: Any, name: str) -> List[str]:
701+
return list([value,] if isinstance(value, str) else value)
702+
703+
options = {
704+
'builddir': _string,
705+
'editable-verbose': _bool,
706+
'dist-args': _string_or_strings,
707+
'setup-args': _string_or_strings,
708+
'compile-args': _string_or_strings,
709+
'install-args': _string_or_strings,
710+
}
711+
assert all(f'{name}-args' in options for name in _MESON_ARGS_KEYS)
712+
713+
config = {}
714+
for key, value in config_settings.items():
715+
parser = options.get(key)
716+
if parser is None:
717+
matches = difflib.get_close_matches(key, options.keys(), n=2)
718+
if matches:
719+
alternatives = ' or '.join(f'"{match}"' for match in matches)
720+
raise ConfigError(f'unknown option "{key}". did you mean {alternatives}?')
721+
else:
722+
raise ConfigError(f'unknown option "{key}"')
723+
config[key] = parser(value, key)
724+
return config
725+
726+
688727
class Project():
689728
"""Meson project wrapper to generate Python artifacts."""
690729

@@ -1056,59 +1095,14 @@ def editable(self, directory: Path) -> pathlib.Path:
10561095
@contextlib.contextmanager
10571096
def _project(config_settings: Optional[Dict[Any, Any]]) -> Iterator[Project]:
10581097
"""Create the project given the given config settings."""
1059-
if config_settings is None:
1060-
config_settings = {}
1061-
1062-
# expand all string values to single element tuples and convert collections to tuple
1063-
config_settings = {
1064-
key: tuple(value) if isinstance(value, Collection) and not isinstance(value, str) else (value,)
1065-
for key, value in config_settings.items()
1066-
}
1067-
1068-
builddir_value = config_settings.get('builddir', {})
1069-
if len(builddir_value) > 0:
1070-
if len(builddir_value) != 1:
1071-
raise ConfigError('Only one value for configuration entry "builddir" can be specified')
1072-
builddir = builddir_value[0]
1073-
if not isinstance(builddir, str):
1074-
raise ConfigError(f'Configuration entry "builddir" should be a string not {type(builddir)}')
1075-
else:
1076-
builddir = None
1077-
1078-
def _validate_string_collection(key: str) -> None:
1079-
assert isinstance(config_settings, Mapping)
1080-
problematic_items: Sequence[Any] = list(filter(None, (
1081-
item if not isinstance(item, str) else None
1082-
for item in config_settings.get(key, ())
1083-
)))
1084-
if problematic_items:
1085-
s = ', '.join(f'"{item}" ({type(item)})' for item in problematic_items)
1086-
raise ConfigError(f'Configuration entries for "{key}" must be strings but contain: {s}')
1087-
1088-
meson_args_keys = _MESON_ARGS_KEYS
1089-
meson_args_cli_keys = tuple(f'{key}-args' for key in meson_args_keys)
1090-
1091-
for key in config_settings:
1092-
known_keys = ('builddir', 'editable-verbose', *meson_args_cli_keys)
1093-
if key not in known_keys:
1094-
import difflib
1095-
matches = difflib.get_close_matches(key, known_keys, n=3)
1096-
if len(matches):
1097-
alternatives = ' or '.join(f'"{match}"' for match in matches)
1098-
raise ConfigError(f'Unknown configuration entry "{key}". Did you mean {alternatives}?')
1099-
else:
1100-
raise ConfigError(f'Unknown configuration entry "{key}"')
11011098

1102-
for key in meson_args_cli_keys:
1103-
_validate_string_collection(key)
1099+
settings = _validate_config_settings(config_settings or {})
1100+
meson_args = {name: settings.get(f'{name}-args', []) for name in _MESON_ARGS_KEYS}
11041101

11051102
with Project.with_temp_working_dir(
1106-
build_dir=builddir,
1107-
meson_args=typing.cast(MesonArgs, {
1108-
key: config_settings.get(f'{key}-args', ())
1109-
for key in meson_args_keys
1110-
}),
1111-
editable_verbose=bool(config_settings.get('editable-verbose'))
1103+
build_dir=settings.get('builddir'),
1104+
meson_args=typing.cast(MesonArgs, meson_args),
1105+
editable_verbose=bool(settings.get('editable-verbose'))
11121106
) as project:
11131107
yield project
11141108

tests/test_pep517.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def test_invalid_config_settings(capsys, package_pure, tmp_path_session):
6767
method(tmp_path_session, {'invalid': ()})
6868
out, err = capsys.readouterr()
6969
assert out.splitlines()[-1].endswith(
70-
'Unknown configuration entry "invalid"')
70+
'unknown option "invalid"')
7171

7272

7373
def test_invalid_config_settings_suggest(capsys, package_pure, tmp_path_session):
@@ -76,4 +76,4 @@ def test_invalid_config_settings_suggest(capsys, package_pure, tmp_path_session)
7676
method(tmp_path_session, {'setup_args': ()})
7777
out, err = capsys.readouterr()
7878
assert out.splitlines()[-1].endswith(
79-
'Unknown configuration entry "setup_args". Did you mean "setup-args" or "dist-args"?')
79+
'unknown option "setup_args". did you mean "setup-args" or "dist-args"?')

0 commit comments

Comments
 (0)