typing: completely type interpreterbase

pull/7657/head
Daniel Mensinger 4 years ago
parent 3489442848
commit ff28f3fa34
No known key found for this signature in database
GPG Key ID: 54DD94C131E277D4
  1. 132
      mesonbuild/interpreterbase.py

@ -24,14 +24,31 @@ import collections.abc
from functools import wraps
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):
self.methods = {} # type: T.Dict[str, T.Callable]
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: T.List[T.Union[mparser.BaseNode, str, int, float, bool, list, dict, 'InterpreterObject', 'ObjectHolder']], kwargs: T.Dict[str, T.Union[mparser.BaseNode, str, int, float, bool, list, dict, 'InterpreterObject', 'ObjectHolder']]):
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):
@ -42,18 +59,13 @@ class InterpreterObject:
TV_InterpreterObject = T.TypeVar('TV_InterpreterObject')
class ObjectHolder(T.Generic[TV_InterpreterObject]):
def __init__(self, obj: InterpreterObject, subproject: T.Optional[str] = None):
def __init__(self, obj: InterpreterObject, subproject: T.Optional[str] = None) -> None:
self.held_object = obj # type: InterpreterObject
self.subproject = subproject # type: str
def __repr__(self):
def __repr__(self) -> str:
return '<Holder: {!r}>'.format(self.held_object)
TYPE_elementary = T.Union[str, int, float, bool]
TYPE_var = T.Union[TYPE_elementary, list, dict, InterpreterObject, ObjectHolder]
TYPE_nvar = T.Union[TYPE_var, mparser.BaseNode]
TYPE_nkwargs = T.Dict[T.Union[mparser.BaseNode, str], TYPE_nvar]
class MesonVersionString(str):
pass
@ -67,11 +79,11 @@ def check_stringlist(a: T.Any, msg: str = 'Arguments must be strings.') -> None:
mlog.debug('Element not a string:', str(a))
raise InvalidArguments(msg)
def _get_callee_args(wrapped_args, want_subproject: bool = False):
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
subproject = None # type: T.Optional[str]
if want_subproject and n == 2:
if hasattr(s, 'subproject'):
# Interpreter base types have 2 args: self, node
@ -145,18 +157,18 @@ def flatten(args: T.Union[TYPE_nvar, T.List[TYPE_nvar]]) -> T.List[TYPE_nvar]:
result.append(a)
return result
def noPosargs(f):
def noPosargs(f: TV_func) -> TV_func:
@wraps(f)
def wrapped(*wrapped_args, **wrapped_kwargs):
def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
args = _get_callee_args(wrapped_args)[2]
if args:
raise InvalidArguments('Function does not take positional arguments.')
return f(*wrapped_args, **wrapped_kwargs)
return wrapped
return T.cast(TV_func, wrapped)
def builtinMethodNoKwargs(f):
def builtinMethodNoKwargs(f: TV_func) -> TV_func:
@wraps(f)
def wrapped(*wrapped_args, **wrapped_kwargs):
def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
node = wrapped_args[0].current_node
method_name = wrapped_args[2]
kwargs = wrapped_args[4]
@ -165,56 +177,56 @@ def builtinMethodNoKwargs(f):
'This will become a hard error in the future',
location=node)
return f(*wrapped_args, **wrapped_kwargs)
return wrapped
return T.cast(TV_func, wrapped)
def noKwargs(f):
def noKwargs(f: TV_func) -> TV_func:
@wraps(f)
def wrapped(*wrapped_args, **wrapped_kwargs):
def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
kwargs = _get_callee_args(wrapped_args)[3]
if kwargs:
raise InvalidArguments('Function does not take keyword arguments.')
return f(*wrapped_args, **wrapped_kwargs)
return wrapped
return T.cast(TV_func, wrapped)
def stringArgs(f):
def stringArgs(f: TV_func) -> TV_func:
@wraps(f)
def wrapped(*wrapped_args, **wrapped_kwargs):
def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
args = _get_callee_args(wrapped_args)[2]
assert(isinstance(args, list))
check_stringlist(args)
return f(*wrapped_args, **wrapped_kwargs)
return wrapped
return T.cast(TV_func, wrapped)
def noArgsFlattening(f):
def noArgsFlattening(f: TV_func) -> TV_func:
setattr(f, 'no-args-flattening', True) # noqa: B010
return f
def disablerIfNotFound(f):
def disablerIfNotFound(f: TV_func) -> TV_func:
@wraps(f)
def wrapped(*wrapped_args, **wrapped_kwargs):
def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
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():
return Disabler()
return ret
return wrapped
return T.cast(TV_func, wrapped)
class permittedKwargs:
def __init__(self, permitted: T.Set[str]):
self.permitted = permitted # type: T.Set[str]
def __call__(self, f):
def __call__(self, f: TV_func) -> TV_func:
@wraps(f)
def wrapped(*wrapped_args, **wrapped_kwargs):
def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
s, node, args, kwargs, _ = _get_callee_args(wrapped_args)
for k in kwargs:
if k not in self.permitted:
mlog.warning('''Passed invalid keyword argument "{}".'''.format(k), location=node)
mlog.warning('This will become a hard error in the future.')
return f(*wrapped_args, **wrapped_kwargs)
return wrapped
return T.cast(TV_func, wrapped)
class FeatureCheckBase(metaclass=abc.ABCMeta):
"Base class for feature version checks"
@ -233,7 +245,7 @@ class FeatureCheckBase(metaclass=abc.ABCMeta):
# Don't do any checks if project() has not been parsed yet
if subproject not in mesonlib.project_meson_versions:
return ''
return mesonlib.project_meson_versions[subproject]
return T.cast(str, mesonlib.project_meson_versions[subproject]) # TODO: remove type cast when fully typing mesonlib
@staticmethod
@abc.abstractmethod
@ -279,15 +291,15 @@ class FeatureCheckBase(metaclass=abc.ABCMeta):
def get_warning_str_prefix(tv: str) -> str:
raise InterpreterException('get_warning_str_prefix not implemented')
def __call__(self, f):
def __call__(self, f: TV_func) -> TV_func:
@wraps(f)
def wrapped(*wrapped_args, **wrapped_kwargs):
def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
subproject = _get_callee_args(wrapped_args, want_subproject=True)[4]
if subproject is None:
raise AssertionError('{!r}'.format(wrapped_args))
self.use(subproject)
return f(*wrapped_args, **wrapped_kwargs)
return wrapped
return T.cast(TV_func, wrapped)
@classmethod
def single_use(cls, feature_name: str, version: str, subproject: str,
@ -306,7 +318,7 @@ class FeatureNew(FeatureCheckBase):
@staticmethod
def check_version(target_version: str, feature_version: str) -> bool:
return mesonlib.version_compare_condition_with_min(target_version, feature_version)
return T.cast(bool, mesonlib.version_compare_condition_with_min(target_version, feature_version)) # TODO: remove once mesonlib is annotated
@staticmethod
def get_warning_str_prefix(tv: str) -> str:
@ -366,9 +378,9 @@ class FeatureCheckKwargsBase(metaclass=abc.ABCMeta):
self.kwargs = kwargs
self.extra_message = extra_message
def __call__(self, f):
def __call__(self, f: TV_func) -> TV_func:
@wraps(f)
def wrapped(*wrapped_args, **wrapped_kwargs):
def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
kwargs, subproject = _get_callee_args(wrapped_args, want_subproject=True)[3:5]
if subproject is None:
raise AssertionError('{!r}'.format(wrapped_args))
@ -379,7 +391,7 @@ class FeatureCheckKwargsBase(metaclass=abc.ABCMeta):
self.feature_check_class.single_use(
name, self.feature_version, subproject, self.extra_message)
return f(*wrapped_args, **wrapped_kwargs)
return wrapped
return T.cast(TV_func, wrapped)
class FeatureNewKwargs(FeatureCheckKwargsBase):
feature_check_class = FeatureNew
@ -407,21 +419,21 @@ class BreakRequest(BaseException):
pass
class MutableInterpreterObject(InterpreterObject):
def __init__(self):
def __init__(self) -> None:
super().__init__()
class Disabler(InterpreterObject):
def __init__(self):
def __init__(self) -> None:
super().__init__()
self.methods.update({'found': self.found_method})
def found_method(self, args, kwargs):
def found_method(self, args: T.Sequence[T.Any], kwargs: T.Dict[str, T.Any]) -> bool:
return False
def is_disabler(i) -> bool:
def is_disabler(i: T.Any) -> bool:
return isinstance(i, Disabler)
def is_arg_disabled(arg) -> bool:
def is_arg_disabled(arg: T.Any) -> bool:
if is_disabler(arg):
return True
if isinstance(arg, list):
@ -430,7 +442,7 @@ def is_arg_disabled(arg) -> bool:
return True
return False
def is_disabled(args, kwargs) -> bool:
def is_disabled(args: T.Sequence[T.Any], kwargs: T.Dict[str, T.Any]) -> bool:
for i in args:
if is_arg_disabled(i):
return True
@ -540,9 +552,9 @@ class InterpreterBase:
elif isinstance(cur, mparser.MethodNode):
return self.method_call(cur)
elif isinstance(cur, mparser.StringNode):
return cur.value
return T.cast(str, cur.value)
elif isinstance(cur, mparser.BooleanNode):
return cur.value
return T.cast(bool, cur.value)
elif isinstance(cur, mparser.IfClauseNode):
return self.evaluate_if(cur)
elif isinstance(cur, mparser.IdNode):
@ -554,7 +566,7 @@ class InterpreterBase:
elif isinstance(cur, mparser.DictNode):
return self.evaluate_dictstatement(cur)
elif isinstance(cur, mparser.NumberNode):
return cur.value
return T.cast(int, cur.value)
elif isinstance(cur, mparser.AndNode):
return self.evaluate_andstatement(cur)
elif isinstance(cur, mparser.OrNode):
@ -721,7 +733,7 @@ The result of this is undefined and will become a hard error in a future Meson r
raise InterpreterException('Second argument to "or" is not a boolean.')
return r
def evaluate_uminusstatement(self, cur) -> T.Union[int, Disabler]:
def evaluate_uminusstatement(self, cur: mparser.UMinusNode) -> T.Union[int, Disabler]:
v = self.evaluate_statement(cur.value)
if isinstance(v, Disabler):
return v
@ -867,7 +879,7 @@ The result of this is undefined and will become a hard error in a future Meson r
if not isinstance(index, str):
raise InterpreterException('Key is not a string')
try:
return iobject[index]
return T.cast(TYPE_var, iobject[index])
except KeyError:
raise InterpreterException('Key %s is not in dict' % index)
else:
@ -892,7 +904,7 @@ The result of this is undefined and will become a hard error in a future Meson r
func_args = posargs # type: T.Any
if not getattr(func, 'no-args-flattening', False):
func_args = flatten(posargs)
return func(node, func_args, self.kwargs_string_keys(kwargs))
return func(node, func_args, kwargs)
else:
self.unknown_function_called(func_name)
return None
@ -934,7 +946,7 @@ The result of this is undefined and will become a hard error in a future Meson r
raise InvalidArguments('Invalid operation "extract_objects" on variable "{}"'.format(object_name))
self.validate_extraction(obj.held_object)
obj.current_node = node
return obj.method_call(method_name, args, self.kwargs_string_keys(kwargs))
return obj.method_call(method_name, args, kwargs)
@builtinMethodNoKwargs
def bool_method_call(self, obj: bool, method_name: str, posargs: T.List[TYPE_nvar], kwargs: T.Dict[str, T.Any]) -> T.Union[str, int]:
@ -1067,7 +1079,7 @@ The result of this is undefined and will become a hard error in a future Meson r
arg = str(arg).lower()
arg_strings.append(str(arg))
def arg_replace(match):
def arg_replace(match: T.Match[str]) -> str:
idx = int(match.group(1))
if idx >= len(arg_strings):
raise InterpreterException('Format placeholder @{}@ out of range.'.format(idx))
@ -1115,12 +1127,12 @@ The result of this is undefined and will become a hard error in a future Meson r
if isinstance(fallback, mparser.BaseNode):
return self.evaluate_statement(fallback)
return fallback
return obj[index]
return T.cast(TYPE_var, obj[index])
m = 'Arrays do not have a method called {!r}.'
raise InterpreterException(m.format(method_name))
@builtinMethodNoKwargs
def dict_method_call(self, obj: dict, method_name: str, posargs: T.List[TYPE_nvar], kwargs: T.Dict[str, T.Any]) -> TYPE_var:
def dict_method_call(self, obj: T.Dict[str, TYPE_var], method_name: str, posargs: T.List[TYPE_nvar], kwargs: T.Dict[str, T.Any]) -> TYPE_var:
if method_name in ('has_key', 'get'):
if method_name == 'has_key':
if len(posargs) != 1:
@ -1194,14 +1206,6 @@ The result of this is undefined and will become a hard error in a future Meson r
kwargs[k] = v
return kwargs
def kwargs_string_keys(self, kwargs: TYPE_nkwargs) -> T.Dict[str, TYPE_nvar]:
kw = {} # type: T.Dict[str, TYPE_nvar]
for key, val in kwargs.items():
if not isinstance(key, str):
raise InterpreterException('Key of kwargs is not a string')
kw[key] = val
return kw
def assignment(self, node: mparser.AssignmentNode) -> None:
assert(isinstance(node, mparser.AssignmentNode))
if self.argument_depth != 0:
@ -1232,7 +1236,7 @@ To specify a keyword argument, use : instead of =.''')
raise InvalidCode('Tried to overwrite internal variable "%s"' % varname)
self.variables[varname] = variable
def get_variable(self, varname) -> TYPE_var:
def get_variable(self, varname: str) -> TYPE_var:
if varname in self.builtin:
return self.builtin[varname]
if varname in self.variables:

Loading…
Cancel
Save