make EnvironmentVariablesObject a proper holder

Currently, EnvironmentVariablesObject is a strange
holder-that's-not-a-holder. This has implicaitons for things that expect
to get an EnvironmentVariables object, as we can't automatically
unholder it, and instead have to to manually do so. Now we can
automatically unholder it, which makes everything much nicer.
pull/9185/head
Dylan Baker 3 years ago
parent d6a1f85248
commit 3731e1d8f3
  1. 43
      mesonbuild/interpreter/interpreter.py
  2. 44
      mesonbuild/interpreter/interpreterobjects.py
  3. 3
      mesonbuild/interpreter/kwargs.py
  4. 15
      mesonbuild/interpreter/mesonmain.py

@ -42,7 +42,6 @@ from .mesonmain import MesonMain
from .dependencyfallbacks import DependencyFallbacksHolder from .dependencyfallbacks import DependencyFallbacksHolder
from .interpreterobjects import ( from .interpreterobjects import (
SubprojectHolder, SubprojectHolder,
EnvironmentVariablesObject,
ConfigurationDataObject, ConfigurationDataObject,
Test, Test,
RunProcess, RunProcess,
@ -396,6 +395,7 @@ class Interpreter(InterpreterBase, HoldableObject):
build.Data: OBJ.DataHolder, build.Data: OBJ.DataHolder,
build.InstallDir: OBJ.InstallDirHolder, build.InstallDir: OBJ.InstallDirHolder,
build.IncludeDirs: OBJ.IncludeDirsHolder, build.IncludeDirs: OBJ.IncludeDirsHolder,
build.EnvironmentVariables: OBJ.EnvironmentVariablesObject,
compilers.RunResult: compilerOBJ.TryRunResultHolder, compilers.RunResult: compilerOBJ.TryRunResultHolder,
dependencies.ExternalLibrary: OBJ.ExternalLibraryHolder, dependencies.ExternalLibrary: OBJ.ExternalLibraryHolder,
coredata.UserFeatureOption: OBJ.FeatureOptionHolder, coredata.UserFeatureOption: OBJ.FeatureOptionHolder,
@ -1723,19 +1723,14 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
kwargs: 'kwargs.FuncTest') -> None: kwargs: 'kwargs.FuncTest') -> None:
self.add_test(node, args, kwargs, True) self.add_test(node, args, kwargs, True)
def unpack_env_kwarg(self, kwargs: T.Union[EnvironmentVariablesObject, T.Dict[str, str], T.List[str]]) -> build.EnvironmentVariables: def unpack_env_kwarg(self, kwargs: T.Union[build.EnvironmentVariables, T.Dict[str, 'TYPE_var'], T.List['TYPE_var'], str]) -> build.EnvironmentVariables:
envlist = kwargs.get('env', EnvironmentVariablesObject()) envlist = kwargs.get('env')
if isinstance(envlist, EnvironmentVariablesObject): if envlist is None:
env = envlist.vars return build.EnvironmentVariables()
elif isinstance(envlist, dict): msg = ENV_KW.validator(envlist)
FeatureNew.single_use('environment dictionary', '0.52.0', self.subproject) if msg:
env = EnvironmentVariablesObject(envlist) raise InvalidArguments(f'"env": {msg}')
env = env.vars return ENV_KW.convertor(envlist)
else:
# Convert from array to environment object
env = EnvironmentVariablesObject(envlist)
env = env.vars
return env
def make_test(self, node: mparser.BaseNode, def make_test(self, node: mparser.BaseNode,
args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, mesonlib.File]], args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, mesonlib.File]],
@ -2361,17 +2356,17 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
@noKwargs @noKwargs
@noArgsFlattening @noArgsFlattening
def func_environment(self, node, args, kwargs): @typed_pos_args('environment', optargs=[(str, list, dict)])
if len(args) > 1: def func_environment(self, node: mparser.FunctionNode, args: T.Tuple[T.Union[None, str, T.List['TYPE_var'], T.Dict[str, 'TYPE_var']]],
raise InterpreterException('environment takes only one optional positional arguments') kwargs: 'TYPE_kwargs') -> build.EnvironmentVariables:
elif len(args) == 1: init = args[0]
if init is not None:
FeatureNew.single_use('environment positional arguments', '0.52.0', self.subproject) FeatureNew.single_use('environment positional arguments', '0.52.0', self.subproject)
initial_values = args[0] msg = ENV_KW.validator(init)
if not isinstance(initial_values, dict) and not isinstance(initial_values, list): if msg:
raise InterpreterException('environment first argument must be a dictionary or a list') raise InvalidArguments(f'"environment": {msg}')
else: return ENV_KW.convertor(init)
initial_values = {} return build.EnvironmentVariables()
return EnvironmentVariablesObject(initial_values, self.subproject)
@typed_pos_args('join_paths', varargs=str, min_varargs=1) @typed_pos_args('join_paths', varargs=str, min_varargs=1)
@noKwargs @noKwargs

