types: Annotate ast/interpreter.py

pull/6316/head
Daniel Mensinger 5 years ago
parent a75255bc4c
commit c48b0dea27
No known key found for this signature in database
GPG Key ID: 54DD94C131E277D4
  1. 84
      mesonbuild/ast/interpreter.py
  2. 7
      mesonbuild/ast/introspection.py
  3. 3
      mesonbuild/interpreter.py
  4. 4
      mesonbuild/interpreterbase.py
  5. 6
      mesonbuild/rewriter.py
  6. 2
      run_unittests.py

@ -21,18 +21,27 @@ from .. import environment
from ..interpreterbase import InvalidArguments, BreakRequest, ContinueRequest from ..interpreterbase import InvalidArguments, BreakRequest, ContinueRequest
from ..mparser import ( from ..mparser import (
AndNode,
ArgumentNode, ArgumentNode,
ArithmeticNode, ArithmeticNode,
ArrayNode, ArrayNode,
AssignmentNode, AssignmentNode,
BaseNode, BaseNode,
ComparisonNode,
ElementaryNode, ElementaryNode,
EmptyNode, EmptyNode,
ForeachClauseNode,
IdNode, IdNode,
StringNode, IfClauseNode,
IfNode,
IndexNode,
MethodNode, MethodNode,
NotNode,
OrNode,
PlusAssignmentNode, PlusAssignmentNode,
StringNode,
TernaryNode, TernaryNode,
UMinusNode,
) )
import os, sys import os, sys
@ -60,13 +69,13 @@ ADD_SOURCE = 0
REMOVE_SOURCE = 1 REMOVE_SOURCE = 1
class AstInterpreter(interpreterbase.InterpreterBase): class AstInterpreter(interpreterbase.InterpreterBase):
def __init__(self, source_root: str, subdir: str, visitors: T.Optional[T.List[AstVisitor]] = None): def __init__(self, source_root: str, subdir: str, subproject: str, visitors: T.Optional[T.List[AstVisitor]] = None) -> None:
super().__init__(source_root, subdir) super().__init__(source_root, subdir, subproject)
self.visitors = visitors if visitors is not None else [] self.visitors = visitors if visitors is not None else []
self.visited_subdirs = {} self.visited_subdirs = {} # type: T.Dict[str, bool]
self.assignments = {} self.assignments = {} # type: T.Dict[str, BaseNode]
self.assign_vals = {} self.assign_vals = {} # type: T.Dict[str, T.Any]
self.reverse_assignment = {} self.reverse_assignment = {} # type: T.Dict[str, BaseNode]
self.funcs.update({'project': self.func_do_nothing, self.funcs.update({'project': self.func_do_nothing,
'test': self.func_do_nothing, 'test': self.func_do_nothing,
'benchmark': self.func_do_nothing, 'benchmark': self.func_do_nothing,
@ -123,21 +132,21 @@ class AstInterpreter(interpreterbase.InterpreterBase):
'summary': self.func_do_nothing, 'summary': self.func_do_nothing,
}) })
def func_do_nothing(self, node, args, kwargs): def func_do_nothing(self, node: BaseNode, args: T.List[T.Union[BaseNode, str, int, float, bool]], kwargs: T.Dict[str, T.Union[BaseNode, str, int, float, bool]]) -> bool:
return True return True
def load_root_meson_file(self): def load_root_meson_file(self) -> None:
super().load_root_meson_file() super().load_root_meson_file()
for i in self.visitors: for i in self.visitors:
self.ast.accept(i) self.ast.accept(i)
def func_subdir(self, node, args, kwargs): def func_subdir(self, node: BaseNode, args: T.List[T.Union[BaseNode, str, int, float, bool]], kwargs: T.Dict[str, T.Union[BaseNode, str, int, float, bool]]) -> None:
args = self.flatten_args(args) args = self.flatten_args(args)
if len(args) != 1 or not isinstance(args[0], str): if len(args) != 1 or not isinstance(args[0], str):
sys.stderr.write('Unable to evaluate subdir({}) in AstInterpreter --> Skipping\n'.format(args)) sys.stderr.write('Unable to evaluate subdir({}) in AstInterpreter --> Skipping\n'.format(args))
return return
prev_subdir = self.subdir prev_subdir = self.subdir # type: str
subdir = os.path.join(prev_subdir, args[0]) subdir = os.path.join(prev_subdir, args[0])
absdir = os.path.join(self.source_root, subdir) absdir = os.path.join(self.source_root, subdir)
buildfilename = os.path.join(subdir, environment.build_filename) buildfilename = os.path.join(subdir, environment.build_filename)
@ -166,41 +175,39 @@ class AstInterpreter(interpreterbase.InterpreterBase):
self.evaluate_codeblock(codeblock) self.evaluate_codeblock(codeblock)
self.subdir = prev_subdir self.subdir = prev_subdir
def method_call(self, node): def method_call(self, node: BaseNode) -> bool:
return True return True
def evaluate_arithmeticstatement(self, cur): def evaluate_arithmeticstatement(self, cur: ArithmeticNode) -> int:
self.evaluate_statement(cur.left) self.evaluate_statement(cur.left)
self.evaluate_statement(cur.right) self.evaluate_statement(cur.right)
return 0 return 0
def evaluate_uminusstatement(self, cur): def evaluate_uminusstatement(self, cur: UMinusNode) -> int:
self.evaluate_statement(cur.value) self.evaluate_statement(cur.value)
return 0 return 0
def evaluate_ternary(self, node): def evaluate_ternary(self, node: TernaryNode) -> None:
assert(isinstance(node, TernaryNode)) assert(isinstance(node, TernaryNode))
self.evaluate_statement(node.condition) self.evaluate_statement(node.condition)
self.evaluate_statement(node.trueblock) self.evaluate_statement(node.trueblock)
self.evaluate_statement(node.falseblock) self.evaluate_statement(node.falseblock)
def evaluate_plusassign(self, node): def evaluate_plusassign(self, node: PlusAssignmentNode) -> None:
assert(isinstance(node, PlusAssignmentNode)) assert(isinstance(node, PlusAssignmentNode))
if node.var_name not in self.assignments: # Cheat by doing a reassignment
self.assignments[node.var_name] = [] self.assignments[node.var_name] = node.value # Save a reference to the value node
self.assign_vals[node.var_name] = []
self.assignments[node.var_name] += [node.value] # Save a reference to the value node
if node.value.ast_id: if node.value.ast_id:
self.reverse_assignment[node.value.ast_id] = node self.reverse_assignment[node.value.ast_id] = node
self.assign_vals[node.var_name] += [self.evaluate_statement(node.value)] self.assign_vals[node.var_name] = self.evaluate_statement(node.value)
def evaluate_indexing(self, node): def evaluate_indexing(self, node: IndexNode) -> int:
return 0 return 0
def unknown_function_called(self, func_name): def unknown_function_called(self, func_name: str) -> None:
pass pass
def reduce_arguments(self, args): def reduce_arguments(self, args: ArgumentNode, resolve_key_nodes: bool = True) -> T.Tuple[list, dict]:
if isinstance(args, ArgumentNode): if isinstance(args, ArgumentNode):
kwargs = {} # type: T.Dict[T.Union[str, BaseNode], BaseNode] kwargs = {} # type: T.Dict[T.Union[str, BaseNode], BaseNode]
for key, val in args.kwargs.items(): for key, val in args.kwargs.items():
@ -215,22 +222,22 @@ class AstInterpreter(interpreterbase.InterpreterBase):
else: else:
return self.flatten_args(args), {} return self.flatten_args(args), {}
def evaluate_comparison(self, node): def evaluate_comparison(self, node: ComparisonNode) -> bool:
self.evaluate_statement(node.left) self.evaluate_statement(node.left)
self.evaluate_statement(node.right) self.evaluate_statement(node.right)
return False return False
def evaluate_andstatement(self, cur): def evaluate_andstatement(self, cur: AndNode) -> bool:
self.evaluate_statement(cur.left) self.evaluate_statement(cur.left)
self.evaluate_statement(cur.right) self.evaluate_statement(cur.right)
return False return False
def evaluate_orstatement(self, cur): def evaluate_orstatement(self, cur: OrNode) -> bool:
self.evaluate_statement(cur.left) self.evaluate_statement(cur.left)
self.evaluate_statement(cur.right) self.evaluate_statement(cur.right)
return False return False
def evaluate_foreach(self, node): def evaluate_foreach(self, node: ForeachClauseNode) -> None:
try: try:
self.evaluate_codeblock(node.block) self.evaluate_codeblock(node.block)
except ContinueRequest: except ContinueRequest:
@ -238,30 +245,31 @@ class AstInterpreter(interpreterbase.InterpreterBase):
except BreakRequest: except BreakRequest:
pass pass
def evaluate_if(self, node): def evaluate_if(self, node: IfClauseNode) -> None:
for i in node.ifs: for i in node.ifs:
self.evaluate_codeblock(i.block) self.evaluate_codeblock(i.block)
if not isinstance(node.elseblock, EmptyNode): if not isinstance(node.elseblock, EmptyNode):
self.evaluate_codeblock(node.elseblock) self.evaluate_codeblock(node.elseblock)
def get_variable(self, varname): def get_variable(self, varname: str) -> int:
return 0 return 0
def assignment(self, node): def assignment(self, node: AssignmentNode) -> None:
assert(isinstance(node, AssignmentNode)) assert(isinstance(node, AssignmentNode))
self.assignments[node.var_name] = [node.value] # Save a reference to the value node self.assignments[node.var_name] = node.value # Save a reference to the value node
if node.value.ast_id: if node.value.ast_id:
self.reverse_assignment[node.value.ast_id] = node self.reverse_assignment[node.value.ast_id] = node
self.assign_vals[node.var_name] = [self.evaluate_statement(node.value)] # Evaluate the value just in case self.assign_vals[node.var_name] = self.evaluate_statement(node.value) # Evaluate the value just in case
def resolve_node(self, node: BaseNode, include_unknown_args: bool = False, id_loop_detect: T.Optional[T.List[str]] = None) -> T.Optional[T.Any]: def resolve_node(self, node: BaseNode, include_unknown_args: bool = False, id_loop_detect: T.Optional[T.List[str]] = None) -> T.Optional[T.Any]:
def quick_resolve(n: BaseNode, loop_detect: T.Optional[T.List[str]] = None) -> T.Any: def quick_resolve(n: BaseNode, loop_detect: T.Optional[T.List[str]] = None) -> T.Any:
if loop_detect is None: if loop_detect is None:
loop_detect = [] loop_detect = []
if isinstance(n, IdNode): if isinstance(n, IdNode):
assert isinstance(n.value, str)
if n.value in loop_detect or n.value not in self.assignments: if n.value in loop_detect or n.value not in self.assignments:
return [] return []
return quick_resolve(self.assignments[n.value][0], loop_detect = loop_detect + [n.value]) return quick_resolve(self.assignments[n.value], loop_detect = loop_detect + [n.value])
elif isinstance(n, ElementaryNode): elif isinstance(n, ElementaryNode):
return n.value return n.value
else: else:
@ -323,7 +331,7 @@ class AstInterpreter(interpreterbase.InterpreterBase):
if isinstance(result, BaseNode): if isinstance(result, BaseNode):
result = self.resolve_node(result, include_unknown_args, id_loop_detect) result = self.resolve_node(result, include_unknown_args, id_loop_detect)
elif isinstance(result, list): elif isinstance(result, list):
new_res = [] new_res = [] # type: T.List[T.Union[BaseNode, str, bool, int, float]]
for i in result: for i in result:
if isinstance(i, BaseNode): if isinstance(i, BaseNode):
resolved = self.resolve_node(i, include_unknown_args, id_loop_detect) resolved = self.resolve_node(i, include_unknown_args, id_loop_detect)
@ -335,12 +343,12 @@ class AstInterpreter(interpreterbase.InterpreterBase):
return result return result
def flatten_args(self, args: T.Any, include_unknown_args: bool = False, id_loop_detect: T.Optional[T.List[str]] = None) -> T.List[T.Any]: def flatten_args(self, args: T.Any, include_unknown_args: bool = False, id_loop_detect: T.Optional[T.List[str]] = None) -> T.List[T.Union[BaseNode, str, bool, int, float]]:
# Make sure we are always dealing with lists # Make sure we are always dealing with lists
if not isinstance(args, list): if not isinstance(args, list):
args = [args] args = [args]
flattend_args = [] flattend_args = [] # type: T.List[T.Union[BaseNode, str, bool, int, float]]
# Resolve the contents of args # Resolve the contents of args
for i in args: for i in args:
@ -354,7 +362,7 @@ class AstInterpreter(interpreterbase.InterpreterBase):
flattend_args += [i] flattend_args += [i]
return flattend_args return flattend_args
def flatten_kwargs(self, kwargs: object, include_unknown_args: bool = False): def flatten_kwargs(self, kwargs: T.Dict[str, T.Union[BaseNode, str, bool, int, float]], include_unknown_args: bool = False) -> T.Dict[str, T.Union[BaseNode, str, bool, int, float]]:
flattend_kwargs = {} flattend_kwargs = {}
for key, val in kwargs.items(): for key, val in kwargs.items():
if isinstance(val, BaseNode): if isinstance(val, BaseNode):

