Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ReadTheDocs #231

Merged
merged 7 commits into from
Aug 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
line_profiler and kernprof
--------------------------

|Pypi| |Downloads| |CircleCI| |GithubActions| |Codecov|
|Pypi| |ReadTheDocs| |Downloads| |CircleCI| |GithubActions| |Codecov|


NOTICE: This is the official ``line_profiler`` repository. The most recent
Expand All @@ -11,6 +11,14 @@ points to this repo. The original
`@rkern <https://github.com/rkern/>`_ is unmaintained. This fork is the
official continuation of the project.

+---------------+--------------------------------------------+
| Github | https://github.com/pyutils/line_profiler |
+---------------+--------------------------------------------+
| Pypi | https://pypi.org/project/line_profiler |
+---------------+--------------------------------------------+
| ReadTheDocs | https://kernprof.readthedocs.io/en/latest/ |
+---------------+--------------------------------------------+

----


Expand Down Expand Up @@ -184,6 +192,9 @@ item to the extensions list::
'line_profiler',
]

Or explicitly call::

%load_ext line_profiler

To get usage help for %lprun, use the standard IPython help mechanism::

Expand Down Expand Up @@ -288,6 +299,20 @@ built on ``cProfile`` or ``line_profiler`` are as follows:
.. _spyder_line_profiler_plugin: https://github.com/spyder-ide/spyder-line-profiler
.. _web_profiler_ui: https://github.com/mirecl/pprof


Related Work
============

Check out these other Python profilers:

* `Scalene <https://github.com/plasma-umass/scalene>`_: A CPU+GPU+memory sampling based profiler.

* `PyInstrument <https://github.com/joerick/pyinstrument>`_: A call stack profiler.

* `Yappi <https://github.com/sumerc/yappi>`_: A tracing profiler that is multithreading, asyncio and gevent aware.

* `profile / cProfile <https://docs.python.org/3/library/profile.html>`_: The builtin profile module.

Frequently Asked Questions
==========================

Expand Down Expand Up @@ -426,4 +451,5 @@ See `CHANGELOG`_.
:target: https://pypistats.org/packages/line_profiler
.. |GithubActions| image:: https://github.com/pyutils/line_profiler/actions/workflows/tests.yml/badge.svg?branch=main
:target: https://github.com/pyutils/line_profiler/actions?query=branch%3Amain

.. |ReadTheDocs| image:: https://readthedocs.org/projects/kernprof/badge/?version=latest
:target: http://ubelt.readthedocs.io/en/latest/
3 changes: 2 additions & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

https://docs.readthedocs.io/en/stable/guides/autobuild-docs-for-pull-requests.html

https://readthedocs.org/dashboard/line-profiler/advanced/
https://readthedocs.org/dashboard/kernprof/advanced/

