Пример #1
0
def profile(fct,
            sort='cumulative',
            rootrem=None,
            as_df=False,
            pyinst_format=None,
            **kwargs):
    """
    Profiles the execution of a function.

    @param      fct             function to profile
    @param      sort            see `sort_stats <https://docs.python.org/3/library/
                                profile.html#pstats.Stats.sort_stats>`_
    @param      rootrem         root to remove in filenames
    @param      as_df           return the results as a dataframe and not text
    @param      pyinst_format   format for :epkg:`pyinstrument`, if not empty,
                                the function uses this module or raises an exception if not
                                installed, the options are *text*, *textu* (text with colors),
                                *json*, *html*
    @param      kwargs          additional parameters used to create the profiler
    @return                     raw results, statistics text dump (or dataframe is *as_df* is True)

    .. plot::

        import matplotlib.pyplot as plt
        from pyquickhelper.pycode.profiling import profile
        from pyquickhelper.texthelper import compare_module_version

        def fctm():
            return compare_module_version('0.20.4', '0.22.dev0')

        pr, df = profile(lambda: [fctm() for i in range(0, 1000)], as_df=True)
        ax = df[['namefct', 'cum_tall']].head(n=15).set_index(
            'namefct').plot(kind='bar', figsize=(8, 3), rot=30)
        ax.set_title("example of a graph")
        for la in ax.get_xticklabels():
            la.set_horizontalalignment('right');
        plt.show()
    """
    if pyinst_format is None:
        pr = cProfile.Profile(**kwargs)
        pr.enable()
        fct()
        pr.disable()
        s = StringIO()
        ps = pstats.Stats(pr, stream=s).sort_stats(sort)
        ps.print_stats()
        res = s.getvalue()
        try:
            pack = site.getsitepackages()
        except AttributeError:  # pragma: no cover
            import numpy
            pack = os.path.normpath(
                os.path.abspath(
                    os.path.join(os.path.dirname(numpy.__file__), "..")))
            pack = [pack]
        pack_ = os.path.normpath(os.path.join(pack[-1], '..'))

        def clean_text(res):
            res = res.replace(pack[-1], "site-packages")
            res = res.replace(pack_, "lib")
            if rootrem is not None:
                if isinstance(rootrem, str):
                    res = res.replace(rootrem, '')
                else:
                    for sub in rootrem:
                        if isinstance(sub, str):
                            res = res.replace(sub, '')
                        elif isinstance(sub, tuple) and len(sub) == 2:
                            res = res.replace(sub[0], sub[1])
                        else:
                            raise TypeError(
                                "rootrem must contains strings or tuple not {0}"
                                .format(rootrem))
            return res

        if as_df:

            def better_name(row):
                if len(row['fct']) > 15:
                    return "{}-{}".format(row['file'].split(':')[-1],
                                          row['fct'])
                name = row['file'].replace("\\", "/")
                return "{}-{}".format(name.split('/')[-1], row['fct'])

            rows = _process_pstats(ps, clean_text)
            import pandas
            df = pandas.DataFrame(rows)
            df = df[[
                'fct', 'file', 'ncalls1', 'ncalls2', 'tin', 'cum_tin', 'tall',
                'cum_tall'
            ]]
            df['namefct'] = df.apply(lambda row: better_name(row), axis=1)
            df = df.groupby(['namefct', 'file'],
                            as_index=False).sum().sort_values(
                                'cum_tall',
                                ascending=False).reset_index(drop=True)
            return ps, df
        else:
            res = clean_text(res)
            return ps, res
    elif as_df:
        raise ValueError(  # pragma: no cover
            "as_df is not a compatible option with pyinst_format")
    else:
        try:
            from pyinstrument import Profiler
        except ImportError as e:  # pragma: no cover
            raise ImportError("pyinstrument is not installed.") from e

        profiler = Profiler(**kwargs)
        profiler.start()
        fct()
        profiler.stop()

        if pyinst_format == "text":
            return profiler, profiler.output_text(unicode=False, color=False)
        elif pyinst_format == "textu":
            return profiler, profiler.output_text(unicode=True, color=True)
        elif pyinst_format == "json":
            from pyinstrument.renderers import JSONRenderer
            return profiler, profiler.output(JSONRenderer())
        elif pyinst_format == "html":
            return profiler, profiler.output_html()
        else:
            raise ValueError("Unknown format '{}'.".format(pyinst_format))