@ -38,7 +38,7 @@ class IntrospectionInterpreter(AstInterpreter):
# Most of the code is stolen from interpreter.Interpreter # Most of the code is stolen from interpreter.Interpreter
def __init__(self, source_root, subdir, backend, visitors=None, cross_file=None, subproject='', subproject_dir='subprojects', env=None): def __init__(self, source_root, subdir, backend, visitors=None, cross_file=None, subproject='', subproject_dir='subprojects', env=None):
visitors = visitors if visitors is not None else [] visitors = visitors if visitors is not None else []
super().__init__(source_root, subdir, visitors=visitors) super().__init__(source_root, subdir, subproject, visitors=visitors)
options = IntrospectionHelper(cross_file) options = IntrospectionHelper(cross_file)
self.cross_file = cross_file self.cross_file = cross_file
@ -46,7 +46,6 @@ class IntrospectionInterpreter(AstInterpreter):
self.environment = environment.Environment(source_root, None, options) self.environment = environment.Environment(source_root, None, options)
else: else:
self.environment = env self.environment = env
self.subproject = subproject
self.subproject_dir = subproject_dir self.subproject_dir = subproject_dir
self.coredata = self.environment.get_coredata() self.coredata = self.environment.get_coredata()
self.option_file = os.path.join(self.source_root, self.subdir, 'meson_options.txt') self.option_file = os.path.join(self.source_root, self.subdir, 'meson_options.txt')
@ -183,8 +182,8 @@ class IntrospectionInterpreter(AstInterpreter):
elif isinstance(curr, IdNode): elif isinstance(curr, IdNode):
# Try to resolve the ID and append the node to the queue # Try to resolve the ID and append the node to the queue
var_name = curr.value var_name = curr.value
if var_name in self.assignments and self.assignments[var_name]: if var_name in self.assignments:
tmp_node = self.assignments[var_name][0] tmp_node = self.assignments[var_name]
if isinstance(tmp_node, (ArrayNode, IdNode, FunctionNode)): if isinstance(tmp_node, (ArrayNode, IdNode, FunctionNode)):
srcqueue += [tmp_node] srcqueue += [tmp_node]
elif isinstance(curr, ArithmeticNode): elif isinstance(curr, ArithmeticNode):

