def exit_call(self, exit_info): frame = exit_info.current_frame if frame.f_code not in self._code_infos: return frame_info = self.stack[frame] top_iteration = frame_info.iteration loop_iterations = top_iteration.extract_iterations()['loops'] node_values = _deep_dict() def extract_values(iteration, path): for k, v in iteration.vals.items(): full_path = (k,) + path d = node_values for path_k in full_path[:-1]: d = d[path_k] d[full_path[-1]] = v for loop in iteration.loops.values(): for i, iteration in enumerate(loop): extract_values(iteration, path + (i,)) extract_values(top_iteration, ()) db_func = self._code_infos[frame.f_code].db_func exc = exit_info.exc_value if exc: traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb)) exception = exception_string(exc) else: traceback_str = exception = None call = Call(id=frame_info.call_id, function=db_func, arguments=frame_info.arguments, return_value=cheap_repr(exit_info.return_value), exception=exception, traceback=traceback_str, data=json.dumps( dict( node_values=node_values, loop_iterations=loop_iterations, type_names=type_registry.names(), num_special_types=type_registry.num_special_types, ), separators=(',', ':') ), start_time=frame_info.start_time) session.add(call) session.commit()
def _db_func(self, data, filename, html_body, name, start_lineno): """ Retrieve the Function object from the database if one exists, or create one. """ function_hash = hashlib.sha256((filename + name + html_body + data + str(start_lineno) ).encode('utf8')).hexdigest() db_func = one_or_none(session.query(Function).filter_by(hash=function_hash)) # type: Optional[Function] if not db_func: db_func = Function(file=filename, name=name, html_body=html_body, lineno=start_lineno, data=data, hash=function_hash) session.add(db_func) session.commit() return db_func
def exit_call(self, exit_info): # type: (ExitCallInfo) -> None """ This is where all the data collected during the call is gathered up and sent to the database. """ frame = exit_info.current_frame # type: FrameType if frame.f_code not in self._code_infos or _tracing_recursively(frame): return frame_info = self.stack[frame] top_iteration = frame_info.iteration # type: Iteration node_values = _deep_dict() self._extract_node_values(top_iteration, (), node_values) db_func = self._code_infos[frame.f_code].db_func # type: Function exc = exit_info.exc_value # type: Optional[Exception] if exc: traceback_str = ''.join(traceback.format_exception(type(exc), exc, exit_info.exc_tb)) exception = exception_string(exc) else: traceback_str = exception = None call = Call(id=frame_info.call_id, function=db_func, arguments=frame_info.arguments, return_value=cheap_repr(exit_info.return_value), exception=exception, traceback=traceback_str, data=json.dumps( dict( node_values=node_values, loop_iterations=top_iteration.extract_iterations()['loops'], type_names=type_registry.names(), num_special_types=type_registry.num_special_types, ), cls=ProtocolEncoder, separators=(',', ':') ), start_time=frame_info.start_time) session.add(call) session.commit()
def __call__(self, func): if inspect.isclass(func): cls = func for name, meth in iteritems(cls.__dict__): if inspect.ismethod(meth) or inspect.isfunction(meth): setattr(cls, name, self.__call__(meth)) return cls new_func = super(BirdsEye, self).__call__(func) code_info = self._code_infos.get(new_func.__code__) if code_info: return new_func lines, start_lineno = inspect.getsourcelines(func) end_lineno = start_lineno + len(lines) name = safe_qualname(func) filename = os.path.abspath(inspect.getsourcefile(func)) traced_file = new_func.traced_file traced_file.root._depth = 0 for node in ast.walk(traced_file.root): for child in ast.iter_child_nodes(node): child._depth = node._depth + 1 positions = [] node_loops = {} for node in traced_file.nodes: if isinstance(node, ast.expr): node_type = 'expr' if not node._is_interesting_expression: continue elif (isinstance(node, (ast.While, ast.For, ast.comprehension)) and not isinstance(node.parent, ast.GeneratorExp)): node_type = 'loop' elif isinstance(node, ast.stmt): node_type = 'stmt' else: continue assert isinstance(node, ast.AST) # In particular FormattedValue is missing this if not hasattr(node, 'first_token'): continue if not start_lineno <= node.first_token.start[0] <= end_lineno: continue start, end = traced_file.tokens.get_text_range(node) if start == end == 0: continue positions.append((start, 1, node._depth, '<span data-index="%s" data-type="%s">' % (node._tree_index, node_type))) positions.append((end, 0, node._depth, '</span>')) if node._loops: node_loops[node._tree_index] = [ n._tree_index for n in node._loops ] comprehensions = group_by_key_func([ comp for comp in traced_file.nodes if isinstance(comp, ast.comprehension) ], lambda c: c.first_token.line) def get_start(n): return traced_file.tokens.get_text_range(n)[0] for comp_list in comprehensions.values(): prev_start = None for comp in sorted(comp_list, key=lambda c: c.first_token.startpos): if comp is comp.parent.generators[0]: start = get_start(comp.parent) if prev_start is not None and start < prev_start: start = get_start(comp) else: start = get_start(comp) if prev_start is not None: positions.append((start, 1, 0, '\n ')) end_lineno += 1 prev_start = start positions.append((len(traced_file.source), None, None, '')) positions.sort() html_body = [] start = 0 for pos, _, _, part in positions: html_body.append(html.escape(traced_file.source[start:pos])) html_body.append(part) start = pos html_body = ''.join(html_body) html_body = '\n'.join( html_body.split('\n')[start_lineno - 1:end_lineno - 1]) db_args = dict(file=filename, name=name, html_body=html_body, lineno=start_lineno, data=json.dumps( dict(node_loops=node_loops, ), sort_keys=True, )) db_func = one_or_none(session.query(Function).filter_by(**db_args)) if not db_func: db_func = Function(**db_args) session.add(db_func) session.commit() self._code_infos[new_func.__code__] = CodeInfo(db_func, traced_file) return new_func