<commit_before>import html
from ipywidgets import DOMWidget
from traitlets import Unicode, Int
from ipywidgets.widgets.widget import CallbackDispatcher, register

from pygments import highlight
from pygments.lexers import PythonLexer
from pygments.formatters import HtmlFormatter

import linecache


class IProfile(DOMWidget):
    def __init__(self, cprofile, lprofile=None, *args, **kwargs):
        self.generate_cprofile_tree(cprofile)
        self.lprofile = lprofile
        self.generate_content(self.roots[0])
        self.value = str(self.html_value)
        self.on_msg(self.handle_on_msg)

        super(IProfile, self).__init__(value=self.value)

    def generate_cprofile_tree(self, cprofile):
        self.cprofile_tree = {}
        for entry in cprofile:
            function = entry[0]
            calls_raw = entry[5]
            calls = {}
            if calls_raw is not None:
                for call in calls_raw:
                    calls[call[0]] = {'callcount': call[1],
                                      'reccallcount': call[2],
                                      'totaltime': call[3],
                                      'inlinetime': call[4]}

            self.cprofile_tree[function] = {'callcount': entry[1],
                                            'reccallcount': entry[2],
                                            'totaltime': entry[3],
                                            'inlinetime': entry[4],
                                            'calls': calls}

        # Find root nodes
        callcounts = dict([(function, 0) for function in self.cprofile_tree])
        for function in self.cprofile_tree:
            for call in self.cprofile_tree[function]['calls']:
                callcounts[call] += 1

        self.roots = []
        for (i, n) in callcounts.iteritems():
            if n == 0:
                self.roots.append(i)

    _view_name = Unicode('IProfileView').tag(sync=True)

    # This trait is the actual html displayed in the widget
    value = Unicode().tag(sync=True)

    # Number of elements in table (used by front end to generate click events)
    n_table_elements = Int(0).tag(sync=True)

    # Dictionary mapping html id's to function names
    id_dict = {}

    def generate_content(self, fun):
        # Generate page for a particular function fun
        self.html_value = html.HTML()
        self.generate_heading(fun)
        self.generate_table(fun)
        if self.lprofile is not None:
            self.generate_lprofile(fun)

    def generate_heading(self, fun):
        try:
            self.html_value.h3(fun.co_name)
            self.html_value.p("From: " + fun.co_filename)
        except AttributeError:
            self.html_value.h3(fun)

    def generate_table(self, fun):
        """
        Generate a table displaying the functions called by fun and their
        respective running times.
        """
        table = self.html_value.table()
        h = table.thead().tr()
        h.th("Function")
        h.th("Total time (seconds)")
        args = [function for function in self.cprofile_tree[fun]['calls']]
        # Sort by total time (descending)
        args.sort(key=lambda x: self.cprofile_tree[x]['totaltime'])
        args.reverse()

        for i in range(len(args)):
            arg = args[i]
            r = table.tr()
            # Function name
            try:
                name = arg.co_name
            except AttributeError:
                name = arg
            r.td.a(name, id="function" + str(i))

            # Total time spent in function
            r.td(str(self.cprofile_tree[arg]['totaltime']))
            self.n_table_elements += 1
            self.id_dict["function" + str(i)] = arg

    def generate_lprofile(self, fun):
        try:
            filename = fun.co_filename
            firstlineno = fun.co_firstlineno
            name = fun.co_name
        except AttributeError:
            print "fun not a code object"
            return

        ltimings_key = (filename, firstlineno, name)

        try:
            ltimings = self.lprofile.timings[ltimings_key]
        except KeyError:
            print "fun not profiled by lprofiler"
            return

        raw_code = ""
        linenos = range(firstlineno, ltimings[-1][0] + 1)
        for lineno in linenos:
            raw_code += linecache.getline(fun.co_filename, lineno)

        formatter = LProfileFormatter(firstlineno, ltimings, noclasses=True)
        self.html_value += highlight(raw_code, PythonLexer(), formatter)

    def handle_on_msg(self, _, content, buffers):
        clicked_fun = self.id_dict[content]
        self.generate_content(clicked_fun)

        self.value = str(self.html_value)

class LProfileFormatter(HtmlFormatter):<commit_msg>Add profile summary<commit_after>import html
from ipywidgets import DOMWidget
from traitlets import Unicode, Int
from ipywidgets.widgets.widget import CallbackDispatcher, register

from pygments import highlight
from pygments.lexers import PythonLexer
from pygments.formatters import HtmlFormatter

