From c48b0dea275a4a4aa80d4fd2700e82c230f27467 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sat, 7 Dec 2019 20:48:44 +0100 Subject: [PATCH] types: Annotate ast/interpreter.py --- mesonbuild/ast/interpreter.py | 84 ++++++++++++++++++--------------- mesonbuild/ast/introspection.py | 7 ++- mesonbuild/interpreter.py | 3 +- mesonbuild/interpreterbase.py | 4 +- mesonbuild/rewriter.py | 6 +-- run_unittests.py | 2 +- 6 files changed, 56 insertions(+), 50 deletions(-) diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py index e2f4e1230..5cf6f284a 100644 --- a/mesonbuild/ast/interpreter.py +++ b/mesonbuild/ast/interpreter.py @@ -21,18 +21,27 @@ from .. import environment from ..interpreterbase import InvalidArguments, BreakRequest, ContinueRequest from ..mparser import ( + AndNode, ArgumentNode, ArithmeticNode, ArrayNode, AssignmentNode, BaseNode, + ComparisonNode, ElementaryNode, EmptyNode, + ForeachClauseNode, IdNode, - StringNode, + IfClauseNode, + IfNode, + IndexNode, MethodNode, + NotNode, + OrNode, PlusAssignmentNode, + StringNode, TernaryNode, + UMinusNode, ) import os, sys @@ -60,13 +69,13 @@ ADD_SOURCE = 0 REMOVE_SOURCE = 1 class AstInterpreter(interpreterbase.InterpreterBase): - def __init__(self, source_root: str, subdir: str, visitors: T.Optional[T.List[AstVisitor]] = None): - super().__init__(source_root, subdir) + def __init__(self, source_root: str, subdir: str, subproject: str, visitors: T.Optional[T.List[AstVisitor]] = None) -> None: + super().__init__(source_root, subdir, subproject) self.visitors = visitors if visitors is not None else [] - self.visited_subdirs = {} - self.assignments = {} - self.assign_vals = {} - self.reverse_assignment = {} + self.visited_subdirs = {} # type: T.Dict[str, bool] + self.assignments = {} # type: T.Dict[str, BaseNode] + self.assign_vals = {} # type: T.Dict[str, T.Any] + self.reverse_assignment = {} # type: T.Dict[str, BaseNode] self.funcs.update({'project': self.func_do_nothing, 'test': self.func_do_nothing, 'benchmark': self.func_do_nothing, @@ -123,21 +132,21 @@ class AstInterpreter(interpreterbase.InterpreterBase): '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 - def load_root_meson_file(self): + def load_root_meson_file(self) -> None: super().load_root_meson_file() for i in self.visitors: 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) if len(args) != 1 or not isinstance(args[0], str): sys.stderr.write('Unable to evaluate subdir({}) in AstInterpreter --> Skipping\n'.format(args)) return - prev_subdir = self.subdir + prev_subdir = self.subdir # type: str subdir = os.path.join(prev_subdir, args[0]) absdir = os.path.join(self.source_root, subdir) buildfilename = os.path.join(subdir, environment.build_filename) @@ -166,41 +175,39 @@ class AstInterpreter(interpreterbase.InterpreterBase): self.evaluate_codeblock(codeblock) self.subdir = prev_subdir - def method_call(self, node): + def method_call(self, node: BaseNode) -> bool: return True - def evaluate_arithmeticstatement(self, cur): + def evaluate_arithmeticstatement(self, cur: ArithmeticNode) -> int: self.evaluate_statement(cur.left) self.evaluate_statement(cur.right) return 0 - def evaluate_uminusstatement(self, cur): + def evaluate_uminusstatement(self, cur: UMinusNode) -> int: self.evaluate_statement(cur.value) return 0 - def evaluate_ternary(self, node): + def evaluate_ternary(self, node: TernaryNode) -> None: assert(isinstance(node, TernaryNode)) self.evaluate_statement(node.condition) self.evaluate_statement(node.trueblock) self.evaluate_statement(node.falseblock) - def evaluate_plusassign(self, node): + def evaluate_plusassign(self, node: PlusAssignmentNode) -> None: assert(isinstance(node, PlusAssignmentNode)) - if node.var_name not in self.assignments: - self.assignments[node.var_name] = [] - self.assign_vals[node.var_name] = [] - self.assignments[node.var_name] += [node.value] # Save a reference to the value node + # Cheat by doing a reassignment + self.assignments[node.var_name] = node.value # Save a reference to the value node if node.value.ast_id: 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 - def unknown_function_called(self, func_name): + def unknown_function_called(self, func_name: str) -> None: 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): kwargs = {} # type: T.Dict[T.Union[str, BaseNode], BaseNode] for key, val in args.kwargs.items(): @@ -215,22 +222,22 @@ class AstInterpreter(interpreterbase.InterpreterBase): else: 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.right) return False - def evaluate_andstatement(self, cur): + def evaluate_andstatement(self, cur: AndNode) -> bool: self.evaluate_statement(cur.left) self.evaluate_statement(cur.right) return False - def evaluate_orstatement(self, cur): + def evaluate_orstatement(self, cur: OrNode) -> bool: self.evaluate_statement(cur.left) self.evaluate_statement(cur.right) return False - def evaluate_foreach(self, node): + def evaluate_foreach(self, node: ForeachClauseNode) -> None: try: self.evaluate_codeblock(node.block) except ContinueRequest: @@ -238,30 +245,31 @@ class AstInterpreter(interpreterbase.InterpreterBase): except BreakRequest: pass - def evaluate_if(self, node): + def evaluate_if(self, node: IfClauseNode) -> None: for i in node.ifs: self.evaluate_codeblock(i.block) if not isinstance(node.elseblock, EmptyNode): self.evaluate_codeblock(node.elseblock) - def get_variable(self, varname): + def get_variable(self, varname: str) -> int: return 0 - def assignment(self, node): + def assignment(self, node: AssignmentNode) -> None: 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: 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 quick_resolve(n: BaseNode, loop_detect: T.Optional[T.List[str]] = None) -> T.Any: if loop_detect is None: loop_detect = [] if isinstance(n, IdNode): + assert isinstance(n.value, str) if n.value in loop_detect or n.value not in self.assignments: 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): return n.value else: @@ -323,7 +331,7 @@ class AstInterpreter(interpreterbase.InterpreterBase): if isinstance(result, BaseNode): result = self.resolve_node(result, include_unknown_args, id_loop_detect) elif isinstance(result, list): - new_res = [] + new_res = [] # type: T.List[T.Union[BaseNode, str, bool, int, float]] for i in result: if isinstance(i, BaseNode): resolved = self.resolve_node(i, include_unknown_args, id_loop_detect) @@ -335,12 +343,12 @@ class AstInterpreter(interpreterbase.InterpreterBase): 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 if not isinstance(args, list): args = [args] - flattend_args = [] + flattend_args = [] # type: T.List[T.Union[BaseNode, str, bool, int, float]] # Resolve the contents of args for i in args: @@ -354,7 +362,7 @@ class AstInterpreter(interpreterbase.InterpreterBase): flattend_args += [i] 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 = {} for key, val in kwargs.items(): if isinstance(val, BaseNode): diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index b191be3ae..083725329 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -38,7 +38,7 @@ class IntrospectionInterpreter(AstInterpreter): # 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): 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) self.cross_file = cross_file @@ -46,7 +46,6 @@ class IntrospectionInterpreter(AstInterpreter): self.environment = environment.Environment(source_root, None, options) else: self.environment = env - self.subproject = subproject self.subproject_dir = subproject_dir self.coredata = self.environment.get_coredata() 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): # Try to resolve the ID and append the node to the queue var_name = curr.value - if var_name in self.assignments and self.assignments[var_name]: - tmp_node = self.assignments[var_name][0] + if var_name in self.assignments: + tmp_node = self.assignments[var_name] if isinstance(tmp_node, (ArrayNode, IdNode, FunctionNode)): srcqueue += [tmp_node] elif isinstance(curr, ArithmeticNode): diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 2769c2d04..6b40815a2 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -2182,13 +2182,12 @@ class Interpreter(InterpreterBase): def __init__(self, build, backend=None, subproject='', subdir='', subproject_dir='subprojects', 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.build = build self.environment = build.environment self.coredata = self.environment.get_coredata() self.backend = backend - self.subproject = subproject self.summary = {} if modules is None: self.modules = {} diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py index 152c65b1d..2865a955c 100644 --- a/mesonbuild/interpreterbase.py +++ b/mesonbuild/interpreterbase.py @@ -360,12 +360,12 @@ def is_disabled(args, kwargs) -> bool: return False 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.funcs = {} self.builtin = {} self.subdir = subdir - self.variables = {} + self.subproject = subproject self.argument_depth = 0 self.current_lineno = -1 # Current node set during a function call. This can be used as location diff --git a/mesonbuild/rewriter.py b/mesonbuild/rewriter.py index a6d225677..46ce56cda 100644 --- a/mesonbuild/rewriter.py +++ b/mesonbuild/rewriter.py @@ -414,10 +414,10 @@ class Rewriter: # Check the assignments tgt = None if target in self.interpreter.assignments: - node = self.interpreter.assignments[target][0] + node = self.interpreter.assignments[target] if isinstance(node, FunctionNode): 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 @@ -434,7 +434,7 @@ class Rewriter: # Check the assignments if dependency in self.interpreter.assignments: - node = self.interpreter.assignments[dependency][0] + node = self.interpreter.assignments[dependency] if isinstance(node, FunctionNode): if node.func_name in ['dependency']: name = self.interpreter.flatten_args(node.args)[0] diff --git a/run_unittests.py b/run_unittests.py index 56551655e..3f5c87e45 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -1398,7 +1398,7 @@ class DataTests(unittest.TestCase): ''' env = get_fake_env() interp = Interpreter(FakeBuild(env), mock=True) - astint = AstInterpreter('.', '') + astint = AstInterpreter('.', '', '') self.assertEqual(set(interp.funcs.keys()), set(astint.funcs.keys()))