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 .interpreterobjects import (
SubprojectHolder,
EnvironmentVariablesObject,
ConfigurationDataObject,
Test,
RunProcess,
@ -396,6 +395,7 @@ class Interpreter(InterpreterBase, HoldableObject):
build.Data: OBJ.DataHolder,
build.InstallDir: OBJ.InstallDirHolder,
build.IncludeDirs: OBJ.IncludeDirsHolder,
build.EnvironmentVariables: OBJ.EnvironmentVariablesObject,
compilers.RunResult: compilerOBJ.TryRunResultHolder,
dependencies.ExternalLibrary: OBJ.ExternalLibraryHolder,
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:
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:
envlist = kwargs.get('env', EnvironmentVariablesObject())
if isinstance(envlist, EnvironmentVariablesObject):
env = envlist.vars
elif isinstance(envlist, dict):
FeatureNew.single_use('environment dictionary', '0.52.0', self.subproject)
env = EnvironmentVariablesObject(envlist)
env = env.vars
else:
# Convert from array to environment object
env = EnvironmentVariablesObject(envlist)
env = env.vars
return env
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')
if envlist is None:
return build.EnvironmentVariables()
msg = ENV_KW.validator(envlist)
if msg:
raise InvalidArguments(f'"env": {msg}')
return ENV_KW.convertor(envlist)
def make_test(self, node: mparser.BaseNode,
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
@noArgsFlattening
def func_environment(self, node, args, kwargs):
if len(args) > 1:
raise InterpreterException('environment takes only one optional positional arguments')
elif len(args) == 1:
@typed_pos_args('environment', optargs=[(str, list, dict)])
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]
if init is not None:
FeatureNew.single_use('environment positional arguments', '0.52.0', self.subproject)
initial_values = args[0]
if not isinstance(initial_values, dict) and not isinstance(initial_values, list):
raise InterpreterException('environment first argument must be a dictionary or a list')
else:
initial_values = {}
return EnvironmentVariablesObject(initial_values, self.subproject)
msg = ENV_KW.validator(init)
if msg:
raise InvalidArguments(f'"environment": {msg}')
return ENV_KW.convertor(init)
return build.EnvironmentVariables()
@typed_pos_args('join_paths', varargs=str, min_varargs=1)
@noKwargs

@ -229,39 +229,19 @@ class RunProcess(MesonInterpreterObject):
def stderr_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.stderr
# TODO: Parsing the initial values should be either done directly in the
# `Interpreter` or in `build.EnvironmentVariables`. This way, this class
# can be converted into a pure object holder.
class EnvironmentVariablesObject(MutableInterpreterObject, MesonInterpreterObject):
# TODO: Move the type cheking for initial_values out of this class and replace T.Any
def __init__(self, initial_values: T.Optional[T.Any] = None, subproject: str = ''):
super().__init__(subproject=subproject)
self.vars = build.EnvironmentVariables()
class EnvironmentVariablesObject(ObjectHolder[build.EnvironmentVariables], MutableInterpreterObject):
def __init__(self, obj: build.EnvironmentVariables, interpreter: 'Interpreter'):
super().__init__(obj, interpreter)
self.methods.update({'set': self.set_method,
'append': self.append_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:
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:
separator = kwargs.get('separator', os.pathsep)
@ -270,9 +250,13 @@ class EnvironmentVariablesObject(MutableInterpreterObject, MesonInterpreterObjec
" argument needs to be a string.")
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:
# 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'
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:
name, values = args
separator = self.unpack_separator(kwargs)
self.vars.set(name, values, separator)
self.held_object.set(name, values, separator)
@stringArgs
@permittedKwargs({'separator'})
@ -291,7 +275,7 @@ class EnvironmentVariablesObject(MutableInterpreterObject, MesonInterpreterObjec
name, values = args
separator = self.unpack_separator(kwargs)
self.warn_if_has_name(name)
self.vars.append(name, values, separator)
self.held_object.append(name, values, separator)
@stringArgs
@permittedKwargs({'separator'})
@ -300,7 +284,7 @@ class EnvironmentVariablesObject(MutableInterpreterObject, MesonInterpreterObjec
name, values = args
separator = self.unpack_separator(kwargs)
self.warn_if_has_name(name)
self.vars.prepend(name, values, separator)
self.held_object.prepend(name, values, separator)
class ConfigurationDataObject(MutableInterpreterObject, MesonInterpreterObject):

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

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

Loading…
Cancel
Save