Skip to content

Commit e0363cc

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 da3ed13 commit e0363cc

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
@@ -689,6 +690,44 @@ def _strings(value: Any, name: str) -> List[str]:
689690
return scheme(table, 'tool.meson-python')
690691

691692

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

@@ -1093,59 +1132,14 @@ def editable(self, directory: Path) -> pathlib.Path:
10931132
@contextlib.contextmanager
10941133
def _project(config_settings: Optional[Dict[Any, Any]]) -> Iterator[Project]:
10951134
"""Create the project given the given config settings."""
1096-
if config_settings is None:
1097-
config_settings = {}
1098-
1099-
# expand all string values to single element tuples and convert collections to tuple
1100-
config_settings = {
1101-
key: tuple(value) if isinstance(value, Collection) and not isinstance(value, str) else (value,)
1102-
for key, value in config_settings.items()
1103-
}
1104-
1105-
builddir_value = config_settings.get('builddir', {})
1106-
if len(builddir_value) > 0:
1107-
if len(builddir_value) != 1:
1108-
raise ConfigError('Only one value for configuration entry "builddir" can be specified')
1109-
builddir = builddir_value[0]
1110-
if not isinstance(builddir, str):
1111-
raise ConfigError(f'Configuration entry "builddir" should be a string not {type(builddir)}')
1112-
else:
1113-
builddir = None
1114-
1115-
def _validate_string_collection(key: str) -> None:
1116-
assert isinstance(config_settings, Mapping)
1117-
problematic_items: Sequence[Any] = list(filter(None, (
1118-
item if not isinstance(item, str) else None
1119-
for item in config_settings.get(key, ())
1120-
)))
1121-
if problematic_items:
1122-
s = ', '.join(f'"{item}" ({type(item)})' for item in problematic_items)
1123-
raise ConfigError(f'Configuration entries for "{key}" must be strings but contain: {s}')
1124-
1125-
meson_args_keys = _MESON_ARGS_KEYS
1126-
meson_args_cli_keys = tuple(f'{key}-args' for key in meson_args_keys)
1127-
1128-
for key in config_settings:
1129-
known_keys = ('builddir', 'editable-verbose', *meson_args_cli_keys)
1130-
if key not in known_keys:
1131-
import difflib
1132-
matches = difflib.get_close_matches(key, known_keys, n=3)
1133-
if len(matches):
1134-
alternatives = ' or '.join(f'"{match}"' for match in matches)
1135-
raise ConfigError(f'Unknown configuration entry "{key}". Did you mean {alternatives}?')
1136-
else:
1137-
raise ConfigError(f'Unknown configuration entry "{key}"')
11381135

1139-
for key in meson_args_cli_keys:
1140-
_validate_string_collection(key)
1136+
settings = _validate_config_settings(config_settings or {})
1137+
meson_args = {name: settings.get(f'{name}-args', []) for name in _MESON_ARGS_KEYS}
11411138

11421139
with Project.with_temp_working_dir(
1143-
build_dir=builddir,
1144-
meson_args=typing.cast(MesonArgs, {
1145-
key: config_settings.get(f'{key}-args', ())
1146-
for key in meson_args_keys
1147-
}),
1148-
editable_verbose=bool(config_settings.get('editable-verbose'))
1140+
build_dir=settings.get('builddir'),
1141+
meson_args=typing.cast(MesonArgs, meson_args),
1142+
editable_verbose=bool(settings.get('editable-verbose'))
11491143
) as project:
11501144
yield project
11511145

tests/test_pep517.py

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

6969

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

0 commit comments

Comments
 (0)