import linecache


class IProfile(DOMWidget):
    def __init__(self, cprofile, lprofile=None, *args, **kwargs):
        self.generate_cprofile_tree(cprofile)
        self.lprofile = lprofile
        self.generate_content()
        self.value = str(self.html_value)
        self.on_msg(self.handle_on_msg)

        super(IProfile, self).__init__(value=self.value)

    def generate_cprofile_tree(self, cprofile):
        self.cprofile_tree = {}
        for entry in cprofile:
            function = entry[0]
            calls_raw = entry[5]
            calls = {}
            if calls_raw is not None:
                for call in calls_raw:
                    calls[call[0]] = {'callcount': call[1],
                                      'reccallcount': call[2],
                                      'totaltime': call[3],
                                      'inlinetime': call[4]}

            self.cprofile_tree[function] = {'callcount': entry[1],
                                            'reccallcount': entry[2],
                                            'totaltime': entry[3],
                                            'inlinetime': entry[4],
                                            'calls': calls}

        # Find root nodes
        callcounts = dict([(function, 0) for function in self.cprofile_tree])
        for function in self.cprofile_tree:
            for call in self.cprofile_tree[function]['calls']:
                callcounts[call] += 1

        self.roots = []
        for (i, n) in callcounts.iteritems():
            if n == 0:
                self.roots.append(i)

    _view_name = Unicode('IProfileView').tag(sync=True)

    # This trait is the actual html displayed in the widget
    value = Unicode().tag(sync=True)

    # Number of elements in table (used by front end to generate click events)
    n_table_elements = Int(0).tag(sync=True)

    # Dictionary mapping html id's to function names
    id_dict = {}

    def generate_content(self, fun=None):
        # Generate page for a particular function fun
        self.html_value = html.HTML()
        self.generate_heading(fun)
        self.generate_table(fun)
        if self.lprofile is not None and fun is not None:
            self.generate_lprofile(fun)

    def generate_heading(self, fun):
        if fun is None:
            self.html_value.h3("Summary")
            return

        try:
            heading = "{} (Calls: {}, Time: {})"
            heading = heading.format(fun.co_name,
                                     self.cprofile_tree[fun]['callcount'],
                                     self.cprofile_tree[fun]['totaltime'])
            self.html_value.h3(heading)
            self.html_value.p("From: " + fun.co_filename)
        except AttributeError:
            self.html_value.h3(fun)

    def generate_table(self, fun):
        """
        Generate a table displaying the functions called by fun and their
        respective running times.
        """
        table = self.html_value.table()
        h = table.thead().tr()
        h.th("Function")
        h.th("Total time (seconds)")
        if fun is None:
            args = self.cprofile_tree.keys()
        else:
            args = [function for function in self.cprofile_tree[fun]['calls']]
        # Sort by total time (descending)
        args.sort(key=lambda x: self.cprofile_tree[x]['totaltime'])
        args.reverse()

        for i in range(len(args)):
            arg = args[i]
            r = table.tr()
            # Function name
            try:
                name = arg.co_name
            except AttributeError:
                name = arg
            r.td.a(name, id="function" + str(i))

            # Total time spent in function
            r.td(str(self.cprofile_tree[arg]['totaltime']))
            self.n_table_elements += 1
            self.id_dict["function" + str(i)] = arg

    def generate_lprofile(self, fun):
        try:
            filename = fun.co_filename
            firstlineno = fun.co_firstlineno
            name = fun.co_name
        except AttributeError:
            return

        ltimings_key = (filename, firstlineno, name)

        try:
            ltimings = self.lprofile.timings[ltimings_key]
        except KeyError:
            return

        raw_code = ""
        linenos = range(firstlineno, ltimings[-1][0] + 1)
        for lineno in linenos:
            raw_code += linecache.getline(fun.co_filename, lineno)

        formatter = LProfileFormatter(firstlineno, ltimings, noclasses=True)
        self.html_value += highlight(raw_code, PythonLexer(), formatter)

    def handle_on_msg(self, _, content, buffers):
        clicked_fun = self.id_dict[content]
        self.generate_content(clicked_fun)

        self.value = str(self.html_value)

class LProfileFormatter(HtmlFormatter):
{'fraction_non_alphanumeric': 0.09639530174159579, 'fraction_numerical': 0.0044552450384771165, 'mean_word_length': 1.843938957673481, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4756269', 'n_tokens_mistral': 2795, 'n_tokens_neox': 2686, 'n_words': 660}