Пример #2
0
def main():
    usage = ("usage: pyinstrument [options] scriptfile [arg] ...")
    parser = OptionParser(usage=usage)
    parser.allow_interspersed_args = False

    parser.add_option('', '--setprofile',
        dest='setprofile', action='store_true',
        help='run in setprofile mode, instead of signal mode', default=False)

    parser.add_option('', '--html',
        dest="output_html", action='store_true',
        help="output HTML instead of text", default=False)
    parser.add_option('', '--flame',
        dest='output_flame', action='store_true',
        help='output an HTML flame chart', default=False)
    parser.add_option('-r', '--renderer',
        dest='output_renderer', action='store', type='string',
        help='python import path to a renderer class', default=None)

    parser.add_option('-o', '--outfile',
        dest="outfile", action='store',
        help="save report to <outfile>", default=None)

    parser.add_option('', '--unicode',
        dest='unicode', action='store_true',
        help='force unicode text output')
    parser.add_option('', '--no-unicode',
        dest='unicode', action='store_false',
        help='force ascii text output')

    parser.add_option('', '--color',
        dest='color', action='store_true',
        help='force ansi color text output')
    parser.add_option('', '--no-color',
        dest='color', action='store_false',
        help='force no color text output')

    parser.add_option('-m', '',
        dest='module_name', action='store',
        help='searches sys.path for the named module and runs the  corresponding .py file as a script.')

    if not sys.argv[1:]:
        parser.print_help()
        sys.exit(2)

    options, args = parser.parse_args()

    if args == [] and options.module_name is None:
        parser.print_help()
        sys.exit(2)

    if options.module_name is not None:
        sys.argv[:] = [options.module_name] + args
        code = "run_module(modname, run_name='__main__')"
        globs = {
            'run_module': runpy.run_module,
            'modname': options.module_name
        }
    else:
        sys.argv[:] = args
        progname = args[0]
        sys.path.insert(0, os.path.dirname(progname))
        with open(progname, 'rb') as fp:
            code = compile(fp.read(), progname, 'exec')
        globs = {
            '__file__': progname,
            '__name__': '__main__',
            '__package__': None,
        }

    if options.output_renderer:
        renderer = options.output_renderer
    elif options.output_html:
        renderer = 'html'
    else:
        renderer = 'text'

    recorder = get_renderer_class(renderer).preferred_recorder

    profiler = Profiler(recorder=recorder)

    profiler.start()

    try:
        exec_(code, globs, None)
    except (SystemExit, KeyboardInterrupt):
        pass

    profiler.stop()

    if options.outfile:
        f = codecs.open(options.outfile, 'w', 'utf-8')
    else:
        f = sys.stdout

    renderer_kwargs = {}

    if renderer == 'text':
        unicode_override = options.unicode != None
        color_override = options.color != None
        unicode = options.unicode if unicode_override else file_supports_unicode(f)
        color = options.color if color_override else file_supports_color(f)
        
        renderer_kwargs = {'unicode': unicode, 'color': color}

    f.write(profiler.output(renderer=renderer, **renderer_kwargs))
    f.close()
Пример #3
0
def main():
    usage = ("usage: pyinstrument [options] scriptfile [arg] ...")
    parser = OptionParser(usage=usage)
    parser.allow_interspersed_args = False

    parser.add_option(
        '-m',
        '',
        dest='module_name',
        action='store',
        help="run library module as a script, like 'python -m module'")

    parser.add_option(
        '-r',
        '--renderer',
        dest='renderer',
        action='store',
        type='string',
        help=
        "how the report should be rendered. One of: 'text', 'html', 'json', or python import path to a renderer class",
        default='text')

    parser.add_option('',
                      '--html',
                      dest="output_html",
                      action='store_true',
                      help="Shortcut for '--renderer=html'",
                      default=False)

    parser.add_option('-o',
                      '--outfile',
                      dest="outfile",
                      action='store',
                      help="save report to <outfile>",
                      default=None)

    parser.add_option('',
                      '--unicode',
                      dest='unicode',
                      action='store_true',
                      help='(text renderer only) force unicode text output')
    parser.add_option('',
                      '--no-unicode',
                      dest='unicode',
                      action='store_false',
                      help='(text renderer only) force ascii text output')

    parser.add_option('',
                      '--color',
                      dest='color',
                      action='store_true',
                      help='(text renderer only) force ansi color text output')
    parser.add_option('',
                      '--no-color',
                      dest='color',
                      action='store_false',
                      help='(text renderer only) force no color text output')

    if not sys.argv[1:]:
        parser.print_help()
        sys.exit(2)

    options, args = parser.parse_args()

    if args == [] and options.module_name is None:
        parser.print_help()
        sys.exit(2)

    if options.module_name is not None:
        sys.argv[:] = [options.module_name] + args
        code = "run_module(modname, run_name='__main__')"
        globs = {
            'run_module': runpy.run_module,
            'modname': options.module_name
        }
    else:
        sys.argv[:] = args
        progname = args[0]
        sys.path.insert(0, os.path.dirname(progname))
        with open(progname, 'rb') as fp:
            code = compile(fp.read(), progname, 'exec')
        globs = {
            '__file__': progname,
            '__name__': '__main__',
            '__package__': None,
        }

    profiler = Profiler()

    profiler.start()

    try:
        exec_(code, globs, None)
    except (SystemExit, KeyboardInterrupt):
        pass

    profiler.stop()

    if options.output_html:
        options.renderer = 'html'

    output_to_temp_file = (options.renderer == 'html' and not options.outfile
                           and file_is_a_tty(sys.stdout))

    if options.outfile:
        f = codecs.open(options.outfile, 'w', 'utf-8')
    elif output_to_temp_file:
        output_file = tempfile.NamedTemporaryFile(suffix='.html', delete=False)
        f = codecs.getwriter('utf-8')(output_file)
        output_filename = output_file.name
    else:
        f = sys.stdout

    renderer_kwargs = {}

    if options.renderer == 'text':
        unicode_override = options.unicode != None
        color_override = options.color != None
        unicode = options.unicode if unicode_override else file_supports_unicode(
            f)
        color = options.color if color_override else file_supports_color(f)

        renderer_kwargs = {'unicode': unicode, 'color': color}

    f.write(profiler.output(renderer=options.renderer, **renderer_kwargs))
    f.close()

    if output_to_temp_file:
        print('stdout is a terminal, so saved profile output to %s' %
              output_filename)
        import webbrowser, urllib.parse
        url = urllib.parse.urlunparse(
            ('file', '', output_filename, '', '', ''))
        webbrowser.open(url)