def visit_Match(self, node): match_node = self.generic_visit(node) line_numbers = set() find_line_numbers(match_node, line_numbers) first_line_number = min(line_numbers) new_nodes = [self._create_context_call('start_assignment')] try_body = [match_node] finally_body = [self._create_context_call('end_assignment')] new_nodes.append( Try(body=try_body, finalbody=finally_body, handlers=[], orelse=[], lineno=first_line_number)) match_node.subject = self._create_bare_context_call( 'set_assignment_value', [match_node.subject]) for case in match_node.cases: try: format_string = self._wrap_assignment_targets((case.pattern, )) except ValueError: # Not a capture pattern, nothing to report. format_string = None if format_string is not None: case.body.insert( 0, self._create_context_call( 'report_assignment', [Str(s=format_string), Num(n=case.pattern.lineno)]))
def visit_Module(self, node): new_node = self.generic_visit(node) line_numbers = set() new_body = [] try_body = new_node.body if try_body: while try_body and self._is_module_header(try_body[0]): # noinspection PyUnresolvedReferences new_body.append(try_body.pop(0)) find_line_numbers(new_node, line_numbers) if line_numbers: first_line_number = min(line_numbers) last_line_number = max(line_numbers) handler_body = [self._create_context_call('exception'), Raise()] handler = ExceptHandler(body=handler_body, lineno=last_line_number) new_body.append(Try(body=try_body, handlers=[handler], orelse=[], finalbody=[])) new_node.body = new_body self._set_statement_line_numbers(try_body, first_line_number) self._set_statement_line_numbers(handler_body, last_line_number) return new_node
def visit_FunctionDef(self, node): """ Instrument a function definition by creating a new report builder for this stack frame and putting it in a local variable. The local variable has the same name as the global variable so all calls can use the same CONTEXT_NAME symbol, but it means that I had to use this: x = globals()['x'].start_frame() Kind of ugly, but I think it was worth it to handle recursive calls. """ if node.name == '__repr__': return node new_node = self.generic_visit(node) line_numbers = set() self._find_line_numbers(new_node, line_numbers) first_line_number = min(line_numbers) last_line_number = max(line_numbers) args = [Num(n=first_line_number), Num(n=last_line_number)] try_body = new_node.body globals_call = Call(func=Name(id='globals', ctx=Load()), args=[], keywords=[], starargs=None, kwargs=None) global_context = Subscript(value=globals_call, slice=Index(value=Str(s=CONTEXT_NAME)), ctx=Load()) start_frame_call = Call(func=Attribute(value=global_context, attr='start_frame', ctx=Load()), args=args, keywords=[], starargs=None, kwargs=None) context_assign = Assign(targets=[Name(id=CONTEXT_NAME, ctx=Store())], value=start_frame_call) new_node.body = [context_assign] # trace function parameter values for target in new_node.args.args: if isinstance(target, Name) and target.id == 'self': continue if arg and isinstance(target, arg) and target.arg == 'self': continue new_node.body.append(self._trace_assignment(target, node.lineno)) handler_body = [self._create_context_call('exception'), Raise()] new_node.body.append( Try(body=try_body, handlers=[ExceptHandler(body=handler_body)], orelse=[], finalbody=[])) self._set_statement_line_numbers(try_body, first_line_number) self._set_statement_line_numbers(handler_body, last_line_number) return new_node
def _create_end_assignment(self, new_nodes, try_body, first_line_number, last_line_number): end_assignment = self._create_context_call('end_assignment') finally_body = [end_assignment] new_nodes.append( Try(body=try_body, finalbody=finally_body, handlers=[], orelse=[], lineno=first_line_number)) self._set_statement_line_numbers(try_body, first_line_number) self._set_statement_line_numbers(finally_body, last_line_number)
def visitTry(self, node: ast.Try) -> None: def get_handler_tracker(handler: ast.ExceptHandler) -> str: """gets a variable name to track exception state for aliased global""" handler_type = handler.type if isinstance(handler_type, Name): desc = handler_type.id elif handler.type is None: desc = "bare" else: desc = "complex" return f"<{desc} {handler.name} at {str(id(handler))}>" for i, handler in enumerate(node.handlers): handler_name = handler.name if handler_name is None or not self.feature_extractor.is_global( handler_name, self.scope ): continue if handler_name not in self.builtins: continue tracker_name = get_handler_tracker(handler) self.scope.add_def(tracker_name) if not isinstance(node.body, TryBodyHook): node.body = [TryBodyHook(node.body)] cast(TryBodyHook, node.body[0]).trackers.append(tracker_name) handler.body = [TryHandlerBodyHook(handler.body, tracker_name)] if not isinstance(node.finalbody, TryFinallyHook): node.finalbody = [TryFinallyHook(node.finalbody)] cast(TryFinallyHook, node.finalbody[0]).handlers_to_restore.append( (tracker_name, handler_name) ) super().visitTry(node) return
def visit_Assign(self, node): existing_node = self.generic_visit(node) try: targets = existing_node.targets except AttributeError: targets = [existing_node.target] if any(map(self._is_untraceable_attribute, targets)): return existing_node if existing_node.value is None: return existing_node line_numbers = set() find_line_numbers(existing_node, line_numbers) first_line_number = min(line_numbers) last_line_number = max(line_numbers) if len(targets) == 1 and isinstance(targets[0], Name): existing_node.value = self._create_bare_context_call( 'assign', [ Str(s=targets[0].id), existing_node.value, Num(n=first_line_number) ]) return existing_node new_nodes = [] format_string = self._wrap_assignment_targets(targets) if (len(targets) == 1 and isinstance(targets[0], Tuple)): existing_node.value = Call(func=Name(id='tuple', ctx=Load()), args=[existing_node.value], keywords=[], starargs=None, kwargs=None) existing_node.value = self._create_bare_context_call( 'set_assignment_value', [existing_node.value]) new_nodes.append(self._create_context_call('start_assignment')) try_body = [existing_node] if format_string is not None: try_body.append( self._create_context_call( 'report_assignment', [Str(s=format_string), Num(n=existing_node.lineno)])) end_assignment = self._create_context_call('end_assignment') finally_body = [end_assignment] new_nodes.append( Try(body=try_body, finalbody=finally_body, handlers=[], orelse=[], lineno=first_line_number)) self._set_statement_line_numbers(try_body, first_line_number) self._set_statement_line_numbers(finally_body, last_line_number) return new_nodes
def visit_AugAssign(self, node): read_target = deepcopy(node.target) existing_node = self.generic_visit(node) line_numbers = set() find_line_numbers(existing_node, line_numbers) first_line_number = min(line_numbers) last_line_number = max(line_numbers) new_nodes = [] try_body = [existing_node] new_nodes.append(self._create_context_call('start_assignment')) format_string = self._wrap_assignment_target(existing_node.target) if format_string is not None: if ':' in format_string: existing_node.value = self._create_bare_context_call( 'set_assignment_value', [existing_node.value]) operator_char = OPERATOR_CHARS.get(type(existing_node.op), '?') format_string += ' {}= {{}} '.format(operator_char) else: self._wrap_assignment_target(read_target, index_to_get=-1) read_target.ctx = Load() set_assignment_value = self._create_context_call( 'set_assignment_value', [read_target]) try_body.append(set_assignment_value) format_string += ' = {}' try_body.append( self._create_context_call( 'report_assignment', [Str(s=format_string), Num(n=existing_node.lineno)])) end_assignment = self._create_context_call('end_assignment') finally_body = [end_assignment] new_nodes.append( Try(body=try_body, finalbody=finally_body, handlers=[], orelse=[], lineno=first_line_number)) self._set_statement_line_numbers(try_body, first_line_number) self._set_statement_line_numbers(finally_body, last_line_number) return new_nodes
def visit_Module(self, node): new_node = self.generic_visit(node) line_numbers = set() self._find_line_numbers(new_node, line_numbers) if line_numbers: first_line_number = min(line_numbers) last_line_number = max(line_numbers) else: first_line_number = last_line_number = 1 try_body = new_node.body handler_body = [self._create_context_call('exception')] handler = ExceptHandler(body=handler_body, lineno=last_line_number) if not try_body: # empty module new_node.body = try_body else: new_node.body = [ Try(body=try_body, handlers=[handler], orelse=[], finalbody=[]) ] self._set_statement_line_numbers(try_body, first_line_number) self._set_statement_line_numbers(handler_body, last_line_number) return new_node
def validate_except_handlers(self, try_node: ast.Try): if len(try_node.handlers) > 1: exception_handlers: List[ast.ExceptHandler] = try_node.handlers.copy() general_exc_handler: ast.ExceptHandler = next( (handler for handler in exception_handlers if (handler.type is None or # no specified exception or is BaseException (isinstance(handler.type, ast.Name) and handler.type.id == BaseException.__name__) ) ), exception_handlers[0] ) try_node.handlers = [general_exc_handler] exception_handlers.remove(general_exc_handler) for handler in exception_handlers: warnings = len(self.warnings) self.visit(handler) if warnings == len(self.warnings): self._log_using_specific_exception_warning(handler.type) if len(try_node.handlers) == 1: self.visit(try_node.handlers[0])
def visit_FunctionDef(self, node): """ Instrument a function definition by creating a new report builder for this stack frame and putting it in a local variable. The local variable has the same name as the global variable so all calls can use the same CONTEXT_NAME symbol, but it means that I had to use this: x = globals()['x'].start_frame() Kind of ugly, but I think it was worth it to handle recursive calls. """ new_node = self.generic_visit(node) line_numbers = set() find_line_numbers(new_node, line_numbers) first_line_number = min(line_numbers) last_line_number = max(line_numbers) args = [Num(n=first_line_number), Num(n=last_line_number)] start_frame_keywords = [] for decorator in new_node.decorator_list: if getattr(decorator, 'id', None) == 'traced': start_frame_keywords.append( keyword(arg='is_decorated', value=Name(id='True', ctx=Load()))) try_body = new_node.body globals_call = Call(func=Name(id='globals', ctx=Load()), args=[], keywords=[], starargs=None, kwargs=None) global_context = Subscript(value=globals_call, slice=Index(value=Str(s=CONTEXT_NAME)), ctx=Load()) start_frame_call = Call(func=Attribute(value=global_context, attr='start_frame', ctx=Load()), args=args, keywords=start_frame_keywords, starargs=None, kwargs=None) context_assign = Assign(targets=[Name(id=CONTEXT_NAME, ctx=Store())], value=start_frame_call) new_node.body = [context_assign] if isinstance(try_body[0], Expr) and isinstance( try_body[0].value, Str): # Move docstring back to top of function. # noinspection PyUnresolvedReferences new_node.body.insert(0, try_body.pop(0)) # trace function parameter values arg_nodes = [] arg_nodes.extend(getattr(new_node.args, 'posonlyargs', [])) arg_nodes.extend(new_node.args.args) arg_nodes.append(new_node.args.kwarg) arg_nodes.append(new_node.args.vararg) arg_nodes.extend(new_node.args.kwonlyargs) for target in arg_nodes: if target is None: continue if isinstance(target, Name) and target.id == 'self': continue if isinstance(target, arg) and target.arg == 'self': continue new_node.body.append(self._trace_assignment(target, node.lineno)) if try_body: handler_body = [self._create_context_call('exception'), Raise()] new_node.body.append( Try(body=try_body, handlers=[ExceptHandler(body=handler_body)], orelse=[], finalbody=[])) self._set_statement_line_numbers(try_body, first_line_number) self._set_statement_line_numbers(handler_body, last_line_number) return new_node
def empty_script_tree(project_id: str, add_main_loop: bool = True) -> Module: """Creates barebones of the script (empty 'main' function). Returns ------- """ main_body: List[stmt] = [ Assign( targets=[Name(id="aps", ctx=Store())], value=Call(func=Name(id="ActionPoints", ctx=Load()), args=[Name(id="res", ctx=Load())], keywords=[]), type_comment=None, ) ] if add_main_loop: main_body.append( While(test=NameConstant(value=True, kind=None), body=[Pass()], orelse=[])) else: """put there "pass" in order to make code valid even if there is no other statement (e.g. no object from resources)""" main_body.append(Pass()) # TODO helper function for try ... except tree = Module( body=[ FunctionDef( name="main", args=arguments( args=[ arg(arg="res", annotation=Name(id=RES_CLS, ctx=Load()), type_comment=None) ], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[], ), body=main_body, decorator_list=[], returns=NameConstant(value=None, kind=None), type_comment=None, ), If( test=Compare(left=Name(id="__name__", ctx=Load()), ops=[Eq()], comparators=[Str(s="__main__", kind="")]), body=[ Try( body=[ With( items=[ withitem( context_expr=Call( func=Name(id=RES_CLS, ctx=Load()), args=[Str(s=project_id, kind="")], keywords=[], ), optional_vars=Name(id="res", ctx=Store()), ) ], body=[ Expr(value=Call( func=Name(id="main", ctx=Load()), args=[Name(id="res", ctx=Load())], keywords=[], )) ], type_comment=None, ) ], handlers=[ ExceptHandler( type=Name(id=Exception.__name__, ctx=Load()), name="e", body=[ Expr(value=Call( func=Name(id=arcor2.exceptions.runtime. print_exception.__name__, ctx=Load()), args=[Name(id="e", ctx=Load())], keywords=[], )) ], ) ], orelse=[], finalbody=[], ) ], orelse=[], ), ], type_ignores=[], ) add_import(tree, arcor2.exceptions.runtime.__name__, arcor2.exceptions.runtime.print_exception.__name__) add_import(tree, RES_MODULE, RES_CLS, try_to_import=False) add_import(tree, "action_points", "ActionPoints", try_to_import=False) return tree