aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/optinterpreter.py
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/optinterpreter.py')
-rw-r--r--mesonbuild/optinterpreter.py195
1 files changed, 122 insertions, 73 deletions
diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py
index 00f63e1..de7ba04 100644
--- a/mesonbuild/optinterpreter.py
+++ b/mesonbuild/optinterpreter.py
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from __future__ import annotations
import re
import typing as T
@@ -19,11 +20,13 @@ from . import coredata
from . import mesonlib
from . import mparser
from . import mlog
-from .interpreterbase import FeatureNew, typed_pos_args, typed_kwargs, ContainerTypeInfo, KwargInfo, permittedKwargs
+from .interpreterbase import FeatureNew, typed_pos_args, typed_kwargs, ContainerTypeInfo, KwargInfo
+from .interpreter.type_checking import NoneType, in_set_validator
if T.TYPE_CHECKING:
from .interpreterbase import TYPE_var, TYPE_kwargs
from .interpreterbase import SubProject
- from typing_extensions import TypedDict
+ from typing_extensions import TypedDict, Literal
+
FuncOptionArgs = TypedDict('FuncOptionArgs', {
'type': str,
'description': str,
@@ -34,13 +37,29 @@ if T.TYPE_CHECKING:
'max': T.Optional[int],
'deprecated': T.Union[bool, str, T.Dict[str, str], T.List[str]],
})
- ParserArgs = TypedDict('ParserArgs', {
- 'yield': bool,
- 'choices': T.Optional[T.List[str]],
- 'value': object,
- 'min': T.Optional[int],
- 'max': T.Optional[int],
- })
+
+ class StringArgs(TypedDict):
+ value: str
+
+ class BooleanArgs(TypedDict):
+ value: bool
+
+ class ComboArgs(TypedDict):
+ value: str
+ choices: T.List[str]
+
+ class IntegerArgs(TypedDict):
+ value: int
+ min: T.Optional[int]
+ max: T.Optional[int]
+
+ class StringArrayArgs(TypedDict):
+ value: T.Optional[T.Union[str, T.List[str]]]
+ choices: T.List[str]
+
+ class FeatureArgs(TypedDict):
+ value: Literal['enabled', 'disabled', 'auto']
+ choices: T.List[str]
class OptionException(mesonlib.MesonException):
@@ -54,13 +73,14 @@ class OptionInterpreter:
def __init__(self, subproject: 'SubProject') -> None:
self.options: 'coredata.MutableKeyedOptionDictType' = {}
self.subproject = subproject
- self.option_types = {'string': self.string_parser,
- 'boolean': self.boolean_parser,
- 'combo': self.combo_parser,
- 'integer': self.integer_parser,
- 'array': self.string_array_parser,
- 'feature': self.feature_parser,
- }
+ self.option_types: T.Dict[str, T.Callable[..., coredata.UserOption]] = {
+ 'string': self.string_parser,
+ 'boolean': self.boolean_parser,
+ 'combo': self.combo_parser,
+ 'integer': self.integer_parser,
+ 'array': self.string_array_parser,
+ 'feature': self.feature_parser,
+ }
def process(self, option_file: str) -> None:
try:
@@ -145,17 +165,25 @@ class OptionInterpreter:
(posargs, kwargs) = self.reduce_arguments(node.args)
self.func_option(posargs, kwargs)
- @typed_kwargs('option',
- KwargInfo('type', str, required=True),
- KwargInfo('description', str, default=''),
- KwargInfo('yield', bool, default=coredata.DEFAULT_YIELDING, since='0.45.0'),
- KwargInfo('choices', (ContainerTypeInfo(list, str), type(None))),
- KwargInfo('value', object),
- KwargInfo('min', (int, type(None))),
- KwargInfo('max', (int, type(None))),
- KwargInfo('deprecated', (bool, str, ContainerTypeInfo(dict, str), ContainerTypeInfo(list, str)),
- default=False, since='0.60.0')
- )
+ @typed_kwargs(
+ 'option',
+ KwargInfo(
+ 'type',
+ str,
+ required=True,
+ validator=in_set_validator({'string', 'boolean', 'integer', 'combo', 'array', 'feature'})
+ ),
+ KwargInfo('description', str, default=''),
+ KwargInfo(
+ 'deprecated',
+ (bool, str, ContainerTypeInfo(dict, str), ContainerTypeInfo(list, str)),
+ default=False,
+ since='0.60.0',
+ feature_validator=lambda x: [FeatureNew('string value to "deprecated" keyword argument', '0.63.0')] if isinstance(x, str) else []
+ ),
+ KwargInfo('yield', bool, default=coredata.DEFAULT_YIELDING, since='0.45.0'),
+ allow_unknown=True,
+ )
@typed_pos_args('option', str)
def func_option(self, args: T.Tuple[str], kwargs: 'FuncOptionArgs') -> None:
opt_name = args[0]
@@ -166,60 +194,81 @@ class OptionInterpreter:
raise OptionException('Option name %s is reserved.' % opt_name)
opt_type = kwargs['type']
- parser = self.option_types.get(opt_type)
- if not parser:
- raise OptionException(f'Unknown type {opt_type}.')
+ parser = self.option_types[opt_type]
description = kwargs['description'] or opt_name
- # Only keep in kwargs arguments that are used by option type's parser
- # because they use @permittedKwargs().
- known_parser_kwargs = {'value', 'choices', 'yield', 'min', 'max'}
- parser_kwargs = {k: v for k, v in kwargs.items() if k in known_parser_kwargs and v is not None}
- opt = parser(description, T.cast('ParserArgs', parser_kwargs))
+ # Drop the arguments we've already consumed
+ n_kwargs = {k: v for k, v in kwargs.items()
+ if k not in {'type', 'description', 'deprecated', 'yield'}}
+
+ opt = parser(description, kwargs['yield'], n_kwargs)
opt.deprecated = kwargs['deprecated']
- if isinstance(opt.deprecated, str):
- FeatureNew.single_use('String value to "deprecated" keyword argument', '0.63.0', self.subproject)
if key in self.options:
mlog.deprecation(f'Option {opt_name} already exists.')
self.options[key] = opt
- @permittedKwargs({'value', 'yield'})
- def string_parser(self, description: str, kwargs: 'ParserArgs') -> coredata.UserOption:
- value = kwargs.get('value', '')
- return coredata.UserStringOption(description, value, kwargs['yield'])
-
- @permittedKwargs({'value', 'yield'})
- def boolean_parser(self, description: str, kwargs: 'ParserArgs') -> coredata.UserOption:
- value = kwargs.get('value', True)
- return coredata.UserBooleanOption(description, value, kwargs['yield'])
-
- @permittedKwargs({'value', 'yield', 'choices'})
- def combo_parser(self, description: str, kwargs: 'ParserArgs') -> coredata.UserOption:
- choices = kwargs.get('choices')
- if not choices:
- raise OptionException('Combo option missing "choices" keyword.')
- value = kwargs.get('value', choices[0])
- return coredata.UserComboOption(description, choices, value, kwargs['yield'])
-
- @permittedKwargs({'value', 'min', 'max', 'yield'})
- def integer_parser(self, description: str, kwargs: 'ParserArgs') -> coredata.UserOption:
- value = kwargs.get('value')
+ @typed_kwargs(
+ 'string option',
+ KwargInfo('value', str, default=''),
+ )
+ def string_parser(self, description: str, yield_: bool, kwargs: StringArgs) -> coredata.UserOption:
+ return coredata.UserStringOption(description, kwargs['value'], yield_)
+
+ @typed_kwargs(
+ 'boolean option',
+ KwargInfo(
+ 'value',
+ (bool, str),
+ default=True,
+ validator=lambda x: None if isinstance(x, bool) or x in {'true', 'false'} else 'boolean options must have boolean values',
+ ),
+ )
+ def boolean_parser(self, description: str, yield_: bool, kwargs: BooleanArgs) -> coredata.UserOption:
+ return coredata.UserBooleanOption(description, kwargs['value'], yield_)
+
+ @typed_kwargs(
+ 'combo option',
+ KwargInfo('value', (str, NoneType)),
+ KwargInfo('choices', ContainerTypeInfo(list, str, allow_empty=False), required=True),
+ )
+ def combo_parser(self, description: str, kwargs: ComboArgs, yield_: bool) -> coredata.UserOption:
+ choices = kwargs['choices']
+ value = kwargs['value']
if value is None:
- raise OptionException('Integer option must contain value argument.')
- inttuple = (kwargs.get('min'), kwargs.get('max'), value)
- return coredata.UserIntegerOption(description, inttuple, kwargs['yield'])
-
- @permittedKwargs({'value', 'yield', 'choices'})
- def string_array_parser(self, description: str, kwargs: 'ParserArgs') -> coredata.UserOption:
- choices = kwargs.get('choices', [])
- value = kwargs.get('value', choices)
- if not isinstance(value, list):
- raise OptionException('Array choices must be passed as an array.')
+ value = kwargs['choices'][0]
+ return coredata.UserComboOption(description, choices, value, yield_)
+
+ @typed_kwargs(
+ 'integer option',
+ KwargInfo(
+ 'value',
+ (int, str),
+ default=True,
+ convertor=int,
+ ),
+ KwargInfo('min', (int, NoneType)),
+ KwargInfo('max', (int, NoneType)),
+ )
+ def integer_parser(self, description: str, yield_: bool, kwargs: IntegerArgs) -> coredata.UserOption:
+ value = kwargs['value']
+ inttuple = (kwargs['min'], kwargs['max'], value)
+ return coredata.UserIntegerOption(description, inttuple, yield_)
+
+ @typed_kwargs(
+ 'string array option',
+ KwargInfo('value', (ContainerTypeInfo(list, str), str, NoneType)),
+ KwargInfo('choices', ContainerTypeInfo(list, str), default=[]),
+ )
+ def string_array_parser(self, description: str, yield_: bool, kwargs: StringArrayArgs) -> coredata.UserOption:
+ choices = kwargs['choices']
+ value = kwargs['value'] if kwargs['value'] is not None else choices
return coredata.UserArrayOption(description, value,
choices=choices,
- yielding=kwargs['yield'])
+ yielding=yield_)
- @permittedKwargs({'value', 'yield'})
- def feature_parser(self, description: str, kwargs: 'ParserArgs') -> coredata.UserOption:
- value = kwargs.get('value', 'auto')
- return coredata.UserFeatureOption(description, value, kwargs['yield'])
+ @typed_kwargs(
+ 'feature option',
+ KwargInfo('value', str, default='auto', validator=in_set_validator({'auto', 'enabled', 'disabled'})),
+ )
+ def feature_parser(self, description: str, yield_: bool, kwargs: FeatureArgs) -> coredata.UserOption:
+ return coredata.UserFeatureOption(description, kwargs['value'], yield_)