Skip to content

Commit feccd5a

Browse files
committed
BUG: improve validation of pyproject.toml meson-python configuration
Switch from an incomplete (an bugged) ad hoc validation to a scheme based validation strategy. The scheme is defined in the function _validate_pyproject_config() as nested dictionaries where the keys are configuration field names and valued are validation functions. Unknown fields result in an error. Fixes #293.
1 parent 9155036 commit feccd5a

File tree

1 file changed

+34
-27
lines changed

1 file changed

+34
-27
lines changed

mesonpy/__init__.py

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,36 @@ def build_editable(self, directory: Path, verbose: bool = False) -> pathlib.Path
658658
return wheel_file
659659

660660

661+
def _table(scheme: Dict[str, Callable[[Any, str], Any]]) -> Callable[[Any, str], Dict[str, Any]]:
662+
def func(value: Any, name: str) -> Dict[str, Any]:
663+
if not isinstance(value, dict):
664+
raise ConfigError(f'configuration entry "{name}" must be a table')
665+
table = {}
666+
for key, val in value.items():
667+
check = scheme.get(key)
668+
if check is None:
669+
raise ConfigError(f'unknown configuration entry "{name}.{key}"')
670+
table[key] = check(val, f'{name}.{key}')
671+
return table
672+
return func
673+
674+
675+
def _strings(value: Any, name: str) -> List[str]:
676+
if not isinstance(value, list) or not all(isinstance(x, str) for x in value):
677+
raise ConfigError(f'configuration entry "{name}" must be a list of strings')
678+
return value
679+
680+
681+
def _validate_pyproject_config(pyproject: Dict[str, Any]) -> Dict[str, Any]:
682+
scheme = _table({
683+
'args': _table({
684+
name: _strings for name in _MESON_ARGS_KEYS
685+
})
686+
})
687+
table = pyproject.get('tool', {}).get('meson-python', {})
688+
return scheme(table, 'tool.meson-python')
689+
690+
661691
class Project():
662692
"""Meson project wrapper to generate Python artifacts."""
663693

@@ -730,14 +760,10 @@ def __init__( # noqa: C901
730760
if self._metadata:
731761
self._validate_metadata()
732762

733-
# load meson args
734-
for key in self._get_config_key('args'):
735-
self._meson_args[key].extend(self._get_config_key(f'args.{key}'))
736-
# XXX: We should validate the user args to make sure they don't conflict with ours.
737-
738-
self._check_for_unknown_config_keys({
739-
'args': _MESON_ARGS_KEYS,
740-
})
763+
# load meson args from pyproject.toml
764+
pyproject_config = _validate_pyproject_config(self._config)
765+
for key, value in pyproject_config.get('args', {}).items():
766+
self._meson_args[key].extend(value)
741767

742768
# meson arguments from the command line take precedence over
743769
# arguments from the configuration file thus are added later
@@ -778,14 +804,6 @@ def __init__( # noqa: C901
778804
if self._metadata and 'version' in self._metadata.dynamic:
779805
self._metadata.version = self.version
780806

781-
def _get_config_key(self, key: str) -> Any:
782-
value: Any = self._config
783-
for part in f'tool.meson-python.{key}'.split('.'):
784-
if not isinstance(value, Mapping):
785-
raise ConfigError(f'Configuration entry "tool.meson-python.{key}" should be a TOML table not {type(value)}')
786-
value = value.get(part, {})
787-
return value
788-
789807
def _proc(self, *args: str) -> None:
790808
"""Invoke a subprocess."""
791809
print('{cyan}{bold}+ {}{reset}'.format(' '.join(args), **_STYLES))
@@ -858,17 +876,6 @@ def _validate_metadata(self) -> None:
858876
f'expected {self._metadata.requires_python}'
859877
)
860878

861-
def _check_for_unknown_config_keys(self, valid_args: Mapping[str, Collection[str]]) -> None:
862-
config = self._config.get('tool', {}).get('meson-python', {})
863-
864-
for key, valid_subkeys in config.items():
865-
if key not in valid_args:
866-
raise ConfigError(f'Unknown configuration key "tool.meson-python.{key}"')
867-
868-
for subkey in valid_args[key]:
869-
if subkey not in valid_subkeys:
870-
raise ConfigError(f'Unknown configuration key "tool.meson-python.{key}.{subkey}"')
871-
872879
@cached_property
873880
def _wheel_builder(self) -> _WheelBuilder:
874881
return _WheelBuilder(

0 commit comments

Comments
 (0)