@ -2182,13 +2182,12 @@ class Interpreter(InterpreterBase):
def __init__(self, build, backend=None, subproject='', subdir='', subproject_dir='subprojects', def __init__(self, build, backend=None, subproject='', subdir='', subproject_dir='subprojects',
modules = None, default_project_options=None, mock=False, ast=None): modules = None, default_project_options=None, mock=False, ast=None):
super().__init__(build.environment.get_source_dir(), subdir) super().__init__(build.environment.get_source_dir(), subdir, subproject)
self.an_unpicklable_object = mesonlib.an_unpicklable_object self.an_unpicklable_object = mesonlib.an_unpicklable_object
self.build = build self.build = build
self.environment = build.environment self.environment = build.environment
self.coredata = self.environment.get_coredata() self.coredata = self.environment.get_coredata()
self.backend = backend self.backend = backend
self.subproject = subproject
self.summary = {} self.summary = {}
if modules is None: if modules is None:
self.modules = {} self.modules = {}

@ -360,12 +360,12 @@ def is_disabled(args, kwargs) -> bool:
return False return False
class InterpreterBase: class InterpreterBase:
def __init__(self, source_root, subdir): def __init__(self, source_root: str, subdir: str, subproject: str) -> None:
self.source_root = source_root self.source_root = source_root
self.funcs = {} self.funcs = {}
self.builtin = {} self.builtin = {}
self.subdir = subdir self.subdir = subdir
self.variables = {} self.subproject = subproject
self.argument_depth = 0 self.argument_depth = 0
self.current_lineno = -1 self.current_lineno = -1
# Current node set during a function call. This can be used as location # Current node set during a function call. This can be used as location