ensure your github account is connected to readthedocs
https://readthedocs.org/accounts/social/connections/
Expand Down Expand Up @@ -164,6 +164,7 @@ def visit_Assign(self, node):
'networkx': ('https://networkx.org/documentation/stable/', None),
'scriptconfig': ('https://scriptconfig.readthedocs.io/en/latest/', None),
'xdev': ('https://xdev.readthedocs.io/en/latest/', None),
'rich': ('https://rich.readthedocs.io/en/latest/', None),

}
__dev_note__ = """
Expand Down
22 changes: 19 additions & 3 deletions kernprof.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ def positive_float(value):
help='Code to execute before the code to profile')
parser.add_argument('-v', '--view', action='store_true',
help='View the results of the profile in addition to saving it')
parser.add_argument('-r', '--rich', action='store_true',
help='Use rich formatting if viewing output')
parser.add_argument('-u', '--unit', default='1e-6', type=positive_float,

help='Output unit (in seconds) in which the timing info is '
Expand Down Expand Up @@ -239,10 +241,17 @@ def positive_float(value):
import line_profiler
prof = line_profiler.LineProfiler()
options.builtin = True
# Overwrite the explicit profile decorator
line_profiler.profile._kernprof_overwrite(prof)
else:
prof = ContextualProfile()

# If line_profiler is installed, then overwrite the explicit decorator
try:
import line_profiler
except ImportError:
...
else:
line_profiler.profile._kernprof_overwrite(prof)

if options.builtin:
builtins.__dict__['profile'] = prof

Expand Down Expand Up @@ -275,11 +284,18 @@ def positive_float(value):
print('Wrote profile results to %s' % options.outfile)
if options.view:
if isinstance(prof, ContextualProfile):
prof.print_stats(stream=original_stdout)
prof.print_stats()
else:
prof.print_stats(output_unit=options.unit,
stripzeros=options.skip_zero,
rich=options.rich,
stream=original_stdout)
else:
print('Inspect results with:')
if isinstance(prof, ContextualProfile):
print(f'{sys.executable} -m pstats "{options.outfile}"')
else:
print(f'{sys.executable} -m line_profiler -rmt "{options.outfile}"')


if __name__ == '__main__':
Expand Down
119 changes: 114 additions & 5 deletions line_profiler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

The line_profiler module for doing line-by-line profiling of functions

+---------------+-------------------------------------------+
| Github | https://github.com/pyutils/line_profiler |
+---------------+-------------------------------------------+
| Pypi | https://pypi.org/project/line_profiler |
+---------------+-------------------------------------------+
+---------------+--------------------------------------------+
| Github | https://github.com/pyutils/line_profiler |
+---------------+--------------------------------------------+
| Pypi | https://pypi.org/project/line_profiler |
+---------------+--------------------------------------------+
| ReadTheDocs | https://kernprof.readthedocs.io/en/latest/ |
+---------------+--------------------------------------------+


Installation
Expand All @@ -20,7 +22,114 @@

pip install line_profiler


Basic Usage
===========

To demonstrate line profiling, we first need to generate a Python script to
profile. Write the following code to a file called ``demo_primes.py``.

.. code:: python

from line_profiler import profile


@profile
def is_prime(n):
'''
Check if the number "n" is prime, with n > 1.

Returns a boolean, True if n is prime.
'''
max_val = n ** 0.5
stop = int(max_val + 1)
for i in range(2, stop):
if n % i == 0:
return False
return True


@profile
def find_primes(size):
primes = []
for n in range(size):
flag = is_prime(n)
if flag:
primes.append(n)
return primes


@profile
def main():
print('start calculating')
primes = find_primes(100000)
print(f'done calculating. Found {len(primes)} primes.')

main()


In this script we explicitly import the ``profile`` function from
``line_profiler``, and then we decorate function of interest with ``@profile``.

By default nothing is profiled when running the script.

.. code:: bash

python demo_primes.py


The output will be

.. code::

start calculating
done calculating. Found 9594 primes.


The quickest way to enable profiling is to set the environment variable
``LINE_PROFILE=1`` and running your script as normal.


.. code:: bash

LINE_PROFILE=1 python demo_primes.py

This will output 3 files: profile_output.txt, profile_output_<timestamp>.txt,
and profile_output.lprof and stdout will look something like:


.. code::

start calculating
done calculating. Found 9594 primes.
Timer unit: 1e-09 s

0.65 seconds - demo_primes.py:4 - is_prime
1.47 seconds - demo_primes.py:19 - find_primes
1.51 seconds - demo_primes.py:29 - main
Wrote profile results to profile_output.txt
Wrote profile results to profile_output_2023-08-12T193302.txt
Wrote profile results to profile_output.lprof
To view details run:
python -m line_profiler -rtmz profile_output.lprof


For more control over the outputs, run your script using :py:mod:`kernprof`.
The following invocation will run your script, dump results to
``demo_primes.py.lprof``, and display results.

.. code:: bash

python -m kernprof -lvr demo_primes.py


Note: the ``-r`` flag will use "rich-output" if you have the :py:mod:`rich`
module installed.

"""
# Note: there are better ways to generate primes
# https://github.com/Sylhare/nprime

