Pyinstrument - a Python profiler. A profiler is a tool to help you optimize your code - make it faster.

Overview

pyinstrument

PyPI version .github/workflows/test.yml Build wheels

Documentation

Screenshot

Pyinstrument is a Python profiler. A profiler is a tool to help you optimize your code - make it faster. To get the biggest speed increase you should focus on the slowest part of your program. Pyinstrument helps you find it!

Installation

pip install pyinstrument

Pyinstrument supports Python 3.7+.

To run Pyinstrument from a git checkout, there's a build step. Take a look at Contributing for more info.

Documentation

To learn how to use pyinstrument, or to check the reference, head to the documentation.

Known issues

  • Profiling code inside a Docker container can cause some strange results, because the gettimeofday syscall that pyinstrument uses is slow in that environment. See #83
  • When using pyinstrument script.py where script.py contains a class serialized with pickle, you might encounter errors because the serialisation machinery doesn't know where __main__ is. See this issue for workarounds

Changelog

v4.1.1

  • Fixed an issue causing PYINSTRUMENT_PROFILE_DIR_RENDERER to output the wrong file extension when used with the speedscope renderer.

v4.1.0

  • You can now use pyinstrument natively in an IPython notebook! Just use %load_ext pyinstrument at the top of your notebook, and then %%pyinstrument in the cell you want to profile.
  • Added support for the speedscope format. This provides a way to view interactive flamecharts using pyinstrument. To use, profile with pyinstrument -r speedscope, and upload to the speedscope web app.
  • You can now configure renderers for the Django middleware file output, using the PYINSTRUMENT_PROFILE_DIR_RENDERER option.
  • Added wheels for Linux aarch64 (64-bit ARM).

v4.0.4

  • Fix a packaging issue where a package called 'test' was installed alongside pyinstrument
  • Use more modern C APIs to resolve deprecation warnings on Python 3.10.
  • Minor docs fixes

v4.0.3

  • CPython 3.10 support
  • Improve error messages when trying to use Profiler from multiple threads
  • Fix crash when rendering sessions that contain a module in a FrameGroup

v4.0.2

  • Fix some packaging issues

v4.0.0

  • Async support! Pyinstrument now detects when an async task hits an await, and tracks time spent outside of the async context under this await.

    So, for example, here's a simple script with an async task that does a sleep:

    import asyncio
    from pyinstrument import Profiler
    
    async def main():
        p = Profiler(async_mode='disabled')
    
        with p:
            print('Hello ...')
            await asyncio.sleep(1)
            print('... World!')
    
        p.print()
    
    asyncio.run(main())

    Before Pyinstrument 4.0.0, we'd see only time spent in the run loop, like this:

      _     ._   __/__   _ _  _  _ _/_   Recorded: 18:33:03  Samples:  2
     /_//_/// /_\ / //_// / //_'/ //     Duration: 1.006     CPU time: 0.001
    /   _/                      v3.4.2
    
    Program: examples/async_example_simple.py
    
    1.006 _run_once  asyncio/base_events.py:1784
    └─ 1.005 select  selectors.py:553
          [3 frames hidden]  selectors, 
         
          
             1.005 kqueue.control  
          
           :0
    
          
         

    Now, with pyinstrument 4.0.0, we get:

      _     ._   __/__   _ _  _  _ _/_   Recorded: 18:30:43  Samples:  2
     /_//_/// /_\ / //_// / //_'/ //     Duration: 1.007     CPU time: 0.001
    /   _/                      v4.0.0
    
    Program: examples/async_example_simple.py
    
    1.006 main  async_example_simple.py:4
    └─ 1.005 sleep  asyncio/tasks.py:641
          [2 frames hidden]  asyncio
             1.005 [await]
    

    For more information, check out the async profiling documentation and the Profiler.async_mode property.

  • Pyinstrument has a documentation site, including full Python API docs!

v3.4.2

  • Fix a bug that caused --show, --show-regex, --show-all to be ignored on the command line.

v3.4.1

  • Under-the-hood modernisation

v3.4.0

  • Added timeline option (boolean) to Profiler methods output_html() and open_in_browser().

v3.3.0

  • Fixed issue with pyinstrument -m module, where pyinstrument wouldn't find modules in the current directory.
  • Dropped support for Python 2.7 and 3.5. Old versions will remain available on PyPI, and pip should choose the correct one automatically.

