aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Mensinger <daniel@mensinger-ka.de>2021-06-10 13:08:59 +0200
committerDaniel Mensinger <daniel@mensinger-ka.de>2021-06-11 10:42:18 +0200
commit687eebee298fd1091eab0173febad7be1ce82e2a (patch)
tree362d92866b8ebeca973fe244aa9f2f3137485356
parent2e3550db148b7d141225d67466ef563649320c2b (diff)
downloadmeson-687eebee298fd1091eab0173febad7be1ce82e2a.zip
meson-687eebee298fd1091eab0173febad7be1ce82e2a.tar.gz
meson-687eebee298fd1091eab0173febad7be1ce82e2a.tar.bz2
interpreter: Split base objects and helpers from interpreterbase.py
-rw-r--r--mesonbuild/interpreterbase/__init__.py53
-rw-r--r--mesonbuild/interpreterbase/baseobjects.py79
-rw-r--r--mesonbuild/interpreterbase/helpers.py107
-rw-r--r--mesonbuild/interpreterbase/interpreterbase.py175
4 files changed, 237 insertions, 177 deletions
diff --git a/mesonbuild/interpreterbase/__init__.py b/mesonbuild/interpreterbase/__init__.py
index d635dfd..0d9c4ed 100644
--- a/mesonbuild/interpreterbase/__init__.py
+++ b/mesonbuild/interpreterbase/__init__.py
@@ -24,8 +24,17 @@ __all__ = [
'is_arg_disabled',
'is_disabled',
+ 'InterpreterException',
+ 'InvalidCode',
+ 'InvalidArguments',
+ 'SubdirDoneRequest',
+ 'ContinueRequest',
+ 'BreakRequest',
+
'check_stringlist',
+ 'default_resolve_key',
'flatten',
+
'noPosargs',
'builtinMethodNoKwargs',
'noKwargs',
@@ -43,14 +52,6 @@ __all__ = [
'FeatureDeprecatedKwargs',
'InterpreterBase',
- 'default_resolve_key',
-
- 'InterpreterException',
- 'InvalidCode',
- 'InvalidArguments',
- 'SubdirDoneRequest',
- 'ContinueRequest',
- 'BreakRequest',
'TV_fw_var',
'TV_fw_args',
@@ -63,6 +64,23 @@ __all__ = [
'TYPE_key_resolver',
]
+from .baseobjects import (
+ InterpreterObject,
+ ObjectHolder,
+ RangeHolder,
+ MutableInterpreterObject,
+
+ TV_fw_var,
+ TV_fw_args,
+ TV_fw_kwargs,
+ TV_func,
+ TYPE_elementary,
+ TYPE_var,
+ TYPE_nvar,
+ TYPE_nkwargs,
+ TYPE_key_resolver,
+)
+
from .exceptions import (
InterpreterException,
InvalidCode,
@@ -72,20 +90,16 @@ from .exceptions import (
BreakRequest,
)
+from .helpers import check_stringlist, default_resolve_key, flatten
+
from .interpreterbase import (
- InterpreterObject,
- ObjectHolder,
- RangeHolder,
MesonVersionString,
- MutableInterpreterObject,
Disabler,
is_disabler,
is_arg_disabled,
is_disabled,
- check_stringlist,
- flatten,
noPosargs,
builtinMethodNoKwargs,
noKwargs,
@@ -103,15 +117,4 @@ from .interpreterbase import (
FeatureDeprecatedKwargs,
InterpreterBase,
- default_resolve_key,
-
- TV_fw_var,
- TV_fw_args,
- TV_fw_kwargs,
- TV_func,
- TYPE_elementary,
- TYPE_var,
- TYPE_nvar,
- TYPE_nkwargs,
- TYPE_key_resolver,
)
diff --git a/mesonbuild/interpreterbase/baseobjects.py b/mesonbuild/interpreterbase/baseobjects.py
new file mode 100644
index 0000000..f48ab9b
--- /dev/null
+++ b/mesonbuild/interpreterbase/baseobjects.py
@@ -0,0 +1,79 @@
+# Copyright 2013-2021 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from .. import mparser
+from .exceptions import InvalidCode
+from .helpers import flatten
+
+import typing as T
+
+TV_fw_var = T.Union[str, int, float, bool, list, dict, 'InterpreterObject', 'ObjectHolder']
+TV_fw_args = T.List[T.Union[mparser.BaseNode, TV_fw_var]]
+TV_fw_kwargs = T.Dict[str, T.Union[mparser.BaseNode, TV_fw_var]]
+
+TV_func = T.TypeVar('TV_func', bound=T.Callable[..., T.Any])
+
+TYPE_elementary = T.Union[str, int, float, bool]
+TYPE_var = T.Union[TYPE_elementary, T.List[T.Any], T.Dict[str, T.Any], 'InterpreterObject', 'ObjectHolder']
+TYPE_nvar = T.Union[TYPE_var, mparser.BaseNode]
+TYPE_nkwargs = T.Dict[str, TYPE_nvar]
+TYPE_key_resolver = T.Callable[[mparser.BaseNode], str]
+
+class InterpreterObject:
+ def __init__(self) -> None:
+ self.methods = {} # type: T.Dict[str, T.Callable[[T.List[TYPE_nvar], TYPE_nkwargs], TYPE_var]]
+ # Current node set during a method call. This can be used as location
+ # when printing a warning message during a method call.
+ self.current_node = None # type: mparser.BaseNode
+
+ def method_call(
+ self,
+ method_name: str,
+ args: TV_fw_args,
+ kwargs: TV_fw_kwargs
+ ) -> TYPE_var:
+ if method_name in self.methods:
+ method = self.methods[method_name]
+ if not getattr(method, 'no-args-flattening', False):
+ args = flatten(args)
+ return method(args, kwargs)
+ raise InvalidCode('Unknown method "%s" in object.' % method_name)
+
+class MutableInterpreterObject(InterpreterObject):
+ def __init__(self) -> None:
+ super().__init__()
+
+TV_InterpreterObject = T.TypeVar('TV_InterpreterObject')
+
+class ObjectHolder(T.Generic[TV_InterpreterObject]):
+ def __init__(self, obj: TV_InterpreterObject, subproject: str = '') -> None:
+ self.held_object = obj
+ self.subproject = subproject
+
+ def __repr__(self) -> str:
+ return f'<Holder: {self.held_object!r}>'
+
+class RangeHolder(InterpreterObject):
+ def __init__(self, start: int, stop: int, step: int) -> None:
+ super().__init__()
+ self.range = range(start, stop, step)
+
+ def __iter__(self) -> T.Iterator[int]:
+ return iter(self.range)
+
+ def __getitem__(self, key: int) -> int:
+ return self.range[key]
+
+ def __len__(self) -> int:
+ return len(self.range)
diff --git a/mesonbuild/interpreterbase/helpers.py b/mesonbuild/interpreterbase/helpers.py
new file mode 100644
index 0000000..1070a9e
--- /dev/null
+++ b/mesonbuild/interpreterbase/helpers.py
@@ -0,0 +1,107 @@
+# Copyright 2013-2021 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from .. import mparser, mlog
+from .exceptions import InvalidArguments, InterpreterException
+
+import collections.abc
+import typing as T
+
+if T.TYPE_CHECKING:
+ from .baseobjects import TYPE_nvar, TV_fw_args, TV_fw_kwargs
+
+def flatten(args: T.Union['TYPE_nvar', T.List['TYPE_nvar']]) -> T.List['TYPE_nvar']:
+ if isinstance(args, mparser.StringNode):
+ assert isinstance(args.value, str)
+ return [args.value]
+ if not isinstance(args, collections.abc.Sequence):
+ return [args]
+ result: T.List['TYPE_nvar'] = []
+ for a in args:
+ if isinstance(a, list):
+ rest = flatten(a)
+ result = result + rest
+ elif isinstance(a, mparser.StringNode):
+ result.append(a.value)
+ else:
+ result.append(a)
+ return result
+
+def check_stringlist(a: T.Any, msg: str = 'Arguments must be strings.') -> None:
+ if not isinstance(a, list):
+ mlog.debug('Not a list:', str(a))
+ raise InvalidArguments('Argument not a list.')
+ if not all(isinstance(s, str) for s in a):
+ mlog.debug('Element not a string:', str(a))
+ raise InvalidArguments(msg)
+
+def default_resolve_key(key: mparser.BaseNode) -> str:
+ if not isinstance(key, mparser.IdNode):
+ raise InterpreterException('Invalid kwargs format.')
+ return key.value
+
+def get_callee_args(wrapped_args: T.Sequence[T.Any], want_subproject: bool = False) -> T.Tuple[T.Any, mparser.BaseNode, 'TV_fw_args', 'TV_fw_kwargs', T.Optional[str]]:
+ s = wrapped_args[0]
+ n = len(wrapped_args)
+ # Raise an error if the codepaths are not there
+ subproject = None # type: T.Optional[str]
+ if want_subproject and n == 2:
+ if hasattr(s, 'subproject'):
+ # Interpreter base types have 2 args: self, node
+ node = wrapped_args[1]
+ # args and kwargs are inside the node
+ args = None
+ kwargs = None
+ subproject = s.subproject
+ elif hasattr(wrapped_args[1], 'subproject'):
+ # Module objects have 2 args: self, interpreter
+ node = wrapped_args[1].current_node
+ # args and kwargs are inside the node
+ args = None
+ kwargs = None
+ subproject = wrapped_args[1].subproject
+ else:
+ raise AssertionError(f'Unknown args: {wrapped_args!r}')
+ elif n == 3:
+ # Methods on objects (*Holder, MesonMain, etc) have 3 args: self, args, kwargs
+ node = s.current_node
+ args = wrapped_args[1]
+ kwargs = wrapped_args[2]
+ if want_subproject:
+ if hasattr(s, 'subproject'):
+ subproject = s.subproject
+ elif hasattr(s, 'interpreter'):
+ subproject = s.interpreter.subproject
+ elif n == 4:
+ # Meson functions have 4 args: self, node, args, kwargs
+ # Module functions have 4 args: self, state, args, kwargs
+ from .interpreterbase import InterpreterBase # TODO: refactor to avoid this import
+ if isinstance(s, InterpreterBase):
+ node = wrapped_args[1]
+ else:
+ node = wrapped_args[1].current_node
+ args = wrapped_args[2]
+ kwargs = wrapped_args[3]
+ if want_subproject:
+ if isinstance(s, InterpreterBase):
+ subproject = s.subproject
+ else:
+ subproject = wrapped_args[1].subproject
+ else:
+ raise AssertionError(f'Unknown args: {wrapped_args!r}')
+ # Sometimes interpreter methods are called internally with None instead of
+ # empty list/dict
+ args = args if args is not None else []
+ kwargs = kwargs if kwargs is not None else {}
+ return s, node, args, kwargs, subproject
diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py
index 708741a..8c28b78 100644
--- a/mesonbuild/interpreterbase/interpreterbase.py
+++ b/mesonbuild/interpreterbase/interpreterbase.py
@@ -18,6 +18,18 @@
from .. import mparser, mesonlib, mlog
from .. import environment, dependencies
+from .baseobjects import (
+ InterpreterObject,
+ MutableInterpreterObject,
+ ObjectHolder,
+ RangeHolder,
+
+ TV_func,
+ TYPE_var,
+ TYPE_nvar,
+ TYPE_nkwargs,
+)
+
from .exceptions import (
InterpreterException,
InvalidCode,
@@ -27,157 +39,25 @@ from .exceptions import (
BreakRequest
)
+from .helpers import check_stringlist, default_resolve_key, flatten, get_callee_args
+
from functools import wraps
import abc
-import collections.abc
import itertools
import os, copy, re
import typing as T
-TV_fw_var = T.Union[str, int, float, bool, list, dict, 'InterpreterObject', 'ObjectHolder']
-TV_fw_args = T.List[T.Union[mparser.BaseNode, TV_fw_var]]
-TV_fw_kwargs = T.Dict[str, T.Union[mparser.BaseNode, TV_fw_var]]
-
-TV_func = T.TypeVar('TV_func', bound=T.Callable[..., T.Any])
-
-TYPE_elementary = T.Union[str, int, float, bool]
-TYPE_var = T.Union[TYPE_elementary, T.List[T.Any], T.Dict[str, T.Any], 'InterpreterObject', 'ObjectHolder']
-TYPE_nvar = T.Union[TYPE_var, mparser.BaseNode]
-TYPE_nkwargs = T.Dict[str, TYPE_nvar]
-TYPE_key_resolver = T.Callable[[mparser.BaseNode], str]
-
-class InterpreterObject:
- def __init__(self) -> None:
- self.methods = {} # type: T.Dict[str, T.Callable[[T.List[TYPE_nvar], TYPE_nkwargs], TYPE_var]]
- # Current node set during a method call. This can be used as location
- # when printing a warning message during a method call.
- self.current_node = None # type: mparser.BaseNode
-
- def method_call(
- self,
- method_name: str,
- args: TV_fw_args,
- kwargs: TV_fw_kwargs
- ) -> TYPE_var:
- if method_name in self.methods:
- method = self.methods[method_name]
- if not getattr(method, 'no-args-flattening', False):
- args = flatten(args)
- return method(args, kwargs)
- raise InvalidCode('Unknown method "%s" in object.' % method_name)
-
-TV_InterpreterObject = T.TypeVar('TV_InterpreterObject')
-
-class ObjectHolder(T.Generic[TV_InterpreterObject]):
- def __init__(self, obj: TV_InterpreterObject, subproject: str = '') -> None:
- self.held_object = obj
- self.subproject = subproject
-
- def __repr__(self) -> str:
- return f'<Holder: {self.held_object!r}>'
class MesonVersionString(str):
pass
-class RangeHolder(InterpreterObject):
- def __init__(self, start: int, stop: int, step: int) -> None:
- super().__init__()
- self.range = range(start, stop, step)
-
- def __iter__(self) -> T.Iterator[int]:
- return iter(self.range)
-
- def __getitem__(self, key: int) -> int:
- return self.range[key]
-
- def __len__(self) -> int:
- return len(self.range)
# Decorators for method calls.
-def check_stringlist(a: T.Any, msg: str = 'Arguments must be strings.') -> None:
- if not isinstance(a, list):
- mlog.debug('Not a list:', str(a))
- raise InvalidArguments('Argument not a list.')
- if not all(isinstance(s, str) for s in a):
- mlog.debug('Element not a string:', str(a))
- raise InvalidArguments(msg)
-
-def _get_callee_args(wrapped_args: T.Sequence[T.Any], want_subproject: bool = False) -> T.Tuple[T.Any, mparser.BaseNode, TV_fw_args, TV_fw_kwargs, T.Optional[str]]:
- s = wrapped_args[0]
- n = len(wrapped_args)
- # Raise an error if the codepaths are not there
- subproject = None # type: T.Optional[str]
- if want_subproject and n == 2:
- if hasattr(s, 'subproject'):
- # Interpreter base types have 2 args: self, node
- node = wrapped_args[1]
- # args and kwargs are inside the node
- args = None
- kwargs = None
- subproject = s.subproject
- elif hasattr(wrapped_args[1], 'subproject'):
- # Module objects have 2 args: self, interpreter
- node = wrapped_args[1].current_node
- # args and kwargs are inside the node
- args = None
- kwargs = None
- subproject = wrapped_args[1].subproject
- else:
- raise AssertionError(f'Unknown args: {wrapped_args!r}')
- elif n == 3:
- # Methods on objects (*Holder, MesonMain, etc) have 3 args: self, args, kwargs
- node = s.current_node
- args = wrapped_args[1]
- kwargs = wrapped_args[2]
- if want_subproject:
- if hasattr(s, 'subproject'):
- subproject = s.subproject
- elif hasattr(s, 'interpreter'):
- subproject = s.interpreter.subproject
- elif n == 4:
- # Meson functions have 4 args: self, node, args, kwargs
- # Module functions have 4 args: self, state, args, kwargs
- if isinstance(s, InterpreterBase):
- node = wrapped_args[1]
- else:
- node = wrapped_args[1].current_node
- args = wrapped_args[2]
- kwargs = wrapped_args[3]
- if want_subproject:
- if isinstance(s, InterpreterBase):
- subproject = s.subproject
- else:
- subproject = wrapped_args[1].subproject
- else:
- raise AssertionError(f'Unknown args: {wrapped_args!r}')
- # Sometimes interpreter methods are called internally with None instead of
- # empty list/dict
- args = args if args is not None else []
- kwargs = kwargs if kwargs is not None else {}
- return s, node, args, kwargs, subproject
-
-def flatten(args: T.Union[TYPE_nvar, T.List[TYPE_nvar]]) -> T.List[TYPE_nvar]:
- if isinstance(args, mparser.StringNode):
- assert isinstance(args.value, str)
- return [args.value]
- if not isinstance(args, collections.abc.Sequence):
- return [args]
- result = [] # type: T.List[TYPE_nvar]
- for a in args:
- if isinstance(a, list):
- rest = flatten(a)
- result = result + rest
- elif isinstance(a, mparser.StringNode):
- result.append(a.value)
- else:
- result.append(a)
- return result
-
def noPosargs(f: TV_func) -> TV_func:
@wraps(f)
def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
- args = _get_callee_args(wrapped_args)[2]
+ args = get_callee_args(wrapped_args)[2]
if args:
raise InvalidArguments('Function does not take positional arguments.')
return f(*wrapped_args, **wrapped_kwargs)
@@ -199,7 +79,7 @@ def builtinMethodNoKwargs(f: TV_func) -> TV_func:
def noKwargs(f: TV_func) -> TV_func:
@wraps(f)
def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
- kwargs = _get_callee_args(wrapped_args)[3]
+ kwargs = get_callee_args(wrapped_args)[3]
if kwargs:
raise InvalidArguments('Function does not take keyword arguments.')
return f(*wrapped_args, **wrapped_kwargs)
@@ -208,7 +88,7 @@ def noKwargs(f: TV_func) -> TV_func:
def stringArgs(f: TV_func) -> TV_func:
@wraps(f)
def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
- args = _get_callee_args(wrapped_args)[2]
+ args = get_callee_args(wrapped_args)[2]
assert(isinstance(args, list))
check_stringlist(args)
return f(*wrapped_args, **wrapped_kwargs)
@@ -221,7 +101,7 @@ def noArgsFlattening(f: TV_func) -> TV_func:
def disablerIfNotFound(f: TV_func) -> TV_func:
@wraps(f)
def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
- kwargs = _get_callee_args(wrapped_args)[3]
+ kwargs = get_callee_args(wrapped_args)[3]
disabler = kwargs.pop('disabler', False)
ret = f(*wrapped_args, **wrapped_kwargs)
if disabler and not ret.held_object.found():
@@ -237,7 +117,7 @@ class permittedKwargs:
def __call__(self, f: TV_func) -> TV_func:
@wraps(f)
def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
- s, node, args, kwargs, _ = _get_callee_args(wrapped_args)
+ s, node, args, kwargs, _ = get_callee_args(wrapped_args)
for k in kwargs:
if k not in self.permitted:
mlog.warning(f'''Passed invalid keyword argument "{k}".''', location=node)
@@ -298,7 +178,7 @@ def typed_pos_args(name: str, *types: T.Union[T.Type, T.Tuple[T.Type, ...]],
@wraps(f)
def wrapper(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
- args = _get_callee_args(wrapped_args)[2]
+ args = get_callee_args(wrapped_args)[2]
# These are implementation programming errors, end users should never see them.
assert isinstance(args, list), args
@@ -480,7 +360,7 @@ def typed_kwargs(name: str, *types: KwargInfo) -> T.Callable[..., T.Any]:
@wraps(f)
def wrapper(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
- kwargs, subproject = _get_callee_args(wrapped_args, want_subproject=True)[3:5]
+ kwargs, subproject = get_callee_args(wrapped_args, want_subproject=True)[3:5]
all_names = {t.name for t in types}
unknowns = set(kwargs).difference(all_names)
@@ -606,7 +486,7 @@ class FeatureCheckBase(metaclass=abc.ABCMeta):
def __call__(self, f: TV_func) -> TV_func:
@wraps(f)
def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
- subproject = _get_callee_args(wrapped_args, want_subproject=True)[4]
+ subproject = get_callee_args(wrapped_args, want_subproject=True)[4]
if subproject is None:
raise AssertionError(f'{wrapped_args!r}')
self.use(subproject)
@@ -693,7 +573,7 @@ class FeatureCheckKwargsBase(metaclass=abc.ABCMeta):
def __call__(self, f: TV_func) -> TV_func:
@wraps(f)
def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
- kwargs, subproject = _get_callee_args(wrapped_args, want_subproject=True)[3:5]
+ kwargs, subproject = get_callee_args(wrapped_args, want_subproject=True)[3:5]
if subproject is None:
raise AssertionError(f'{wrapped_args!r}')
for arg in self.kwargs:
@@ -712,10 +592,6 @@ class FeatureDeprecatedKwargs(FeatureCheckKwargsBase):
feature_check_class = FeatureDeprecated
-class MutableInterpreterObject(InterpreterObject):
- def __init__(self) -> None:
- super().__init__()
-
class Disabler(InterpreterObject):
def __init__(self) -> None:
super().__init__()
@@ -745,11 +621,6 @@ def is_disabled(args: T.Sequence[T.Any], kwargs: T.Dict[str, T.Any]) -> bool:
return True
return False
-def default_resolve_key(key: mparser.BaseNode) -> str:
- if not isinstance(key, mparser.IdNode):
- raise InterpreterException('Invalid kwargs format.')
- return key.value
-
class InterpreterBase:
elementary_types = (int, float, str, bool, list)