diff options
Diffstat (limited to 'mesonbuild/interpreter')
-rw-r--r-- | mesonbuild/interpreter/interpreter.py | 9 | ||||
-rw-r--r-- | mesonbuild/interpreter/interpreterobjects.py | 12 | ||||
-rw-r--r-- | mesonbuild/interpreter/mesonmain.py | 14 | ||||
-rw-r--r-- | mesonbuild/interpreter/type_checking.py | 43 |
4 files changed, 54 insertions, 24 deletions
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 30d0e4c..380cc1d 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -63,6 +63,8 @@ from .type_checking import ( DEPFILE_KW, DISABLER_KW, ENV_KW, + ENV_METHOD_KW, + ENV_SEPARATOR_KW, INSTALL_KW, INSTALL_MODE_KW, CT_INSTALL_TAG_KW, @@ -71,6 +73,7 @@ from .type_checking import ( REQUIRED_KW, NoneType, in_set_validator, + env_convertor_with_method ) from . import primitives as P_OBJ @@ -2610,9 +2613,9 @@ external dependencies (including libraries) must go to "dependencies".''') for lang in kwargs['language']: argsdict[lang] = argsdict.get(lang, []) + args - @noKwargs @noArgsFlattening @typed_pos_args('environment', optargs=[(str, list, dict)]) + @typed_kwargs('environment', ENV_METHOD_KW, ENV_SEPARATOR_KW.evolve(since='0.62.0')) def func_environment(self, node: mparser.FunctionNode, args: T.Tuple[T.Union[None, str, T.List['TYPE_var'], T.Dict[str, 'TYPE_var']]], kwargs: 'TYPE_kwargs') -> build.EnvironmentVariables: init = args[0] @@ -2621,7 +2624,9 @@ external dependencies (including libraries) must go to "dependencies".''') msg = ENV_KW.validator(init) if msg: raise InvalidArguments(f'"environment": {msg}') - return ENV_KW.convertor(init) + if isinstance(init, dict) and any(i for i in init.values() if isinstance(i, list)): + FeatureNew.single_use('List of string in dictionary value', '0.62.0', self.subproject, location=node) + return env_convertor_with_method(init, kwargs['method'], kwargs['separator']) return build.EnvironmentVariables() @typed_pos_args('join_paths', varargs=str, min_varargs=1) diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 989877e..4520f09 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -20,7 +20,7 @@ from ..interpreterbase import ( typed_pos_args, typed_kwargs, typed_operator, noArgsFlattening, noPosargs, noKwargs, unholder_return, TYPE_var, TYPE_kwargs, TYPE_nvar, TYPE_nkwargs, flatten, resolve_second_level_holders, InterpreterException, InvalidArguments, InvalidCode) -from ..interpreter.type_checking import NoneType +from ..interpreter.type_checking import NoneType, ENV_SEPARATOR_KW from ..dependencies import Dependency, ExternalLibrary, InternalDependency from ..programs import ExternalProgram from ..mesonlib import HoldableObject, MesonException, OptionKey, listify, Popen_safe @@ -232,10 +232,6 @@ class RunProcess(MesonInterpreterObject): def stderr_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self.stderr - -_ENV_SEPARATOR_KW = KwargInfo('separator', str, default=os.pathsep) - - class EnvironmentVariablesHolder(ObjectHolder[build.EnvironmentVariables], MutableInterpreterObject): def __init__(self, obj: build.EnvironmentVariables, interpreter: 'Interpreter'): @@ -260,20 +256,20 @@ class EnvironmentVariablesHolder(ObjectHolder[build.EnvironmentVariables], Mutab FeatureNew(m, '0.58.0', location=self.current_node).use(self.subproject) @typed_pos_args('environment.set', str, varargs=str, min_varargs=1) - @typed_kwargs('environment.set', _ENV_SEPARATOR_KW) + @typed_kwargs('environment.set', ENV_SEPARATOR_KW) def set_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'EnvironmentSeparatorKW') -> None: name, values = args self.held_object.set(name, values, kwargs['separator']) @typed_pos_args('environment.append', str, varargs=str, min_varargs=1) - @typed_kwargs('environment.append', _ENV_SEPARATOR_KW) + @typed_kwargs('environment.append', ENV_SEPARATOR_KW) def append_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'EnvironmentSeparatorKW') -> None: name, values = args self.warn_if_has_name(name) self.held_object.append(name, values, kwargs['separator']) @typed_pos_args('environment.prepend', str, varargs=str, min_varargs=1) - @typed_kwargs('environment.prepend', _ENV_SEPARATOR_KW) + @typed_kwargs('environment.prepend', ENV_SEPARATOR_KW) def prepend_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'EnvironmentSeparatorKW') -> None: name, values = args self.warn_if_has_name(name) diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index 5781e03..4533c4a 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -12,7 +12,7 @@ from .. import mlog from ..mesonlib import MachineChoice, OptionKey from ..programs import OverrideProgram, ExternalProgram -from ..interpreter.type_checking import ENV_KW +from ..interpreter.type_checking import ENV_KW, ENV_METHOD_KW, ENV_SEPARATOR_KW, env_convertor_with_method from ..interpreterbase import (MesonInterpreterObject, FeatureNew, FeatureDeprecated, typed_pos_args, noArgsFlattening, noPosargs, noKwargs, typed_kwargs, KwargInfo, InterpreterException) @@ -20,6 +20,7 @@ from .primitives import MesonVersionString from .type_checking import NATIVE_KW, NoneType if T.TYPE_CHECKING: + from typing_extensions import Literal from ..backend.backends import ExecutableSerialisation from ..compilers import Compiler from ..interpreterbase import TYPE_kwargs, TYPE_var @@ -41,6 +42,10 @@ if T.TYPE_CHECKING: native: mesonlib.MachineChoice + class AddDevenvKW(TypedDict): + method: Literal['set', 'prepend', 'append'] + separator: str + class MesonMain(MesonInterpreterObject): def __init__(self, build: 'build.Build', interpreter: 'Interpreter'): @@ -438,13 +443,14 @@ class MesonMain(MesonInterpreterObject): return prop_name in self.interpreter.environment.properties[kwargs['native']] @FeatureNew('add_devenv', '0.58.0') - @noKwargs + @typed_kwargs('environment', ENV_METHOD_KW, ENV_SEPARATOR_KW.evolve(since='0.62.0')) @typed_pos_args('add_devenv', (str, list, dict, build.EnvironmentVariables)) - def add_devenv_method(self, args: T.Tuple[T.Union[str, list, dict, build.EnvironmentVariables]], kwargs: 'TYPE_kwargs') -> None: + def add_devenv_method(self, args: T.Tuple[T.Union[str, list, dict, build.EnvironmentVariables]], + kwargs: 'AddDevenvKW') -> None: env = args[0] msg = ENV_KW.validator(env) if msg: raise build.InvalidArguments(f'"add_devenv": {msg}') - converted = ENV_KW.convertor(env) + converted = env_convertor_with_method(env, kwargs['method'], kwargs['separator']) assert isinstance(converted, build.EnvironmentVariables) self.build.devenv.append(converted) diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index e94027b..cce794f 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -3,10 +3,13 @@ """Helpers for strict type checking.""" +from __future__ import annotations +import os import typing as T from .. import compilers -from ..build import EnvironmentVariables, CustomTarget, BuildTarget, CustomTargetIndex, ExtractedObjects, GeneratedList, IncludeDirs +from ..build import (EnvironmentVariables, EnvInitValueType, CustomTarget, BuildTarget, + CustomTargetIndex, ExtractedObjects, GeneratedList, IncludeDirs) from ..coredata import UserFeatureOption from ..interpreterbase import TYPE_var from ..interpreterbase.decorators import KwargInfo, ContainerTypeInfo @@ -16,6 +19,8 @@ from ..programs import ExternalProgram # Helper definition for type checks that are `Optional[T]` NoneType: T.Type[None] = type(None) +if T.TYPE_CHECKING: + from typing_extensions import Literal 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.""" @@ -131,7 +136,8 @@ REQUIRED_KW: KwargInfo[T.Union[bool, UserFeatureOption]] = KwargInfo( DISABLER_KW: KwargInfo[bool] = KwargInfo('disabler', bool, default=False) -def _env_validator(value: T.Union[EnvironmentVariables, T.List['TYPE_var'], T.Dict[str, 'TYPE_var'], str, None]) -> T.Optional[str]: +def _env_validator(value: T.Union[EnvironmentVariables, T.List['TYPE_var'], T.Dict[str, 'TYPE_var'], str, None], + allow_dict_list: bool = True) -> T.Optional[str]: def _splitter(v: str) -> T.Optional[str]: split = v.split('=', 1) if len(split) == 1: @@ -152,12 +158,18 @@ def _env_validator(value: T.Union[EnvironmentVariables, T.List['TYPE_var'], T.Di elif isinstance(value, dict): # We don't need to spilt here, just do the type checking for k, dv in value.items(): - if not isinstance(dv, str): + if allow_dict_list: + if any(i for i in listify(dv) if not isinstance(i, str)): + return f"Dictionary element {k} must be a string or list of strings not {dv!r}" + elif not isinstance(dv, str): return f"Dictionary element {k} must be a string not {dv!r}" # We know that otherwise we have an EnvironmentVariables object or None, and # we're okay at this point return None +def _options_validator(value: T.Union[EnvironmentVariables, T.List['TYPE_var'], T.Dict[str, 'TYPE_var'], str, None]) -> T.Optional[str]: + # Reusing the env validator is a littl overkill, but nicer than duplicating the code + return _env_validator(value, allow_dict_list=False) def split_equal_string(input: str) -> T.Tuple[str, str]: """Split a string in the form `x=y` @@ -167,18 +179,25 @@ def split_equal_string(input: str) -> T.Tuple[str, str]: a, b = input.split('=', 1) return (a, b) +_FullEnvInitValueType = T.Union[EnvironmentVariables, T.List[str], T.List[T.List[str]], EnvInitValueType, str, None] -def _env_convertor(value: T.Union[EnvironmentVariables, T.List[str], T.List[T.List[str]], T.Dict[str, str], str, None]) -> EnvironmentVariables: +# Split _env_convertor() and env_convertor_with_method() to make mypy happy. +# It does not want extra arguments in KwargInfo convertor callable. +def env_convertor_with_method(value: _FullEnvInitValueType, + init_method: Literal['set', 'prepend', 'append'] = 'set', + separator: str = os.pathsep) -> EnvironmentVariables: if isinstance(value, str): - return EnvironmentVariables(dict([split_equal_string(value)])) + return EnvironmentVariables(dict([split_equal_string(value)]), init_method, separator) elif isinstance(value, list): - return EnvironmentVariables(dict(split_equal_string(v) for v in listify(value))) + return EnvironmentVariables(dict(split_equal_string(v) for v in listify(value)), init_method, separator) elif isinstance(value, dict): - return EnvironmentVariables(value) + return EnvironmentVariables(value, init_method, separator) elif value is None: return EnvironmentVariables() return value +def _env_convertor(value: _FullEnvInitValueType) -> EnvironmentVariables: + return env_convertor_with_method(value) ENV_KW: KwargInfo[T.Union[EnvironmentVariables, T.List, T.Dict, str, None]] = KwargInfo( 'env', @@ -230,8 +249,7 @@ OVERRIDE_OPTIONS_KW: KwargInfo[T.List[str]] = KwargInfo( ContainerTypeInfo(list, str), listify=True, default=[], - # Reusing the env validator is a littl overkill, but nicer than duplicating the code - validator=_env_validator, + validator=_options_validator, convertor=_override_options_convertor, ) @@ -309,5 +327,10 @@ DEFAULT_OPTIONS: KwargInfo[T.List[str]] = KwargInfo( ContainerTypeInfo(list, (str, IncludeDirs)), listify=True, default=[], - validator=_env_validator, + validator=_options_validator, ) + +ENV_METHOD_KW = KwargInfo('method', str, default='set', since='0.62.0', + validator=in_set_validator({'set', 'prepend', 'append'})) + +ENV_SEPARATOR_KW = KwargInfo('separator', str, default=os.pathsep, since='0.62.0') |