@ -414,10 +414,10 @@ class Rewriter:
# Check the assignments # Check the assignments
tgt = None tgt = None
if target in self.interpreter.assignments: if target in self.interpreter.assignments:
node = self.interpreter.assignments[target][0] node = self.interpreter.assignments[target]
if isinstance(node, FunctionNode): if isinstance(node, FunctionNode):
if node.func_name in ['executable', 'jar', 'library', 'shared_library', 'shared_module', 'static_library', 'both_libraries']: if node.func_name in ['executable', 'jar', 'library', 'shared_library', 'shared_module', 'static_library', 'both_libraries']:
tgt = self.interpreter.assign_vals[target][0] tgt = self.interpreter.assign_vals[target]
return tgt return tgt
@ -434,7 +434,7 @@ class Rewriter:
# Check the assignments # Check the assignments
if dependency in self.interpreter.assignments: if dependency in self.interpreter.assignments:
node = self.interpreter.assignments[dependency][0] node = self.interpreter.assignments[dependency]
if isinstance(node, FunctionNode): if isinstance(node, FunctionNode):
if node.func_name in ['dependency']: if node.func_name in ['dependency']:
name = self.interpreter.flatten_args(node.args)[0] name = self.interpreter.flatten_args(node.args)[0]

@ -1398,7 +1398,7 @@ class DataTests(unittest.TestCase):
''' '''
env = get_fake_env() env = get_fake_env()
interp = Interpreter(FakeBuild(env), mock=True) interp = Interpreter(FakeBuild(env), mock=True)
astint = AstInterpreter('.', '') astint = AstInterpreter('.', '', '')
self.assertEqual(set(interp.funcs.keys()), set(astint.funcs.keys())) self.assertEqual(set(interp.funcs.keys()), set(astint.funcs.keys()))

Loading…
Cancel
Save