From 1b2f35fc2a28b397c08b1acabb67c8a8f55e84be Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 30 Jun 2026 10:30:35 -0700 Subject: [PATCH 1/2] optinterpreter: tighten up the typing by removing Any This reduces the amount of Any used, which allows mypy to provide better type checking. --- mesonbuild/optinterpreter.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py index e6f897b359e0..88b08655b599 100644 --- a/mesonbuild/optinterpreter.py +++ b/mesonbuild/optinterpreter.py @@ -17,19 +17,21 @@ if T.TYPE_CHECKING: from .interpreterbase import TYPE_var, TYPE_kwargs from .mesonlib import SubProject - from typing_extensions import TypedDict, Literal + from typing_extensions import TypeAlias, TypedDict, Literal, NotRequired from .options import OptionStore _DEPRECATED_ARGS = T.Union[bool, str, T.Dict[str, str], T.List[str]] + ParserFuncT: TypeAlias = T.Callable[[str, str, tuple[bool, _DEPRECATED_ARGS], TYPE_kwargs], options.AnyOptionType] + FuncOptionArgs = TypedDict('FuncOptionArgs', { 'type': str, 'description': str, 'yield': bool, - 'choices': T.Optional[T.List[str]], + 'choices': NotRequired[list[str] | None], 'value': object, - 'min': T.Optional[int], - 'max': T.Optional[int], + 'min': NotRequired[int | None], + 'max': NotRequired[int | None], 'deprecated': _DEPRECATED_ARGS, }) @@ -68,7 +70,7 @@ class OptionInterpreter: def __init__(self, optionstore: 'OptionStore', subproject: 'SubProject') -> None: self.options: options.MutableKeyedOptionDictType = {} self.subproject = subproject - self.option_types: T.Dict[str, T.Callable[..., options.AnyOptionType]] = { + self.option_types: dict[str, ParserFuncT] = { 'string': self.string_parser, 'boolean': self.boolean_parser, 'combo': self.combo_parser, @@ -199,8 +201,9 @@ def func_option(self, args: T.Tuple[str], kwargs: 'FuncOptionArgs') -> None: description = kwargs['description'] or opt_name # 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'}} + n_kwargs = T.cast( + 'TYPE_kwargs', + {k: v for k, v in kwargs.items() if k not in {'type', 'description', 'deprecated', 'yield'}}) opt = parser(opt_name, description, (kwargs['yield'], kwargs['deprecated']), n_kwargs) if key in self.options: @@ -237,7 +240,7 @@ def combo_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECA choices = kwargs['choices'] value = kwargs['value'] if value is None: - value = kwargs['choices'][0] + value = choices[0] return options.UserComboOption(name, description, value, *args, choices=choices) @typed_kwargs( From 8ea09d77c6ea63e9fca0ee802f694b75b8119c46 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 30 Jun 2026 11:36:02 -0700 Subject: [PATCH 2/2] optinterpreter: make the parser interfaces more like other interfaces By putting the name and description parameters in the args tuple, we end up with a signature of `(self, args: list[TYPE_var], TYPE_dict)`, this matches up quite closely with other interfaces, and will make the future work of having a single calling convention much simpler. --- mesonbuild/interpreterbase/decorators.py | 4 +-- mesonbuild/optinterpreter.py | 35 ++++++++++++++---------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py index bfd7cc24afcd..ffe058e8f907 100644 --- a/mesonbuild/interpreterbase/decorators.py +++ b/mesonbuild/interpreterbase/decorators.py @@ -59,7 +59,7 @@ def get_callee_args(wrapped_args: T.Tuple[ModuleObject, ModuleState, T.List[TYPE @T.overload -def get_callee_args(wrapped_args: T.Tuple[OptionInterpreter, str, str, T.List[TYPE_var], TYPE_kwargs]) -> CalleeArgs: ... +def get_callee_args(wrapped_args: T.Tuple[OptionInterpreter, T.List[TYPE_var], TYPE_kwargs]) -> CalleeArgs: ... def get_callee_args(wrapped_args: T.Union[ @@ -67,7 +67,7 @@ def get_callee_args(wrapped_args: T.Union[ T.Tuple[ObjectHolder, object], T.Tuple[InterpreterBase, FunctionNode, T.List[TYPE_var], TYPE_kwargs], T.Tuple[ModuleObject, ModuleState, T.List[TYPE_var], TYPE_kwargs], - T.Tuple[OptionInterpreter, str, str, T.List[TYPE_var], TYPE_kwargs], + T.Tuple[OptionInterpreter, T.List[TYPE_var], TYPE_kwargs], ]) -> CalleeArgs: if is_module(wrapped_args[0]): s = wrapped_args[1] diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py index 88b08655b599..c58cb2fff922 100644 --- a/mesonbuild/optinterpreter.py +++ b/mesonbuild/optinterpreter.py @@ -22,7 +22,7 @@ _DEPRECATED_ARGS = T.Union[bool, str, T.Dict[str, str], T.List[str]] - ParserFuncT: TypeAlias = T.Callable[[str, str, tuple[bool, _DEPRECATED_ARGS], TYPE_kwargs], options.AnyOptionType] + ParserFuncT: TypeAlias = T.Callable[[tuple[str, str, bool, _DEPRECATED_ARGS], TYPE_kwargs], options.AnyOptionType] FuncOptionArgs = TypedDict('FuncOptionArgs', { 'type': str, @@ -205,7 +205,7 @@ def func_option(self, args: T.Tuple[str], kwargs: 'FuncOptionArgs') -> None: 'TYPE_kwargs', {k: v for k, v in kwargs.items() if k not in {'type', 'description', 'deprecated', 'yield'}}) - opt = parser(opt_name, description, (kwargs['yield'], kwargs['deprecated']), n_kwargs) + opt = parser((opt_name, description, kwargs['yield'], kwargs['deprecated']), n_kwargs) if key in self.options: mlog.deprecation(f'Option {opt_name} already exists.') self.options[key] = opt @@ -214,8 +214,9 @@ def func_option(self, args: T.Tuple[str], kwargs: 'FuncOptionArgs') -> None: 'string option', KwargInfo('value', str, default=''), ) - def string_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: StringArgs) -> options.UserOption: - return options.UserStringOption(name, description, kwargs['value'], *args) + def string_parser(self, args: T.Tuple[str, str, bool, _DEPRECATED_ARGS], kwargs: StringArgs) -> options.UserOption: + name, description, yielding, deprecated = args + return options.UserStringOption(name, description, kwargs['value'], yielding, deprecated) @typed_kwargs( 'boolean option', @@ -227,8 +228,8 @@ def string_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPREC deprecated_values={str: ('1.1.0', 'use a boolean, not a string')}, ), ) - def boolean_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: BooleanArgs) -> options.UserOption: - yielding, deprecated = args + def boolean_parser(self, args: T.Tuple[str, str, bool, _DEPRECATED_ARGS], kwargs: BooleanArgs) -> options.UserOption: + name, description, yielding, deprecated = args return options.UserBooleanOption(name, description, kwargs['value'], yielding=yielding, deprecated=deprecated) @typed_kwargs( @@ -236,12 +237,13 @@ def boolean_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRE KwargInfo('value', (str, NoneType)), KwargInfo('choices', ContainerTypeInfo(list, str, allow_empty=False), required=True), ) - def combo_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: ComboArgs) -> options.UserOption: + def combo_parser(self, args: T.Tuple[str, str, bool, _DEPRECATED_ARGS], kwargs: ComboArgs) -> options.UserOption: choices = kwargs['choices'] value = kwargs['value'] if value is None: value = choices[0] - return options.UserComboOption(name, description, value, *args, choices=choices) + name, description, yielding, deprecated = args + return options.UserComboOption(name, description, value, yielding, deprecated, choices=choices) @typed_kwargs( 'integer option', @@ -255,16 +257,17 @@ def combo_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECA KwargInfo('min', (int, NoneType)), KwargInfo('max', (int, NoneType)), ) - def integer_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: IntegerArgs) -> options.UserOption: + def integer_parser(self, args: T.Tuple[str, str, bool, _DEPRECATED_ARGS], kwargs: IntegerArgs) -> options.UserOption: + name, description, yielding, deprecated = args return options.UserIntegerOption( - name, description, kwargs['value'], *args, min_value=kwargs['min'], max_value=kwargs['max']) + name, description, kwargs['value'], yielding, deprecated, min_value=kwargs['min'], max_value=kwargs['max']) @typed_kwargs( 'string array option', KwargInfo('value', (ContainerTypeInfo(list, str), str, NoneType)), KwargInfo('choices', ContainerTypeInfo(list, str), default=[]), ) - def string_array_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: StringArrayArgs) -> options.UserOption: + def string_array_parser(self, args: T.Tuple[str, str, bool, _DEPRECATED_ARGS], kwargs: StringArrayArgs) -> options.UserOption: choices = kwargs['choices'] value = kwargs['value'] if kwargs['value'] is not None else choices if isinstance(value, str): @@ -272,15 +275,17 @@ def string_array_parser(self, name: str, description: str, args: T.Tuple[bool, _ FeatureDeprecated('String value for array option', '1.3.0').use(self.subproject) else: raise mesonlib.MesonException('Value does not define an array: ' + value) + name, description, yielding, deprecated = args return options.UserStringArrayOption( name, description, value, choices=choices, - yielding=args[0], - deprecated=args[1]) + yielding=yielding, + deprecated=deprecated) @typed_kwargs( 'feature option', KwargInfo('value', str, default='auto', validator=in_set_validator({'auto', 'enabled', 'disabled'})), ) - def feature_parser(self, name: str, description: str, args: T.Tuple[bool, _DEPRECATED_ARGS], kwargs: FeatureArgs) -> options.UserOption: - return options.UserFeatureOption(name, description, kwargs['value'], *args) + def feature_parser(self, args: T.Tuple[str, str, bool, _DEPRECATED_ARGS], kwargs: FeatureArgs) -> options.UserOption: + name, description, yielding, deprecated = args + return options.UserFeatureOption(name, description, kwargs['value'], yielding, deprecated)