Skip to main content

Python code speed analyzer

Project description

PyHaste

GitHub Workflow Status Code style: ruff Pre-commit PyPI PyPI - Python Version License: BSD 3-Clause

Python code speed analyzer.

PyHaste screenshot

Monitor the performance of your scripts etc. tools and understand where time is spent.

Installation

It's a Python library, what do you expect?

pip install pyhaste
# OR
poetry add pyhaste

Normal usage

To measure your code, pyhaste exports a measure context manager, give it a name as an argument. Alternatively wrap functions in measure_wrap with the name as an argument. Once you want a report call report from pyhaste.

import time

from pyhaste import measure, report, measure_wrap


@measure_wrap("prepare_task")
def prepare_task():
  time.sleep(0.1)


@measure_wrap("find_items")
def find_items():
  return [1, 2, 3]


@measure_wrap("process_item")
def process_item(item):
  time.sleep(item * 0.1)


with measure("task"):
  prepare_task()

  for item in find_items():
    process_item(item)

time.sleep(0.01)
report()
────────────────────────── PyHaste report ───────────────────────────

┏━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━━━┓
┃ Name               ┃    Time ┃  Tot % ┃  Rel % ┃ Calls ┃ Per call ┃
┡━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━━━┩
│ task               │ 0.700 s │ 98.58% │        │     1 │  0.700 s │
│ task ›process_item │ 0.600 s │ 84.49% │ 85.70% │     3 │  0.200 s │
│ task ›prepare_task │ 0.100 s │ 14.09% │ 14.29% │     1 │  0.100 s │
│ Unmeasured         │ 0.010 s │  1.42% │        │       │          │
│ task ›find_items   │ 0.000 s │  0.00% │  0.00% │     1 │  0.000 s │
├────────────────────┼─────────┼────────┼────────┼───────┼──────────┤
│ Total              │ 0.710 s │   100% │        │       │          │
└────────────────────┴─────────┴────────┴────────┴───────┴──────────┘

In case you need more complex analysis, you might benefit from pyhaste.Analyzer and creating your own instances, e.g. for measuring time spent on separate tasks in a longer running job:

import time
from random import uniform
from pyhaste import Analyzer

for item in [1, 2, 3]:
  analyzer = Analyzer()
  with analyzer.measure(f"process_item({item})"):
    with analyzer.measure("db.find"):
      time.sleep(uniform(0.04, 0.06) * item)
    with analyzer.measure("calculate"):
      with analyzer.measure("guestimate"):
        with analyzer.measure("do_math"):
          time.sleep(uniform(0.1, 0.15) * item)
    with analyzer.measure("save"):
      time.sleep(uniform(0.05, 0.075) * item)
  time.sleep(uniform(0.01, 0.025) * item)
  analyzer.report()
────────────────────────────────────────── PyHaste report ──────────────────────────────────────────

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━┓
┃ Name                                            ┃    Time ┃  Tot % ┃   Rel % ┃ Calls ┃ Per call ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━┩
│ process_item(1)                                 │ 0.232 s │ 92.26% │         │     1 │  0.232 s │
│ process_item(1) ›calculate                      │ 0.122 s │ 48.38% │  52.44% │     1 │  0.122 s │
│ process_item(1) ›calculate ›guestimate          │ 0.122 s │ 48.38% │ 100.00% │     1 │  0.122 s │
│ process_item(1) ›calculate ›guestimate ›do_math │ 0.122 s │ 48.37% │  99.99% │     1 │  0.122 s │
│ process_item(1) ›save                           │ 0.058 s │ 23.23% │  25.18% │     1 │  0.058 s │
│ process_item(1) ›db.find                        │ 0.052 s │ 20.64% │  22.37% │     1 │  0.052 s │
│ Unmeasured                                      │ 0.019 s │  7.74% │         │       │          │
├─────────────────────────────────────────────────┼─────────┼────────┼─────────┼───────┼──────────┤
│ Total                                           │ 0.251 s │   100% │         │       │          │
└─────────────────────────────────────────────────┴─────────┴────────┴─────────┴───────┴──────────┘

