From aa895b383cc726800d7d6e7e540db96dfe9df1f2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 23 Aug 2021 14:07:57 -0700 Subject: [PATCH] interpreter: Add a helper for checking constrained inputs This is quite valuable for enum-like inputs, where only a certain set of values is allowed. --- mesonbuild/interpreter/compiler.py | 4 ++-- mesonbuild/interpreter/interpreter.py | 3 ++- mesonbuild/interpreter/type_checking.py | 11 +++++++++++ unittests/internaltests.py | 3 ++- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py index 7491fce4f..53c3ce0a2 100644 --- a/mesonbuild/interpreter/compiler.py +++ b/mesonbuild/interpreter/compiler.py @@ -17,7 +17,7 @@ from ..interpreterbase import (ObjectHolder, noPosargs, noKwargs, InterpreterException) from ..interpreterbase.decorators import ContainerTypeInfo, typed_kwargs, KwargInfo, typed_pos_args from .interpreterobjects import (extract_required_kwarg, extract_search_dirs) -from .type_checking import REQUIRED_KW +from .type_checking import REQUIRED_KW, in_set_validator if T.TYPE_CHECKING: from ..interpreter import Interpreter @@ -638,7 +638,7 @@ class CompilerHolder(ObjectHolder['Compiler']): @typed_kwargs( 'compiler.get_supported_arguments', KwargInfo('checked', str, default='off', since='0.59.0', - validator=lambda s: 'must be one of "warn", "require" or "off"' if s not in ['warn', 'require', 'off'] else None) + validator=in_set_validator({'warn', 'require', 'off'})), ) def get_supported_arguments_method(self, args: T.Tuple[T.List[str]], kwargs: 'GetSupportedArgumentKw') -> T.List[str]: supported_args: T.List[str] = [] diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index c716c8523..5b03e7eb0 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -55,6 +55,7 @@ from .type_checking import ( LANGUAGE_KW, NATIVE_KW, REQUIRED_KW, + in_set_validator, ) from pathlib import Path @@ -181,7 +182,7 @@ TEST_KWARGS: T.List[KwargInfo] = [ validator=lambda x: 'must be an absolute path' if not os.path.isabs(x) else None), KwargInfo('protocol', str, default='exitcode', - validator=lambda x: 'value must be one of "exitcode", "tap", "gtest", "rust"' if x not in {'exitcode', 'tap', 'gtest', 'rust'} else None, + validator=in_set_validator({'exitcode', 'tap', 'gtest', 'rust'}), since_values={'gtest': '0.55.0', 'rust': '0.57.0'}), KwargInfo('depends', ContainerTypeInfo(list, (build.CustomTarget, build.BuildTarget)), listify=True, default=[], since='0.46.0'), diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 6088eef9e..dd0411c5a 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -11,6 +11,17 @@ from ..interpreterbase.decorators import KwargInfo, ContainerTypeInfo from ..mesonlib import FileMode, MachineChoice +def in_set_validator(choices: T.Set[str]) -> T.Callable[[str], T.Optional[str]]: + """Check that the choice given was one of the given set.""" + + def inner(check: str) -> T.Optional[str]: + if check not in choices: + return f"must be one of {', '.join(sorted(choices))}, not {check}" + return None + + return inner + + def _language_validator(l: T.List[str]) -> T.Optional[str]: """Validate language keyword argument. diff --git a/unittests/internaltests.py b/unittests/internaltests.py index a4615d70c..daf3efd33 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -40,6 +40,7 @@ from mesonbuild.mesonlib import ( LibType, MachineChoice, PerMachine, Version, is_windows, is_osx, is_cygwin, is_openbsd, search_version, MesonException, OptionKey, ) +from mesonbuild.interpreter.type_checking import in_set_validator from mesonbuild.dependencies import PkgConfigDependency from mesonbuild.programs import ExternalProgram import mesonbuild.modules.pkgconfig @@ -1376,7 +1377,7 @@ class InternalTests(unittest.TestCase): KwargInfo('output', ContainerTypeInfo(dict, str), default={}, deprecated_values={'foo': '0.9'}, since_values={'bar': '1.1'}), KwargInfo( 'mode', str, - validator=lambda x: 'Should be one of "clean", "build", "rebuild"' if x not in {'clean', 'build', 'rebuild', 'deprecated', 'since'} else None, + validator=in_set_validator({'clean', 'build', 'rebuild', 'deprecated', 'since'}), deprecated_values={'deprecated': '1.0'}, since_values={'since': '1.1'}), )