Source code for grains.profiling

# -*- coding: utf-8 -*-
"""
This module implements profiling facilities so that user code can be tested for speed.

Functions
---------
.. autosummary::
    :toctree: functions/

    profile

"""
import os
import codecs
import inspect
from contextlib import contextmanager

try:
    from pyinstrument import Profiler
    from pyinstrument.renderers.html import HTMLRenderer
except:
    raise ImportError('This module requires the pyinstrument module.')


[docs]@contextmanager def profile(output_format='html', output_filename=None, html_open=False): """Profiles a block of code with Pyinstrument. Intended to be used in a `with` statement. Parameters ---------- output_format : {'html', 'text'}, optional Shows the result either as text or in an HTML file. If :code:`output_format = 'html'`, the file is saved according to the :code:`output_filename` parameter. The default is 'html'. output_filename : str, optional Only taken into account if :code:`output_format = 'html'`. If not given (default), the html output is saved to the same directory the caller resides. The name of the html file is the same as that of the caller. html_open : bool, optional Only taken into account if :code:`output_format = 'html'`. If True, the generated HTML file is opened in the default browser. The default is False. Yields ------ Notes ----- In the implementation, we move two levels up in the stack frame, one for exiting the context manager and one for exiting this generator. This assumes that :meth:`profile` was called as a context manager. As a good practice, provide the :code:`output_filename` input argument. Examples -------- Measure the time needed to generate 1 million uniformly distributed random numbers. >>> import random >>> with profile('html') as p: ... for _ in range(1000000): ... rand_num = random.uniform(1, 2.2) """ # If required, guess the path where the results will be saved if output_format == 'html' and not output_filename: caller = inspect.currentframe().f_back.f_back.f_locals['__file__'] output_filename = os.path.splitext(caller)[0] + '.html' # Start the profiler profiler = Profiler() profiler.start() # Give back the execution to the caller function yield # Finish profiling and show the results profiler.stop() if output_format == 'html': if html_open: HTMLRenderer().open_in_browser(profiler.last_session, output_filename=output_filename) else: with codecs.open(output_filename, 'w', 'utf-8') as f: f.write(HTMLRenderer().render(profiler.last_session)) elif output_format == 'text': print(profiler.output_text(unicode=True, color=True, show_all=True))