@ -229,39 +229,19 @@ class RunProcess(MesonInterpreterObject):
def stderr_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: def stderr_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.stderr return self.stderr
# TODO: Parsing the initial values should be either done directly in the
# `Interpreter` or in `build.EnvironmentVariables`. This way, this class class EnvironmentVariablesObject(ObjectHolder[build.EnvironmentVariables], MutableInterpreterObject):
# can be converted into a pure object holder.
class EnvironmentVariablesObject(MutableInterpreterObject, MesonInterpreterObject): def __init__(self, obj: build.EnvironmentVariables, interpreter: 'Interpreter'):
# TODO: Move the type cheking for initial_values out of this class and replace T.Any super().__init__(obj, interpreter)
def __init__(self, initial_values: T.Optional[T.Any] = None, subproject: str = ''):
super().__init__(subproject=subproject)
self.vars = build.EnvironmentVariables()
self.methods.update({'set': self.set_method, self.methods.update({'set': self.set_method,
'append': self.append_method, 'append': self.append_method,
'prepend': self.prepend_method, 'prepend': self.prepend_method,
}) })
if isinstance(initial_values, build.EnvironmentVariables):
self.vars = initial_values
elif isinstance(initial_values, dict):
for k, v in initial_values.items():
self.set_method([k, v], {})
elif initial_values is not None:
for e in mesonlib.listify(initial_values):
if not isinstance(e, str):
raise InterpreterException('Env var definition must be a list of strings.')
if '=' not in e:
raise InterpreterException('Env var definition must be of type key=val.')
(k, val) = e.split('=', 1)
k = k.strip()
val = val.strip()
if ' ' in k:
raise InterpreterException('Env var key must not have spaces in it.')
self.set_method([k, val], {})
def __repr__(self) -> str: def __repr__(self) -> str:
repr_str = "<{0}: {1}>" repr_str = "<{0}: {1}>"
return repr_str.format(self.__class__.__name__, self.vars.envvars) return repr_str.format(self.__class__.__name__, self.held_object.envvars)
def unpack_separator(self, kwargs: T.Dict[str, T.Any]) -> str: def unpack_separator(self, kwargs: T.Dict[str, T.Any]) -> str:
separator = kwargs.get('separator', os.pathsep) separator = kwargs.get('separator', os.pathsep)
@ -270,9 +250,13 @@ class EnvironmentVariablesObject(MutableInterpreterObject, MesonInterpreterObjec
" argument needs to be a string.") " argument needs to be a string.")
return separator return separator
def __deepcopy__(self, memo: T.Dict[str, object]) -> 'EnvironmentVariablesObject':
# Avoid trying to copy the intepreter
return EnvironmentVariablesObject(copy.deepcopy(self.held_object), self.interpreter)
def warn_if_has_name(self, name: str) -> None: def warn_if_has_name(self, name: str) -> None:
# Multiple append/prepend operations was not supported until 0.58.0. # Multiple append/prepend operations was not supported until 0.58.0.
if self.vars.has_name(name): if self.held_object.has_name(name):
m = f'Overriding previous value of environment variable {name!r} with a new one' m = f'Overriding previous value of environment variable {name!r} with a new one'
FeatureNew('0.58.0', m).use(self.subproject) FeatureNew('0.58.0', m).use(self.subproject)
@ -282,7 +266,7 @@ class EnvironmentVariablesObject(MutableInterpreterObject, MesonInterpreterObjec
def set_method(self, args: T.Tuple[str, T.List[str]], kwargs: T.Dict[str, T.Any]) -> None: def set_method(self, args: T.Tuple[str, T.List[str]], kwargs: T.Dict[str, T.Any]) -> None:
name, values = args name, values = args
separator = self.unpack_separator(kwargs) separator = self.unpack_separator(kwargs)
self.vars.set(name, values, separator) self.held_object.set(name, values, separator)
@stringArgs @stringArgs
@permittedKwargs({'separator'}) @permittedKwargs({'separator'})
@ -291,7 +275,7 @@ class EnvironmentVariablesObject(MutableInterpreterObject, MesonInterpreterObjec
name, values = args name, values = args
separator = self.unpack_separator(kwargs) separator = self.unpack_separator(kwargs)
self.warn_if_has_name(name) self.warn_if_has_name(name)
self.vars.append(name, values, separator) self.held_object.append(name, values, separator)
@stringArgs @stringArgs
@permittedKwargs({'separator'}) @permittedKwargs({'separator'})
@ -300,7 +284,7 @@ class EnvironmentVariablesObject(MutableInterpreterObject, MesonInterpreterObjec
name, values = args name, values = args
separator = self.unpack_separator(kwargs) separator = self.unpack_separator(kwargs)
self.warn_if_has_name(name) self.warn_if_has_name(name)
self.vars.prepend(name, values, separator) self.held_object.prepend(name, values, separator)
class ConfigurationDataObject(MutableInterpreterObject, MesonInterpreterObject): class ConfigurationDataObject(MutableInterpreterObject, MesonInterpreterObject):

@ -11,7 +11,6 @@ from typing_extensions import TypedDict, Literal
from .. import build from .. import build
from .. import coredata from .. import coredata
from ..mesonlib import MachineChoice, File, FileMode, FileOrString from ..mesonlib import MachineChoice, File, FileMode, FileOrString
from .interpreterobjects import EnvironmentVariablesObject
class FuncAddProjectArgs(TypedDict): class FuncAddProjectArgs(TypedDict):
@ -39,7 +38,7 @@ class BaseTest(TypedDict):
workdir: T.Optional[str] workdir: T.Optional[str]
depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]] depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]]
priority: int priority: int
env: EnvironmentVariablesObject env: build.EnvironmentVariables
suite: T.List[str] suite: T.List[str]