__submodules__ = [
'line_profiler',
'ipython_extension',
Expand Down
6 changes: 6 additions & 0 deletions line_profiler/_line_profiler.pyx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#cython: language_level=3
"""
This is the Cython backend used in :py:mod:`line_profiler.line_profiler`.
"""
from .python25 cimport PyFrameObject, PyObject, PyStringObject
from sys import byteorder
cimport cython
Expand Down Expand Up @@ -160,6 +163,9 @@ cdef class LineProfiler:
"""
Time the execution of lines of Python code.

This is the Cython base class for
:class:`line_profiler.line_profiler.LineProfiler`.

Example:
>>> import copy
>>> import line_profiler
Expand Down
40 changes: 31 additions & 9 deletions line_profiler/explicit_profiler.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
"""
The idea is that we are going to expose a top-level ``profile`` decorator which
will be disabled by default **unless** you are running with with line profiler
itself OR if the LINE_PROFILE environment variable is True.
New in ``line_profiler`` version 4.1.0, this module defines a top-level
``profile`` decorator which will be disabled by default **unless** a script is
being run with :mod:`kernprof`, if the environment variable ``LINE_PROFILE`` is
set, or if ``--line-profile`` is given on the command line.

This uses the :mod:`atexit` module to perform a profile dump at the end.
In the latter two cases, the :mod:`atexit` module is used to display and dump
line profiling results to disk when Python exits.

This work is ported from :mod:`xdev`.
If none of the enabling conditions are met, then
:py:obj:`line_profiler.profile` is a noop. This means you no longer have to add
and remove the implicit ``profile`` decorators required by previous version of
this library.

Basic usage is to import line_profiler and decorate your function with
line_profiler.profile. By default this does nothing, it's a no-op decorator.
However, if you run with the environment variable ``LINE_PROFILER=1`` or if
``'--profile' in sys.argv'``, then it enables profiling and at the end of your
script it will output the profile text.

Here is a minimal example:
Here is a minimal example that will write a script to disk and then run it
with profiling enabled or disabled by various methods:

.. code:: bash

Expand Down Expand Up @@ -71,7 +77,9 @@ def main():
LINE_PROFILE=1 python demo.py


An example with in-code enabling:
The explicit :py:attr:`line_profiler.profile` decorator can also be enabled and
configured in the Python code itself by calling
:func:`line_profiler.profile.enable`. The following example demonstrates this:

.. code:: bash

Expand Down Expand Up @@ -99,7 +107,10 @@ def fib(n):
python demo.py


An example with in-code enabling and disabling:
Likewise there is a :func:`line_profiler.profile.disable` function that will
prevent any subsequent functions decorated with ``@profile`` from being
profiled. In the following example, profiling information will only be recorded
for ``func2`` and ``func4``.

.. code:: bash

Expand Down Expand Up @@ -145,7 +156,11 @@ def func4():
echo "---"
echo "## Configuration handled inside the script"
python demo.py

# Running with --line-profile will also profile ``func1``
python demo.py --line-profile

The core functionality in this module was ported from :mod:`xdev`.
"""
from .line_profiler import LineProfiler
import sys
Expand All @@ -160,6 +175,8 @@ class GlobalProfiler:
"""
Manages a profiler that will output on interpreter exit.

The :py:obj:`line_profile.profile` decorator is an instance of this object.

Attributes:
setup_config (Dict[str, List[str]]):
Determines how the implicit setup behaves by defining which
Expand Down Expand Up @@ -302,6 +319,10 @@ def __call__(self, func):
Returns:
Callable: a potentially wrapped function
"""
# from multiprocessing import current_process
# if current_process().name != 'MainProcess':
# return func

if self.enabled is None:
# Force a setup if we haven't done it before.
self._implicit_setup()
Expand All @@ -313,7 +334,8 @@ def show(self):
"""
Write the managed profiler stats to enabled outputs.

If the implicit setup triggered, then this will be called by atexit.
If the implicit setup triggered, then this will be called by
:py:mod:`atexit`.
"""
import io
import pathlib
Expand Down
7 changes: 6 additions & 1 deletion line_profiler/ipython_extension.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
"""
This module defines the ``%lprun`` IPython magic.
"""

from io import StringIO

from IPython.core.magic import Magics, magics_class, line_magic
Expand All @@ -16,7 +20,8 @@ def lprun(self, parameter_s=""):
line_profiler module.

Usage:
%lprun -f func1 -f func2 <statement>

%lprun -f func1 -f func2 <statement>

The given statement (which doesn't require quote marks) is run via the
LineProfiler. Profiling is enabled for the functions specified by the -f
Expand Down
Loading