Ejemplo n.º 1
0
    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()
Ejemplo n.º 2
0
 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
Ejemplo n.º 3
0
    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()
Ejemplo n.º 4
0
    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