From 8f55a24cfb1fb02e428fcf3a1952fe80f4978fca Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 5 May 2025 13:10:13 +0200 Subject: options: support non-string data types in more places Allow OptionStringLikeDict to use non-string data types, and use it as much as possible instead of string-valued dictionaries. Signed-off-by: Paolo Bonzini --- mesonbuild/ast/introspection.py | 5 +++-- mesonbuild/interpreter/interpreter.py | 3 ++- mesonbuild/options.py | 38 +++++++++++++++++------------------ 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index a270dfec2..bb1e241e5 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -22,6 +22,7 @@ from .interpreter import AstInterpreter if T.TYPE_CHECKING: from ..build import BuildTarget from ..interpreterbase import TYPE_var + from ..options import OptionDict from .visitor import AstVisitor @@ -130,14 +131,14 @@ class IntrospectionInterpreter(AstInterpreter): if self.environment.first_invocation or (self.subproject != '' and self.subproject not in self.coredata.initialized_subprojects): if self.subproject == '': self.coredata.optstore.initialize_from_top_level_project_call( - T.cast('T.Dict[T.Union[OptionKey, str], str]', self.project_default_options), + T.cast('OptionDict', self.project_default_options), {}, # TODO: not handled by this Interpreter. self.environment.options) else: self.coredata.optstore.initialize_from_subproject_call( self.subproject, {}, # TODO: this isn't handled by the introspection interpreter... - T.cast('T.Dict[T.Union[OptionKey, str], str]', self.project_default_options), + T.cast('OptionDict', self.project_default_options), {}) # TODO: this isn't handled by the introspection interpreter... self.coredata.initialized_subprojects.add(self.subproject) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 44209d097..2113362d8 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -115,6 +115,7 @@ if T.TYPE_CHECKING: from . import kwargs as kwtypes from ..backend.backends import Backend from ..interpreterbase.baseobjects import InterpreterObject, TYPE_var, TYPE_kwargs + from ..options import OptionDict from ..programs import OverrideProgram from .type_checking import SourcesVarargsType @@ -868,7 +869,7 @@ class Interpreter(InterpreterBase, HoldableObject): return sub def do_subproject(self, subp_name: str, kwargs: kwtypes.DoSubproject, force_method: T.Optional[wrap.Method] = None, - extra_default_options: T.Optional[T.Dict[str, options.ElementaryOptionValues]] = None) -> SubprojectHolder: + extra_default_options: T.Optional[OptionDict] = None) -> SubprojectHolder: if subp_name == 'sub_static': pass disabled, required, feature = extract_required_kwarg(kwargs, self.subproject) diff --git a/mesonbuild/options.py b/mesonbuild/options.py index f8408bb72..0e637dcba 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -310,7 +310,7 @@ class OptionKey: return self.machine is MachineChoice.BUILD if T.TYPE_CHECKING: - OptionStringLikeDict: TypeAlias = T.Dict[T.Union[OptionKey, str], str] + OptionDict: TypeAlias = T.Dict[T.Union[OptionKey, str], ElementaryOptionValues] @dataclasses.dataclass class UserOption(T.Generic[_T], HoldableObject): @@ -1222,31 +1222,31 @@ class OptionStore: perproject_global_options.append(strvaluetuple) return (global_options, perproject_global_options, project_options) - def optlist2optdict(self, optlist: T.List[str]) -> OptionStringLikeDict: - optdict: OptionStringLikeDict = {} + def optlist2optdict(self, optlist: T.List[str]) -> OptionDict: + optdict: OptionDict = {} for p in optlist: k, v = p.split('=', 1) optdict[k] = v return optdict - def prefix_split_options(self, coll: OptionStringLikeDict) -> T.Tuple[str, OptionStringLikeDict]: + def prefix_split_options(self, coll: OptionDict) -> T.Tuple[T.Optional[str], OptionDict]: prefix = None - others_d: OptionStringLikeDict = {} + others_d: OptionDict = {} for k, v in coll.items(): - if isinstance(k, OptionKey) and k.name == 'prefix': - prefix = v - elif k == 'prefix': + if k == 'prefix' or (isinstance(k, OptionKey) and k.name == 'prefix'): + if not isinstance(v, str): + raise MesonException('Incorrect type for prefix option (expected string)') prefix = v else: others_d[k] = v return (prefix, others_d) def first_handle_prefix(self, - project_default_options: OptionStringLikeDict, - cmd_line_options: OptionStringLikeDict, + project_default_options: OptionDict, + cmd_line_options: OptionDict, machine_file_options: T.Mapping[OptionKey, ElementaryOptionValues]) \ - -> T.Tuple[OptionStringLikeDict, - OptionStringLikeDict, + -> T.Tuple[OptionDict, + OptionDict, T.MutableMapping[OptionKey, ElementaryOptionValues]]: # Copy to avoid later mutation nopref_machine_file_options = T.cast( @@ -1281,8 +1281,8 @@ class OptionStore: self.options[OptionKey('prefix')].set_value(prefix) def initialize_from_top_level_project_call(self, - project_default_options_in: T.Union[T.List[str], OptionStringLikeDict], - cmd_line_options_in: OptionStringLikeDict, + project_default_options_in: T.Union[T.List[str], OptionDict], + cmd_line_options_in: OptionDict, machine_file_options_in: T.Mapping[OptionKey, ElementaryOptionValues]) -> None: first_invocation = True if isinstance(project_default_options_in, list): @@ -1362,7 +1362,7 @@ class OptionStore: else: self.pending_options[key] = valstr - def validate_cmd_line_options(self, cmd_line_options: OptionStringLikeDict) -> None: + def validate_cmd_line_options(self, cmd_line_options: OptionDict) -> None: unknown_options = [] for keystr, valstr in cmd_line_options.items(): if isinstance(keystr, str): @@ -1382,16 +1382,16 @@ class OptionStore: keys = ', '.join(unknown_options) raise MesonException(f'Unknown options: {keys}') - def hacky_mchackface_back_to_list(self, optdict: T.Union[T.List[str], OptionStringLikeDict]) -> T.List[str]: + def hacky_mchackface_back_to_list(self, optdict: T.Union[T.List[str], OptionDict]) -> T.List[str]: if isinstance(optdict, dict): return [f'{k}={v}' for k, v in optdict.items()] return optdict def initialize_from_subproject_call(self, subproject: str, - spcall_default_options_in: OptionStringLikeDict, - project_default_options_in: T.Union[T.List[str], OptionStringLikeDict], - cmd_line_options: OptionStringLikeDict) -> None: + spcall_default_options_in: OptionDict, + project_default_options_in: T.Union[T.List[str], OptionDict], + cmd_line_options: OptionDict) -> None: is_first_invocation = True spcall_default_options = self.hacky_mchackface_back_to_list(spcall_default_options_in) project_default_options = self.hacky_mchackface_back_to_list(project_default_options_in) -- cgit v1.2.3