v3.2.0

  • Added the ability to track time in C functions. Minor note - Pyinstrument will record time spent C functions as 'leaf' functions, due to a limitation in how Python records frames. Python -> C -> Python is recorded as Python -> Python, but Python -> Python -> C will be attributed correctly. (#103)

v3.1.2

  • Fix <__array_function__ internals> frames appearing as app code in reports

v3.1.1

  • Added support for timeline mode on HTML and JSON renderers
  • Released as a tarball as well as a universal wheel

v3.1.0

  • Added PYINSTRUMENT_SHOW_CALLBACK option on the Django middleware to add a condition to showing the profile (could be used to run pyinstrument on a live server!)
  • Fixed bug in the Django middleware where file would not be written because of a unicode error

v3.0.3

  • Fixed bug with the Django middleware on Windows where profiling would fail because we were trying to put an illegal character '?' in the profile path. (#66)

v3.0.2

  • Add --show and --show-regex options, to mark certain files to be displayed. This helps to profile inside specific modules, while hiding others. For example, pyinstrument --show '*/sympy/*' script.py.

v3.0.1

  • Fix #60: pass all arguments after -m module_name to the called module
  • Fix crash during HTML/JSON output when no frames were captured.

v3.0.0

  • Pyinstrument will now hide traces through libraries that you're using by default. So instead of showing you loads of frames going through the internals of something external e.g. urllib, it lets you focus on your code.

    Before After
    image image

    To go back to the old behaviour, use --show-all on the command line.

  • 'Entry' frames of hidden groups are shown, so you know which call is the problem

  • Really slow frames in the groups are shown too, e.g. the 'read' call on the socket

  • Application code is highlighted in the console

  • Additional metrics are shown at the top of the trace - timestamp, number of samples, duration, CPU time

  • Hidden code is controlled by the --hide or --hide-regex options - matching on the path of the code files.

      --hide=EXPR           glob-style pattern matching the file paths whose
                            frames to hide. Defaults to '*/lib/*'.
      --hide-regex=REGEX    regex matching the file paths whose frames to hide.
                            Useful if --hide doesn't give enough control.
    
  • Outputting a timeline is supported from the command line.

      -t, --timeline        render as a timeline - preserve ordering and don't
                            condense repeated calls
    
  • Because there are a few rendering options now, you can load a previous profiling session using --load-prev - pyinstrument keeps the last 10 sessions.

  • Hidden groups can also call back into application code, that looks like this:

    image

  • (internal) When recording timelines, frame trees are completely linear now, allowing for the creation of super-accurate frame charts.

  • (internal) The HTML renderer has been rewritten as a Vue.js app. All the console improvements apply to the HTML output too, plus it's interactive.

  • (internal) A lot of unit and integration tests added!

Yikes! See #49 for the gory details. I hope you like it.

v2.3.0

  • Big refactor!
    • Recorders have been removed. The frame recording is now internal to the Profiler object. This means the 'frame' objects are more general-purpose, which paves the way for...
    • Processors! These are functions that mutate the tree to sculpt the output. They are used by the renderers to filter the output to the correct form. Now, instead of a time-aggregating recorder, the profiler just uses timeline-style recording (this is lower-overhead anyway) and the aggregation is done as a processing step.
    • The upshot of this is that it's now way easier to alter the tree to filter stuff out, and do more advanced things like combining frames that we don't care about. More features to come that use this in v3.0!
  • Importlib frames are removed - you won't see them at all. Their children are retained, so imports are just transparent.
  • Django profile file name is now limited to a hundred of characters (#50)
  • Fix bug with --html option (#53)
  • Add --version command line option

v2.2.1

  • Fix crash when using on the command line.

v2.2.0

  • Added support for JSON output. Use pyinstrument --renderer=json scriptfile.py. PR

  • @iddan has put together an interactive viewer using the JSON output!

    image

  • When running pyinstrument --html and you don't pipe the output to a file, pyinstrument will write the console output to a temp file and open that in a browser.

v2.1.0

  • Added support for running modules with pyinstrument via the command line. The new syntax is the -m flag e.g. pyinstrument -m module_name! PR

v2.0.4

v2.0.3

  • Pyinstrument can now be used in a with block.

    For example:

    profiler = pyinstrument.Profiler()
    with profiler:
        # do some work here...
    print(profiler.output_text())
    
  • Middleware fix for older versions of Django

v2.0.2

  • Fix for max recursion error when used to profile programs with a lot of frames on the stack.

v2.0.1

  • Ensure license is included in the sdist.

v2.0.0

  • Pyinstrument uses a new profiling mode. Rather than using signals, pyintrument uses a new statistical profiler built on PyEval_SetProfile. This means no more main thread restriction, no more IO errors when using Pyinstrument, and no need for a separate more 'setprofile' mode!

  • Renderers. Users can customize Pyinstrument to use alternative renderers with the renderer argument on Profiler.output(), or using the --renderer argument on the command line.

  • Recorders. To support other use cases of Pyinstrument (e.g. flame charts), pyinstrument now has a 'timeline' recorder mode. This mode records captured frames in a linear way, so the program execution can be viewed on a timeline.

v0.13

  • pyinstrument command. You can now profile python scripts from the shell by running $ pyinstrument script.py. This is now equivalent to python -m pyinstrument. Thanks @asmeurer!

v0.12

  • Application code is highlighted in HTML traces to make it easier to spot

  • Added PYINSTRUMENT_PROFILE_DIR option to the Django interface, which will log profiles of all requests to a file the specified folder. Useful for profiling API calls.

  • Added PYINSTRUMENT_USE_SIGNAL option to the Django interface, for use when signal mode presents problems.

Contributing

To setup a dev environment:

virtualenv --python=python3 env
. env/bin/activate
pip install --upgrade pip
pip install -r requirements-dev.txt
pre-commit install --install-hooks

To get some sample output:

pyinstrument examples/wikipedia_article_word_count.py

To run the tests:

pytest

To run linting checks locally:

pre-commit run --all-files

Some of the pre-commit checks, like isort or black, will auto-fix the problems they find. So if the above command returns an error, try running it again, it might succeed the second time :)

Running all the checks can be slow, so you can also run checks individually, e.g., to format source code that fails isort or black checks:

pre-commit run --all-files isort
pre-commit run --all-files black

To diagnose why pyright checks are failing:

pre-commit run --all-files pyright

The HTML renderer Vue.js app

The HTML renderer works by embedding a JSON representation of the sample with a Javascript 'bundle' inside an HTML file that can be viewed in any web browser.

To edit the html renderer style, do:

cd html_renderer
npm ci
npm run serve

When launched without a top-level window.profileSession object, it will fetch a sample profile so you can work with it.

To compile the JS app and bundle it back into the pyinstrument python tool:

bin/build_js_bundle.py [--force]
Comments
  • feature request: dump and analyze

    feature request: dump and analyze

    It is cool to print beautiful callstacks after we stop the profiler and call output_text/html. However, this only gives users one angle to look at data: full-depth (the whole callstack is printed) and caller-based (caller are parents, and callee functions are children on the tree).

    In many cases, it is useful if we can analyze the same profiling data from different angles. For example:

    (1) callee-based: for development in a big software framework, new functions are part of the framework, and are often at the bottom or at most in the middle of the call stack. If the profiler can only print callstack as caller-based, it would take a lot of time to find the data people care about.

    (2) reduced callstack depth:if I only want a big picture, I may want to filter the results with an option like "depth=1" or "depth=2" etc, to print only functions at the top or bottom of the callstack. This option should work in either caller-based or callee-based stack. I know in web UI, the stack tree and fold and expand so it doesn't have to be changed, but the text mode doesn't have this option, and if we want to generate reports, we need to process text output.

    In order to do this, we might want to separate result display into two steps. Step1: dump the data. Step 2: process the data.

    Step 1 conceptually works as it does now: when profiler is stopped, it "dump" profiling data into a user specified or default file name.

    Step 2: this requires a new tool that can understand the dump and display callstacks as a user desires.

    So the point is to give users more angles to analyze without having to profile the same application multiple times.

    Any thoughts?

    enhancement 
    opened by codelol 14
  • caught AssertionError: Please raise an issue at https://github.com/joerick/pyinstrument/issues and let me know how you caused this error!

    caught AssertionError: Please raise an issue at https://github.com/joerick/pyinstrument/issues and let me know how you caused this error!

    version: 4.1.1 I've been able to write a pytest scenario reproducing the error:

    app code:

    #!/usr/bin/env python3
    from typing import Callable
    
    from fastapi import FastAPI
    from pyinstrument import Profiler
    from starlette.requests import Request
    from starlette.responses import Response
    
    
    class ProfilingMiddleware:
        """ middleware that profiles request """
    
        def __init__(self, profiler_interval: float = 0.0001, ):
            self._profiler = Profiler(interval=profiler_interval)
    
        async def __call__(self, request: Request, call_next: Callable) -> Response:
            """ profile request """
            self._profiler.start()
            try:
                response = await call_next(request)
            finally:
                self._profiler.stop()
                output = self._profiler.output_text(color=True)
                print(output)
            return response
    
    
    def create_app() -> FastAPI:
        """ application factory """
        app = FastAPI()
    
        @app.post('/')
        def endpoint():
            """ endpoint example """
            print('inside endpoint')
            return {}
    
        app.middleware('http')(ProfilingMiddleware())
    
        return app
    
    
    APP = create_app()
    

    test code:

    from fastapi.testclient import TestClient
    
    from app import APP
    
    client = TestClient(APP)
    
    
    def test_case() -> None:
        """ test 2 requests lead to an error while 1 passes """
        client.post(url='/')
    
        client.get(url='/dfhjhjkdfs')  # no matter which request you send you get the error (tried switching method/url etc)
    

    executing the test leads to the following:

    =================================================================================================================== test session starts ====================================================================================================================
    platform darwin -- Python 3.9.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 -- /Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/bin/python
    cachedir: .pytest_cache
    rootdir: /Users/erezm/repos/ec-notification-service, configfile: pytest.ini, testpaths: .
    plugins: anyio-3.5.0, pythonpath-0.7.3
    collected 1 item                                                                                                                                                                                                                                           
    
    tests/feature_test/test_app_routes.py::test_case FAILED                                                                                                                                                                                              [100%]
    
    ========================================================================================================================= FAILURES =========================================================================================================================
    ________________________________________________________________________________________________________________________ test_case _________________________________________________________________________________________________________________________
    Traceback (most recent call last):
      File "/Users/erezm/repos/ec-notification-service/tests/feature_test/test_app_routes.py", line 12, in test_case
        client.get(url='/dfhjhjkdfs')
      File "/Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/lib/python3.9/site-packages/requests/sessions.py", line 555, in get
        return self.request('GET', url, **kwargs)
      File "/Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/lib/python3.9/site-packages/starlette/testclient.py", line 468, in request
        return super().request(
      File "/Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/lib/python3.9/site-packages/requests/sessions.py", line 542, in request
        resp = self.send(prep, **send_kwargs)
      File "/Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/lib/python3.9/site-packages/requests/sessions.py", line 655, in send
        r = adapter.send(request, **kwargs)
      File "/Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/lib/python3.9/site-packages/starlette/testclient.py", line 266, in send
        raise exc
      File "/Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/lib/python3.9/site-packages/starlette/testclient.py", line 263, in send
        portal.call(self.app, scope, receive, send)
      File "/Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/lib/python3.9/site-packages/anyio/from_thread.py", line 240, in call
        return cast(T_Retval, self.start_task_soon(func, *args).result())
      File "/Users/erezm/.pyenv/versions/3.9.6/lib/python3.9/concurrent/futures/_base.py", line 445, in result
        return self.__get_result()
      File "/Users/erezm/.pyenv/versions/3.9.6/lib/python3.9/concurrent/futures/_base.py", line 390, in __get_result
        raise self._exception
      File "/Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/lib/python3.9/site-packages/anyio/from_thread.py", line 187, in _call_func
        retval = await retval
      File "/Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/lib/python3.9/site-packages/fastapi/applications.py", line 208, in __call__
        await super().__call__(scope, receive, send)
      File "/Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/lib/python3.9/site-packages/starlette/applications.py", line 112, in __call__
        await self.middleware_stack(scope, receive, send)
      File "/Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/lib/python3.9/site-packages/starlette/middleware/errors.py", line 181, in __call__
        raise exc
      File "/Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in __call__
        await self.app(scope, receive, _send)
      File "/Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/lib/python3.9/site-packages/starlette/middleware/base.py", line 55, in __call__
        response = await self.dispatch_func(request, call_next)
      File "/Users/erezm/repos/ec-notification-service/src/app.py", line 24, in __call__
        output = self._profiler.output_text(color=True)
      File "/Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/lib/python3.9/site-packages/pyinstrument/profiler.py", line 295, in output_text
        return self.output(
      File "/Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/lib/python3.9/site-packages/pyinstrument/profiler.py", line 323, in output
        return renderer.render(session)
      File "/Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/lib/python3.9/site-packages/pyinstrument/renderers/console.py", line 34, in render
        frame = self.preprocess(session.root_frame())
      File "/Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/lib/python3.9/site-packages/pyinstrument/session.py", line 145, in root_frame
        assert root_frame is None, ASSERTION_MESSAGE
    AssertionError: Please raise an issue at https://github.com/joerick/pyinstrument/issues and let me know how you caused this error!
    ------------------------------------------------------------------------------------------------------------------- Captured stdout call -------------------------------------------------------------------------------------------------------------------
    inside endpoint
    
      _     ._   __/__   _ _  _  _ _/_   Recorded: 19:21:02  Samples:  15
     /_//_/// /_\ / //_// / //_'/ //     Duration: 0.002     CPU time: 0.002
    /   _/                      v4.1.1
    
    Program: /Users/erezm/.local/share/virtualenvs/ec-notification-service-6WygbirR/bin/pytest -k test_app_routes -vv
    
    0.002 run_forever  asyncio/base_events.py:583
    |- 0.001 _run_once  asyncio/base_events.py:1815
    |     [49 frames hidden]  asyncio, starlette, fastapi, anyio, t...
    |        0.000 __call__  starlette/middleware/base.py:20
    |        `- 0.000 __call__  app.py:17
    |           `- 0.000 stop  pyinstrument/profiler.py:136
    |                 [3 frames hidden]  pyinstrument
    `- 0.000 [self]  
    
    
    =================================================================================================================== slowest 5 durations ====================================================================================================================
    0.07s call     tests/feature_test/test_app_routes.py::test_case
    0.00s setup    tests/feature_test/test_app_routes.py::test_case
    0.00s teardown tests/feature_test/test_app_routes.py::test_case
    ================================================================================================================= short test summary info ==================================================================================================================
    FAILED tests/feature_test/test_app_routes.py::test_case - AssertionError: Please raise an issue at https://github.com/joerick/pyinstrument/issues and let me know how you caused this error!
    ==================================================================================================================== 1 failed in 0.32s =====================================================================================================================
    

    note that the test passes if a single request is issued.

    my environment:

    Package            Version
    ------------------ ---------
    anyio              3.5.0
    asgiref            3.4.1
    astroid            2.8.6
    attrs              21.4.0
    certifi            2021.10.8
    charset-normalizer 2.0.10
    click              8.0.3
    fastapi            0.70.0
    gunicorn           20.0.4
    h11                0.12.0
    idna               3.3
    iniconfig          1.1.1
    isort              5.10.1
    lazy-object-proxy  1.7.1
    mccabe             0.6.1
    ntplib             0.4.0
    packaging          21.3
    pip                21.3.1
    platformdirs       2.4.1
    pluggy             1.0.0
    prometheus-client  0.11.0
    py                 1.11.0
    pydantic           1.9.0
    pyinstrument       4.1.1
    pylint             2.11.1
    pyparsing          3.0.6
    pytest             6.2.5
    pytest-pythonpath  0.7.3
    python-multipart   0.0.5
    redis              3.5.3
    requests           2.26.0
    setuptools         59.8.0
    six                1.16.0
    sniffio            1.2.0
    starlette          0.16.0
    starlette-exporter 0.8.2
    texel-common       0.8.12
    texel-logging      0.1.3
    toml               0.10.2
    typing_extensions  4.0.1
    urllib3            1.26.8
    uvicorn            0.15.0
    wheel              0.37.1
    wrapt              1.13.3
    
    • running on macOS 12.1 (though it reproduced on ubuntu machines as well)
    opened by erez-marmor 12
  • Contributing instructions not working in a vanilla environment

    Contributing instructions not working in a vanilla environment

    Hello,

    I thought I'd sit down this evening and knock out #119 but I was stopped when the Contributing instructions didn't work for me. After 20 minutes of troubleshooting I figured maybe the community here may have some quick insight to get me up and running.

    I'm running with pyenv, created and activated an environment with Python 3.9.0 and I'm using node v15.5.0.

    Here is the output of python setup.py build running in my local directory:

    
    (pyinstrument) ➜  pyinstrument git:(master)  python setup.py build
    running build
    running build_py
    7.3.0
    Running command: npm install
    
    added 1482 packages, removed 7 packages, and audited 1657 packages in 23s
    
    17 vulnerabilities (10 low, 1 moderate, 6 high)
    
    To address all issues, run:
      npm audit fix
    
    Run `npm audit` for details.
    Running command: npm run build
    
    > [email protected] build
    > vue-cli-service build
    
    node:internal/modules/cjs/loader:928
      throw err;
      ^
    
    Error: Cannot find module 'json-parse-even-better-errors'
    Require stack:
    - /Users/steven.tartakovsky/code/pyinstrument/html_renderer/node_modules/read-pkg/node_modules/parse-json/index.js
    - /Users/steven.tartakovsky/code/pyinstrument/html_renderer/node_modules/read-pkg/index.js
    - /Users/steven.tartakovsky/code/pyinstrument/html_renderer/node_modules/@vue/cli-service/lib/Service.js
    - /Users/steven.tartakovsky/code/pyinstrument/html_renderer/node_modules/@vue/cli-service/bin/vue-cli-service.js
        at Function.Module._resolveFilename (node:internal/modules/cjs/loader:925:15)
        at Function.Module._load (node:internal/modules/cjs/loader:769:27)
        at Module.require (node:internal/modules/cjs/loader:997:19)
        at require (node:internal/modules/cjs/helpers:92:18)
        at Object.<anonymous> (/Users/steven.tartakovsky/code/pyinstrument/html_renderer/node_modules/read-pkg/node_modules/parse-json/index.js:3:18)
        at Module._compile (node:internal/modules/cjs/loader:1108:14)
        at Object.Module._extensions..js (node:internal/modules/cjs/loader:1137:10)
        at Module.load (node:internal/modules/cjs/loader:973:32)
        at Function.Module._load (node:internal/modules/cjs/loader:813:14)
        at Module.require (node:internal/modules/cjs/loader:997:19) {
      code: 'MODULE_NOT_FOUND',
      requireStack: [
        '/Users/steven.tartakovsky/code/pyinstrument/html_renderer/node_modules/read-pkg/node_modules/parse-json/index.js',
        '/Users/steven.tartakovsky/code/pyinstrument/html_renderer/node_modules/read-pkg/index.js',
        '/Users/steven.tartakovsky/code/pyinstrument/html_renderer/node_modules/@vue/cli-service/lib/Service.js',
        '/Users/steven.tartakovsky/code/pyinstrument/html_renderer/node_modules/@vue/cli-service/bin/vue-cli-service.js'
      ]
    }
    npm ERR! code 1
    npm ERR! path /Users/steven.tartakovsky/code/pyinstrument/html_renderer
    npm ERR! command failed
    npm ERR! command sh -c vue-cli-service build
    
    npm ERR! A complete log of this run can be found in:
    npm ERR!     /Users/steven.tartakovsky/.npm/_logs/2021-02-14T02_01_54_525Z-debug.log
    Traceback (most recent call last):
      File "/Users/steven.tartakovsky/code/pyinstrument/setup.py", line 70, in <module>
        setup(
      File "/Users/steven.tartakovsky/.pyenv/versions/pyinstrument/lib/python3.9/site-packages/setuptools/__init__.py", line 165, in setup
        return distutils.core.setup(**attrs)
      File "/Users/steven.tartakovsky/.pyenv/versions/3.9.0/lib/python3.9/distutils/core.py", line 148, in setup
        dist.run_commands()
      File "/Users/steven.tartakovsky/.pyenv/versions/3.9.0/lib/python3.9/distutils/dist.py", line 966, in run_commands
        self.run_command(cmd)
      File "/Users/steven.tartakovsky/.pyenv/versions/3.9.0/lib/python3.9/distutils/dist.py", line 985, in run_command
        cmd_obj.run()
      File "/Users/steven.tartakovsky/.pyenv/versions/3.9.0/lib/python3.9/distutils/command/build.py", line 135, in run
        self.run_command(cmd_name)
      File "/Users/steven.tartakovsky/.pyenv/versions/3.9.0/lib/python3.9/distutils/cmd.py", line 313, in run_command
        self.distribution.run_command(command)
      File "/Users/steven.tartakovsky/.pyenv/versions/3.9.0/lib/python3.9/distutils/dist.py", line 985, in run_command
        cmd_obj.run()
      File "/Users/steven.tartakovsky/code/pyinstrument/setup.py", line 38, in run
        self.check_call(['npm', 'run', 'build'], cwd=HTML_RENDERER_DIR)
      File "/Users/steven.tartakovsky/code/pyinstrument/setup.py", line 14, in check_call
        subprocess.check_call(args, **popen_kwargs)
      File "/Users/steven.tartakovsky/.pyenv/versions/3.9.0/lib/python3.9/subprocess.py", line 373, in check_call
        raise CalledProcessError(retcode, cmd)
    subprocess.CalledProcessError: Command '['npm', 'run', 'build']' returned non-zero exit status 1.
    
    opened by startakovsky 12
  • skip hidden frames

    skip hidden frames

    fixes #190

    updated version of #211. It was easier to just start a new PR than it was to resolve the conflicts after the py 3.11 updates. From the original PR:

    This adds a generic "hidden" property to the frame record objects. Currently this is only set to true if the frame has tracebackhide==True in it's locals dict, as discussed in https://github.com/joerick/pyinstrument/issues/190. Also adds a default processor that hides any frames with .hidden==True.

    Not sure if this is the right approach overall, and very not sure if the changes to the c extension were the right ones. @joerick Feedback would be much appreciated.

    I also added the .devcontainer dir that I came up with while developing these changes. This allows for developing pyinstrument in a container when using vscode (and maybe other ides?). Specifically, this allowed me to do stuff like simultaneously debug the python and c extension code (even though I'm developing on a mac), which was super useful. Dunno if you want this in the repo, I can remove it if you prefer.

    opened by telamonian 10
  • Added asyncio support to cell magic

    Added asyncio support to cell magic

    The current version of the pyinstrument magic does not support async code at all. It doesn't matter whether you have async_mode set to enabled or disabled, it will break because it will try to run in a new event loop.

    This seems to be a known upstream issue but I don't think anyone is working on it: https://github.com/ipython/ipython/issues/11314

    This modification is certainly very hacky, but it's working quite well for me and I don't foresee any issues with it. If it does cause issues, it doesn't do anything when using --async_mode=disabled :)

    opened by wolph 10
  • Add support for IPython/Jupyter notebook magics

    Add support for IPython/Jupyter notebook magics

    Example usage (works in ipython/notebook/lab):

    %load_ext pyinstrument
    
    %%pyinstrument
    def a():
        b()
        c()
    def b():
        d()
    def c():
        d()
    def d():
        e()
    def e():
        time.sleep(1)
    a()
    

    image

    Extra care is taken to not profile jupyter running the code, which is the majority of the complexity of this commit.

    Fixes: #153

    opened by mlucool 10
  • Asyncio applications have a mysterious [SELF] function

    Asyncio applications have a mysterious [SELF] function

    The first function is labelled as [SELF] and i'm not sure how to interpret it. Is it how long the event loop took to get back to the function or something else?

    For context, i'm creating a profiler before and closing it after every request in Sanic. Its a Flask-like asyncio powered web framework.

    opened by AggressivelyMeows 10
  • Add option to lookup and run program from path

    Add option to lookup and run program from path

    Uses runpy.run_path & shutil.which. Implements #97. Any comments on the implementation welcome.

    Not sure about the name of the option (--path currently), and it would be good to include a test for it too, I assume. Hence marked as draft.

    opened by chrish42 9
  • Feature request: multiple renderings

    Feature request: multiple renderings

    For the profiling workflow I'm creating, it would be super useful to be able to ask for multiple renderings of a report. For example, an HTML render to show to humans, and a JSON one for post-processing scripts to work from.

    I'd be more than happy to do a pull request. The only thing that would need a bit of discussion beforehand is how to represent that in the command-line interface.

    opened by chrish42 9
  • AssertionError when use flask+gunicorn+gevent

    AssertionError when use flask+gunicorn+gevent

    Hai ! I encount this AssertionError when I use pyinstrument to hook my flask app like the examples/flask_hello.py said.

    AssertionError: Please raise an issue at http://github.com/pyinstrument/issues and let me know how you caused this error!
    

    This error doesn't happen every time I send request. Instead, several consecutive requests occur once. Then Nginx will return 500 status code.

    Here is the full trackback stack from gunicorn error.log file:

    [2020-01-22 04:34:28 +0800] [26251] [ERROR] Error handling request /v2/services/xxxx/xxxx/get?key1=value1&key2=value2&key3=value3&__profile__
    Traceback (most recent call last):
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/gunicorn/workers/base_async.py", line 55, in handle
        self.handle_request(listener_name, req, client, addr)
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/gunicorn/workers/ggevent.py", line 143, in handle_request
        super().handle_request(listener_name, req, sock, addr)
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/gunicorn/workers/base_async.py", line 106, in handle_request
        respiter = self.wsgi(environ, resp.start_response)
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/flask/app.py", line 2463, in __call__
        return self.wsgi_app(environ, start_response)
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/werkzeug/middleware/proxy_fix.py", line 228, in __call__
        return self.app(environ, start_response)
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/flask/app.py", line 2449, in wsgi_app
        response = self.handle_exception(e)
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/flask/app.py", line 1866, in handle_exception
        reraise(exc_type, exc_value, tb)
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/flask/_compat.py", line 39, in reraise
        raise value
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/flask/app.py", line 2446, in wsgi_app
        response = self.full_dispatch_request()
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/flask/app.py", line 1952, in full_dispatch_request
        return self.finalize_request(rv)
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/flask/app.py", line 1969, in finalize_request
        response = self.process_response(response)
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/flask/app.py", line 2266, in process_response
        response = handler(response)
      File "/home/pyserver/www/wsgi.py", line 44, in after_request
        output_html = g.profiler.output_html()
      File "</home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/pyinstrument/vendor/decorator.py:decorator-gen-9>", line 2, in output_html
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/pyinstrument/util.py", line 37, in caller
        return func(*args, **kwargs)
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/pyinstrument/profiler.py", line 101, in output_html
        return renderers.HTMLRenderer().render(self.last_session)
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/pyinstrument/renderers/html.py", line 23, in render
        session_json = self.render_json(session)
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/pyinstrument/renderers/html.py", line 69, in render_json
        return json_renderer.render(session)
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/pyinstrument/renderers/jsonrenderer.py", line 36, in render
        frame = self.preprocess(session.root_frame())
      File "/home/pyserver/.pyenv/versions/3.6.6/envs/envpy36/lib/python3.6/site-packages/pyinstrument/session.py", line 77, in root_frame
        assert root_frame is None, ASSERTION_MESSAGE
    AssertionError: Please raise an issue at http://github.com/pyinstrument/issues and let me know how you caused this error!
    

    Here is my gunicorn.conf.py file

    import os
    import multiprocessing
    
    chdir = WSGI_APP_ROOT
    
    daemon = True
    accesslog = os.path.join(LOG_DIR, "access.log")
    errorlog  = os.path.join(LOG_DIR, "error.log")
    
    pidfile = os.path.join(RUN_DIR, "gunicorn.pid")
    
    user  = 33  # www-data
    group = 33  # www-data
    
    bind = [ "unix:" + os.path.join(RUN_DIR, "gunicorn.sock") ]
    
    workers = multiprocessing.cpu_count() * 2 + 1
    worker_class = "gevent"
    worker_connections = 1024
    threads = multiprocessing.cpu_count() * 3 
    
    timeout = 15
    graceful_timeout = 15
    keepalive = 5
    

    Here is my wsgi.py module

    PYINSTRUMENT_HOOK          = True
    PYINSTRUMENT_PROFILE_FIELD = "__profile__"
    
    from gevent.monkey import patch_all
    patch_all()
    
    import os
    from werkzeug.middleware.proxy_fix import ProxyFix
    from v2.app import create_app
    
    app = create_app("development")
    
    if PYINSTRUMENT_HOOK:
    
        from flask import g, request, make_response
        from pyinstrument import Profiler
    
        @app.before_request
        def before_request():
            if PYINSTRUMENT_PROFILE_FIELD in request.args:
                g.profiler = Profiler()
                g.profiler.start()
    
        @app.after_request
        def after_request(response):
            if not hasattr(g, "profiler"):
                return response
            g.profiler.stop()
            output_html = g.profiler.output_html()
            return make_response(output_html)
    
    app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1)
    
    

    The requirements.txt

    Flask==1.1.1
    Werkzeug==0.15.4 
    greenlet==0.4.15
    gevent==1.4.0
    gunicorn==20.0.4
    pyinstrument==3.1.0
    

    The pyenv version

    Python 3.6.6
    
    opened by zhongxinghong 9
  • Getting IOError

    Getting IOError

    rahul@rahul-desktop { ~/pycharm-workspace/ceres_new }-> python -m pyinstrument ceres/sim/sim_definition.py Traceback (most recent call last): File "/usr/lib/python2.7/runpy.py", line 162, in run_module_as_main "main", fname, loader, pkg_name) File "/usr/lib/python2.7/runpy.py", line 72, in run_code exec code in run_globals File "/home/rahul/src/pyinstrument/pyinstrument/main.py", line 135, in main() File "/home/rahul/src/pyinstrument/pyinstrument/main.py", line 79, in main exec(code, globs, None) File "/home/rahul/src/pyinstrument/pyinstrument/main.py", line 25, in exec exec("exec code in globs, locs") File "", line 1, in File "ceres/sim/sim_definition.py", line 48, in main() File "ceres/sim/sim_definition.py", line 31, in main from aglyph.assembler import Assembler File "/usr/local/lib/python2.7/dist-packages/aglyph/init.py", line 61, in platform.platform())) File "/usr/lib/python2.7/platform.py", line 1576, in platform system,node,release,version,machine,processor = uname() File "/usr/lib/python2.7/platform.py", line 1273, in uname processor = _syscmd_uname('-p','') File "/usr/lib/python2.7/platform.py", line 1029, in _syscmd_uname output = string.strip(f.read()) IOError: [Errno 4] Interrupted system call Alarm clock

    opened by rahul-paul 9
  • Fix show all

    Fix show all

    fixes #223

    This PR address the hide/show bug that @joerick pointed out in #223. I'll open separate issues for the other discussed in that issue.

    This turned out to be more complex than I would have thought for rendering related junk. In part this was because the --show-all cmd line option was initially busted. Passing --show-all on the cmd line will now ensure that show_all=True gets passed to the kwargs of the Renderer's constructor. I also fixed the behavior of the base Render's constructor when show_all=True. This together appears to fix the bug pointed out in #223. @joerick Can you please test this PR out when you have a sec and let me know if that part also seems fixed to you?

    opened by telamonian 0
  • `FileNotFoundError` when calling profile.output_text

    `FileNotFoundError` when calling profile.output_text

    Apologies - I'm having trouble making a minimal reproducible example for this issue, but here's what I'm seeing:

    In certain situations, when trying to render pyinstrument output as text I get a FileNotFoundError. In post mortem debugging the stack looks like:

    -> ] = profile.output_text(unicode=True, color=True)
      <python path>/pyinstrument/profiler.py(295)output_text()
    -> return self.output(
      <python path>/pyinstrument/profiler.py(323)output()
    -> return renderer.render(session)
      <python path>/pyinstrument/renderers/console.py(51)render()
    -> result += self.render_frame(self.root_frame)
      <python path>/pyinstrument/renderers/console.py(137)render_frame()
    -> result += self.render_frame(child, indent=c_indent, child_indent=cc_indent)
      <python path>/pyinstrument/renderers/console.py(137)render_frame()
    -> result += self.render_frame(child, indent=c_indent, child_indent=cc_indent)
      <python path>/pyinstrument/renderers/console.py(137)render_frame()
    -> result += self.render_frame(child, indent=c_indent, child_indent=cc_indent)
      <python path>/pyinstrument/renderers/console.py(118)render_frame()
    -> libraries=truncate(", ".join(frame.group.libraries), 40),
    > <python path>/pyinstrument/frame.py(349)libraries()
    -> if frame.file_path_short:
      <python path>/pyinstrument/frame.py(160)file_path_short()
    -> candidate = os.path.relpath(self.file_path, path)
      <python path>/python3.9/posixpath.py(473)relpath()
    -> path_list = [x for x in abspath(path).split(sep) if x]
      <python path>/python3.9/posixpath.py(380)abspath()
    -> cwd = os.getcwd()
    

    So, I don't entirely understand why os.getcwd() is failing but I don't think that's due to pyinstrument.

    What is maybe strange to me is the Frame triggering the issue looks like:

    Frame(identifier=Int64HashTable.factorize<built-in>0, time=0.002000, len(children)=0), group=FrameGroup(len(frames)=29)
    

    And if I look on https://github.com/joerick/pyinstrument/blob/main/pyinstrument/frame.py#L160,

    (Pdb) self.file_path
    '<built-in>'
    (Pdb) self._identifier_parts
    ['Int64HashTable.factorize', '<built-in>', '0']
    

    Which makes me think that pyinstrument shouldn't be trying loop through sys.path and immediately set self._file_path_short = None. Is that the intended behavior here?

    opened by gerrymanoim 0
  • option for CPU time

    option for CPU time

    Pyinstrument records duration using ‘wall-clock’ time. When you’re writing a program that downloads data, reads files, and talks to databases, all that time is included in the tracked time by pyinstrument.

    That’s really important when debugging performance problems, since Python is often used as a ‘glue’ language between other services.

    In the other 50% of python programs out there, CPU efficiency is important. Sure, for a synchronous Python program, CPU and wall time are basically the same, and pyinstrument can be used to find the places consuming CPU.

    However, for async programs, since pyinstrument only offers wall time, it cannot be used to find what code is consuming CPU. For this use case, attributing time to sleep() or network blocking is unwanted.

    opened by belm0 0
  • Followup for IPython integration

    Followup for IPython integration

    I got some feedback from the guys I implemented #217 for:

    For:

    $ cat profiler.py
    import time
    
    def D():
        time.sleep(0.7)
    
    def C():
        __tracebackhide__ = True
        time.sleep(0.1)
        D()
    
    def B():
        __tracebackhide__ = True
        time.sleep(0.1)
        C()
    
    def A():
        time.sleep(0.1)
        B()
    
    A()
    
    

    Profiler output:

    1.004 <module>  <string>:1
       1.004 run_path  runpy.py:245
          1.001 _run_module_code  runpy.py:89
             1.001 _run_code  runpy.py:63
                1.001 <module>  profiler.py:1
                   1.001 A  profiler.py:16
                      0.700 D  profiler.py:3
                         0.700 sleep  None
                            [2 frames hidden]  <built-in>
                      0.301 sleep  None
                         [2 frames hidden]  <built-in>
    
    
    1. Inside A, we would like to see that a few frames (B&C) were hidden because of __tracebackhide__. We also suggest including a >way to expand hidden nodes in HTML format.
    2. I see, sleep None. Should it be <built-in> instead? [Note: I don't see None in Jupyter notebook].

    I'm not entirely sure what of the above is bugs, intended behavior, or new feature requests. The request for expandable hidden nodes in html format seems like a reasonable new feature. I can start looking into making that happen.

    As for "Inside A, we would like to see that a few frames (B&C) were hidden because of __tracebackhide__", I think what they mean is that when a frame is hidden due to the __tracebackhide__ rule, the output should explicitly say that.

    @joerick Can you please clarify exactly what the above example profiling output is currently saying about the hidden frames? I assume the first [2 frames hidden] <built-in> line is refering to the B and C scopes. Is that right? In that case, the <built-in> label is a bug, and should say <ipython built-in> (or something) instead. What does the second repeat of the [2 frames hidden] <built-in> refer to?

    opened by telamonian 1
  • Having issues profiling FastAPI calls

    Having issues profiling FastAPI calls

    First, thanks so much for your hard work on this great library. It is a fantastic addition to the python ecosystem!

    I am implementing profiling in FastAPI as a middleware, listening for a header val or query arg like the example shows. My middleware setup is a little different, but is added to the call stack just the same

    """Middleware that enables on-demand app profiling when a profile header is passed."""
    
    from fastapi import Request, Response
    from fastapi.responses import HTMLResponse, PlainTextResponse
    from pyinstrument import Profiler, renderers
    from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
    
    from app.utils import ErrorReporter, Logger
    
    PROFILING_INTERVAL = 0.001  # 1 millisecond
    PROFILING_ASYNC_MODE = "enabled"
    
    
    class ProfilingMiddleware(BaseHTTPMiddleware):
        """Middleware that enables on-demand app profiling when a profile header is passed."""
    
        async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
            """Middleware that enables on-demand app profiling when a profile header is passed."""
            try:
                profiling = self.should_profile(request)
                if profiling:
                    profiler = Profiler(interval=PROFILING_INTERVAL, async_mode=PROFILING_ASYNC_MODE)
                    profiler.start()
                    await call_next(request)
                    profiler.stop()
                    return HTMLResponse(profiler.output_html())
    
            except Exception as exc:  # pylint: disable=broad-except
                ErrorReporter.report(exc)
                Logger.error(f"Could not profile endpoint: {str(exc)}")
    
            response = await call_next(request)
            return response
    
        @staticmethod
        def should_profile(request: Request) -> bool:
            """Return true if the profiling header or query param was passed."""
            return request.headers.get("X-Profile", False) or request.query_params.get("profile", False)
    

    The profiling works -- in that it is added to the app call stack, is enabled, detects code, renders output. However, there is no useful output that I can get. Here are some examples, in html, and speedscope formats:

    Screen Shot 2022-10-25 at 4 48 31 PM Screen Shot 2022-10-25 at 4 54 16 PM Screen Shot 2022-10-25 at 5 00 19 PM Screen Shot 2022-10-25 at 5 03 12 PM

    So what is custom_route_logger you ask? It is the official FastAPI way to subclass the Request object, and is defined async, like this:

        def get_route_handler(self) -> Callable:
            """Use a custom route handler."""
            original_route_handler = super().get_route_handler()
    
            async def custom_route_handler(request: Request) -> Response:
                request = REDACTEDRequest(request.scope, request.receive)
                return await original_route_handler(request)
    
            return custom_route_handler
    

    I know the problem comes from this async, and because it is ASGI middleware, I cannot take the async away. But I also cannot profile inside the application as-is.

    The goal is to get a single profile of application code on a given api call. My question is, is there some sort of async setup I missed here, or is this an impossible task?

    opened by coneybeare 6
Owner
Joe Rickerby
Technologist @nordprojects. @pypa maintainer.
Joe Rickerby
Visual profiler for Python

vprof vprof is a Python package providing rich and interactive visualizations for various Python program characteristics such as running time and memo

Nick Volynets 3.9k Jan 1, 2023
VizTracer is a low-overhead logging/debugging/profiling tool that can trace and visualize your python code execution.

VizTracer is a low-overhead logging/debugging/profiling tool that can trace and visualize your python code execution.

null 2.8k Jan 8, 2023
The official code of LM-Debugger, an interactive tool for inspection and intervention in transformer-based language models.

LM-Debugger is an open-source interactive tool for inspection and intervention in transformer-based language models. This repository includes the code

Mor Geva 110 Dec 28, 2022
Graphical Python debugger which lets you easily view the values of all evaluated expressions

birdseye birdseye is a Python debugger which records the values of expressions in a function call and lets you easily view them after the function exi

Alex Hall 1.5k Dec 24, 2022
printstack is a Python package that adds stack trace links to the builtin print function, so that editors such as PyCharm can link you to the source of the print call.

printstack is a Python package that adds stack trace links to the builtin print function, so that editors such as PyCharm can link to the source of the print call.

null 101 Aug 26, 2022
PINCE is a front-end/reverse engineering tool for the GNU Project Debugger (GDB), focused on games.

PINCE is a front-end/reverse engineering tool for the GNU Project Debugger (GDB), focused on games. However, it can be used for any reverse-engi

Korcan Karaokçu 1.5k Jan 1, 2023
Debugger capable of attaching to and injecting code into python processes.

DISCLAIMER: This is not an official google project, this is just something I wrote while at Google. Pyringe What this is Pyringe is a python debugger

Google 1.6k Dec 15, 2022
Monitor Memory usage of Python code

Memory Profiler This is a python module for monitoring memory consumption of a process as well as line-by-line analysis of memory consumption for pyth

Fabian Pedregosa 80 Nov 18, 2022
Inject code into running Python processes

pyrasite Tools for injecting arbitrary code into running Python processes. homepage: http://pyrasite.com documentation: http://pyrasite.rtfd.org downl

Luke Macken 2.7k Jan 8, 2023
Visual Interaction with Code - A portable visual debugger for python

VIC Visual Interaction with Code A simple tool for debugging and interacting with running python code. This tool is designed to make it easy to inspec

Nathan Blank 1 Nov 16, 2021
Hunter is a flexible code tracing toolkit.

Overview docs tests package Hunter is a flexible code tracing toolkit, not for measuring coverage, but for debugging, logging, inspection and other ne

Ionel Cristian Mărieș 705 Dec 8, 2022
pdb++, a drop-in replacement for pdb (the Python debugger)

pdb++, a drop-in replacement for pdb What is it? This module is an extension of the pdb module of the standard library. It is meant to be fully compat

null 1k Dec 24, 2022
Full-screen console debugger for Python

PuDB: a console-based visual debugger for Python Its goal is to provide all the niceties of modern GUI-based debuggers in a more lightweight and keybo

Andreas Klöckner 2.6k Jan 1, 2023
Trace any Python program, anywhere!

lptrace lptrace is strace for Python programs. It lets you see in real-time what functions a Python program is running. It's particularly useful to de

Karim Hamidou 687 Nov 20, 2022
Debugging manhole for python applications.

Overview docs tests package Manhole is in-process service that will accept unix domain socket connections and present the stacktraces for all threads

Ionel Cristian Mărieș 332 Dec 7, 2022
(OLD REPO) Line-by-line profiling for Python - Current repo ->

line_profiler and kernprof line_profiler is a module for doing line-by-line profiling of functions. kernprof is a convenient script for running either

Robert Kern 3.6k Jan 6, 2023
Parsing ELF and DWARF in Python

pyelftools pyelftools is a pure-Python library for parsing and analyzing ELF files and DWARF debugging information. See the User's guide for more deta

Eli Bendersky 1.6k Jan 4, 2023
pdb++, a drop-in replacement for pdb (the Python debugger)

pdb++, a drop-in replacement for pdb What is it? This module is an extension of the pdb module of the standard library. It is meant to be fully compat

null 1k Jan 2, 2023
Run-time type checker for Python

This library provides run-time type checking for functions defined with PEP 484 argument (and return) type annotations. Four principal ways to do type

Alex Grönholm 1.1k Jan 5, 2023