Skip to content

Commit fe7904b

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 fe7904b

File tree

2 files changed

+42
-52
lines changed

2 files changed

+42
-52
lines changed

mesonpy/__init__.py

Lines changed: 40 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,40 @@ 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+
**{f'{name}-args': _string_or_strings for name in _MESON_ARGS_KEYS},
711+
}
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+
692727
class Project():
693728
"""Meson project wrapper to generate Python artifacts."""
694729

@@ -1093,59 +1128,14 @@ def editable(self, directory: Path) -> pathlib.Path:
10931128
@contextlib.contextmanager
10941129
def _project(config_settings: Optional[Dict[Any, Any]]) -> Iterator[Project]:
10951130
"""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}"')
11381131

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

11421135
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'))
1136+
build_dir=settings.get('builddir'),
1137+
meson_args=typing.cast(MesonArgs, meson_args),
1138+
editable_verbose=bool(settings.get('editable-verbose'))
11491139
) as project:
11501140
yield project
11511141

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)