────────────────────────────────────────── PyHaste report ──────────────────────────────────────────

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━┓
┃ Name                                            ┃    Time ┃  Tot % ┃   Rel % ┃ Calls ┃ Per call ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━┩
│ process_item(2)                                 │ 0.511 s │ 94.66% │         │     1 │  0.511 s │
│ process_item(2) ›calculate                      │ 0.288 s │ 53.38% │  56.40% │     1 │  0.288 s │
│ process_item(2) ›calculate ›guestimate          │ 0.288 s │ 53.38% │ 100.00% │     1 │  0.288 s │
│ process_item(2) ›calculate ›guestimate ›do_math │ 0.288 s │ 53.38% │  99.99% │     1 │  0.288 s │
│ process_item(2) ›save                           │ 0.125 s │ 23.10% │  24.41% │     1 │  0.125 s │
│ process_item(2) ›db.find                        │ 0.098 s │ 18.16% │  19.19% │     1 │  0.098 s │
│ Unmeasured                                      │ 0.029 s │  5.34% │         │       │          │
├─────────────────────────────────────────────────┼─────────┼────────┼─────────┼───────┼──────────┤
│ Total                                           │ 0.540 s │   100% │         │       │          │
└─────────────────────────────────────────────────┴─────────┴────────┴─────────┴───────┴──────────┘

────────────────────────────────────────── PyHaste report ──────────────────────────────────────────

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━┓
┃ Name                                            ┃    Time ┃  Tot % ┃   Rel % ┃ Calls ┃ Per call ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━┩
│ process_item(3)                                 │ 0.749 s │ 93.21% │         │     1 │  0.749 s │
│ process_item(3) ›calculate                      │ 0.368 s │ 45.84% │  49.18% │     1 │  0.368 s │
│ process_item(3) ›calculate ›guestimate          │ 0.368 s │ 45.84% │ 100.00% │     1 │  0.368 s │
│ process_item(3) ›calculate ›guestimate ›do_math │ 0.368 s │ 45.84% │ 100.00% │     1 │  0.368 s │
│ process_item(3) ›save                           │ 0.217 s │ 27.07% │  29.04% │     1 │  0.217 s │
│ process_item(3) ›db.find                        │ 0.163 s │ 20.29% │  21.77% │     1 │  0.163 s │
│ Unmeasured                                      │ 0.055 s │  6.79% │         │       │          │
├─────────────────────────────────────────────────┼─────────┼────────┼─────────┼───────┼──────────┤
│ Total                                           │ 0.803 s │   100% │         │       │          │
└─────────────────────────────────────────────────┴─────────┴────────┴─────────┴───────┴──────────┘

Async usage and FastAPI example

Async Python causes some problems for following the stack, so we can't create these nested call stacks and relative calculations automatically. If you're ok with just making sure your names have the necessary degree of identification in some other way, you can use the async version by importing from pyhaste.async_analyzer. There's also an additional measure_wrap_async.

See pyhaste_async_demo.py and the FastAPI demo for usage examples.

If you've got good ideas on how we can reliably track the call stack context in async Python code please do share 🙂

Development

Issues and PRs are welcome!

Please open an issue first to discuss the idea before sending a PR so that you know if it would be wanted or needs re-thinking or if you should just make a fork for yourself.

For local development, make sure you install pre-commit, then run:

pre-commit install
poetry install
poetry run ptw .
poetry run python example.py

cd fastapi_example
poetry run python example.py

License

The code is released under the BSD 3-Clause license. Details in the LICENSE.md file.

Financial support

This project has been made possible thanks to Cocreators and Lietu. You can help us continue our open source work by supporting us on Buy me a coffee.

"Buy Me A Coffee"

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyhaste-1.1.4.tar.gz (6.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyhaste-1.1.4-py3-none-any.whl (7.8 kB view details)

Uploaded Python 3

File details

Details for the file pyhaste-1.1.4.tar.gz.

File metadata

  • Download URL: pyhaste-1.1.4.tar.gz
  • Upload date:
  • Size: 6.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pyhaste-1.1.4.tar.gz
Algorithm Hash digest
SHA256 09580275ffe96f053443e2651dd39aaf75abe68df152e4b49b8c4f1b1293b33a
MD5 9b72bb1bf2d1aaf26044472997b9d9fe
BLAKE2b-256 93da4905a2bd17ea06f936572040ce368e2a7b8ceae69c3aae05e35fbbb30b6d

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyhaste-1.1.4.tar.gz:

Publisher: publish.yaml on cocreators-ee/pyhaste

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyhaste-1.1.4-py3-none-any.whl.

File metadata

  • Download URL: pyhaste-1.1.4-py3-none-any.whl
  • Upload date:
  • Size: 7.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pyhaste-1.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 d39ae70cf5c89370a162cf245acd121e863089fdf73496052dec1da33e1abb9f
MD5 a0ac95d69e4dd7b38fabfbfbd1c2664c
BLAKE2b-256 04b20ff23bd0f8cac15c58978549b47449b9efad1b8b843e46ab9a2ac35e0617

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyhaste-1.1.4-py3-none-any.whl:

Publisher: publish.yaml on cocreators-ee/pyhaste

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page