def find_qualnames(code, prefix=""): for subcode in code.co_consts: if type(subcode) != type(code): continue qualname = prefix + subcode.co_name instructions = list(get_instructions(subcode))[:4] opnames = [inst.opname for inst in instructions] arg_reprs = [inst.argrepr for inst in instructions] is_class = ( opnames == "LOAD_NAME STORE_NAME LOAD_CONST STORE_NAME".split() and arg_reprs == ["__name__", "__module__", repr(qualname), "__qualname__"] ) yield subcode, qualname for x in find_qualnames( subcode, qualname + ("." if is_class else ".<locals>.") ): yield x
def check_code(self, code, nodes): linestarts = dict(dis.findlinestarts(code)) instructions = get_instructions(code) lineno = None for inst in instructions: if time.time() - self.start_time > 45 * 60: # Avoid travis time limit of 50 minutes raise TimeOut lineno = linestarts.get(inst.offset, lineno) if not inst.opname.startswith(( 'BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'SLICE+', 'COMPARE_OP', 'CALL_', 'IS_OP', 'CONTAINS_OP', )): continue frame = C() frame.f_lasti = inst.offset frame.f_code = code frame.f_globals = globals() frame.f_lineno = lineno source = Source.for_frame(frame) node = None try: try: node = Source.executing(frame).node except Exception: if inst.opname.startswith(('COMPARE_OP', 'CALL_')): continue if isinstance(only(source.statements_at_line(lineno)), (ast.AugAssign, ast.Import)): continue raise except Exception: print(source.text, lineno, inst, node and ast.dump(node), code, file=sys.stderr, sep='\n') raise nodes[node].append((inst, frame.__dict__)) yield [inst.opname, node_string(source, node)] for const in code.co_consts: if isinstance(const, type(code)): for x in self.check_code(const, nodes): yield x
def check_code(self, code, nodes, decorators, check_names): linestarts = dict(dis.findlinestarts(code)) instructions = get_instructions(code) lineno = None for inst in instructions: if time.time() - self.start_time > 45 * 60: # Avoid travis time limit of 50 minutes raise TimeOut lineno = linestarts.get(inst.offset, lineno) if not inst.opname.startswith( ( 'BINARY_', 'UNARY_', 'LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD', 'SLICE+', 'COMPARE_OP', 'CALL_', 'IS_OP', 'CONTAINS_OP', ) + ('LOAD_NAME', 'LOAD_GLOBAL', 'LOAD_FAST', 'LOAD_DEREF', 'LOAD_CLASSDEREF') * check_names ): continue frame = C() frame.f_lasti = inst.offset frame.f_code = code frame.f_globals = globals() frame.f_lineno = lineno source = Source.for_frame(frame) node = None try: try: ex = Source.executing(frame) node = ex.node except Exception: if inst.opname.startswith(('COMPARE_OP', 'IS_OP', 'CALL_', 'LOAD_NAME')): continue if inst.opname == 'LOAD_FAST' and inst.argval == '.0': continue if inst.argval == 'AssertionError': continue if any( isinstance(stmt, (ast.AugAssign, ast.Import)) for stmt in source.statements_at_line(lineno) ): continue raise # argval isn't set for all relevant instructions in python 2 if isinstance(node, ast.Name) and (PY3 or inst.argval): self.assertEqual(inst.argval, node.id) except Exception: print(source.text, lineno, inst, node and ast.dump(node), code, file=sys.stderr, sep='\n') raise if ex.decorator: decorators[(node.lineno, node.name)].append(ex.decorator) else: nodes[node].append((inst, frame.__dict__)) yield [inst.opname, node_string(source, ex.decorator or node)] for const in code.co_consts: if isinstance(const, type(code)): for x in self.check_code(const, nodes, decorators, check_names=check_names): yield x