@ -7,13 +7,13 @@ from .. import mlog
from ..mesonlib import MachineChoice, OptionKey from ..mesonlib import MachineChoice, OptionKey
from ..programs import OverrideProgram, ExternalProgram from ..programs import OverrideProgram, ExternalProgram
from ..interpreter.type_checking import ENV_KW
from ..interpreterbase import (MesonInterpreterObject, FeatureNew, FeatureDeprecated, from ..interpreterbase import (MesonInterpreterObject, FeatureNew, FeatureDeprecated,
typed_pos_args, permittedKwargs, noArgsFlattening, noPosargs, noKwargs, typed_pos_args, permittedKwargs, noArgsFlattening, noPosargs, noKwargs,
typed_kwargs, KwargInfo, MesonVersionString, InterpreterException) typed_kwargs, KwargInfo, MesonVersionString, InterpreterException)
from .interpreterobjects import (ExecutableHolder, ExternalProgramHolder, from .interpreterobjects import (ExecutableHolder, ExternalProgramHolder,
CustomTargetHolder, CustomTargetIndexHolder, CustomTargetHolder, CustomTargetIndexHolder)
EnvironmentVariablesObject)
from .type_checking import NATIVE_KW, NoneType from .type_checking import NATIVE_KW, NoneType
import typing as T import typing as T
@ -415,9 +415,10 @@ class MesonMain(MesonInterpreterObject):
@FeatureNew('add_devenv', '0.58.0') @FeatureNew('add_devenv', '0.58.0')
@noKwargs @noKwargs
@typed_pos_args('add_devenv', (str, list, dict, EnvironmentVariablesObject)) @typed_pos_args('add_devenv', (str, list, dict, build.EnvironmentVariables))
def add_devenv_method(self, args: T.Union[str, list, dict, EnvironmentVariablesObject], kwargs: T.Dict[str, T.Any]) -> None: def add_devenv_method(self, args: T.Tuple[T.Union[str, list, dict, build.EnvironmentVariables]], kwargs: T.Dict[str, T.Any]) -> None:
env = args[0] env = args[0]
if isinstance(env, (str, list, dict)): msg = ENV_KW.validator(env)
env = EnvironmentVariablesObject(env) if msg:
self.build.devenv.append(env.vars) raise build.InvalidArguments(f'"add_devenv": {msg}')
self.build.devenv.append(ENV_KW.convertor(env))

Loading…
Cancel
Save