Skip to main content

High-precision Python performance measurement toolkit

Project description

nanowatch

High-precision Python performance measurement toolkit. Nanosecond accuracy. Zero dependencies. Minimal output.


Install

pip install nanowatch

Or from source:

pip install -e .

Interfaces

1. Function Decorator

from nanowatch import watch

@watch
def compute(n):
    return sum(range(n))

# With a custom label
@watch("heavy computation")
def compute(n):
    return sum(range(n))

# Async functions work identically
@watch
async def fetch_data(url):
    ...

Output:

  compute                                       1.243 ms

2. Code Block Context Manager

from nanowatch import watch_block

with watch_block("db query"):
    results = db.execute(sql)

3. Inline Call (no decorator needed)

from nanowatch import watch_call

data = watch_call(json.loads, raw_string, name="parse response")

4. Class Mixin (auto-instruments all public methods)

from nanowatch import WatchedMixin

class UserService(WatchedMixin):
    _watch_prefix = "UserService"   # optional, defaults to class name

    def fetch_user(self, user_id):
        ...

    def save_user(self, user):
        ...

    def _internal_helper(self):     # skipped (underscore prefix)
        ...

Every call to fetch_user or save_user is automatically timed.

Custom collector via DI:

from nanowatch import WatchedMixin, Collector

my_collector = Collector()

class OrderService(WatchedMixin):
    _watch_collector = my_collector
    _watch_prefix = "OrderService"

    def create_order(self, data):
        ...

5. WSGI Middleware (Flask, Django, etc.)

from nanowatch import WsgiMiddleware

# Flask
app.wsgi_app = WsgiMiddleware(app.wsgi_app)

# Django (in wsgi.py)
application = WsgiMiddleware(get_wsgi_application())

Output per request:

  HTTP GET /api/users                           4.231 ms  [method=GET, path=/api/users]

6. ASGI Middleware (FastAPI, Starlette, etc.)

from nanowatch import AsgiMiddleware

# FastAPI
app.add_middleware(AsgiMiddleware)

# Or manually wrap
app = AsgiMiddleware(app)

7. Line Profiler (checkpoint-based)

Measures time between named points inside a function.

from nanowatch import LineProfiler

def process_order(order):
    prof = LineProfiler("process_order")

    validate(order)
    prof.mark("validated")

    result = db.save(order)
    prof.mark("saved to db")

    notify(order)
    prof.mark("notified")

    prof.finish()

Output:

  process_order | validated                      312 us  [session=process_order, checkpoint=validated]
  process_order | saved to db                  2.841 ms  [session=process_order, checkpoint=saved to db]
  process_order | notified                     1.102 ms  [session=process_order, checkpoint=notified]
  [nanowatch] session 'process_order' complete (3 checkpoints)
  ------------------------------------------------------------------------

Reports

Print summary to stdout

import nanowatch

# ... run your code ...

nanowatch.summary()

Output:

========================================================================
  nanowatch | Performance Summary
  2025-06-01 14:32:10
========================================================================
  UserService.fetch_user
    calls : 48
    min   : 812 us
    max   : 4.231 ms
    avg   : 1.103 ms
    total : 52.944 ms
------------------------------------------------------------------------
  HTTP GET /api/users                          4.231 ms  [method=GET, path=/api/users]
------------------------------------------------------------------------
  Total tracked time : 1.204 s
  Total measurements : 57
========================================================================

Save to JSON file

nanowatch.save("perf_results.json")
{
  "generated_at": "2025-06-01T14:32:10.123456",
  "total_measurements": 57,
  "records": [
    {
      "name": "UserService.fetch_user",
      "duration_ns": 1103000,
      "duration_us": 1103.0,
      "duration_ms": 1.103,
      "duration_s": 0.001103,
      "context": {}
    }
  ],
  "groups": {
    "UserService.fetch_user": {
      "count": 48,
      "min_ns": 812000,
      "max_ns": 4231000,
      "avg_ns": 1103000,
      "total_ns": 52944000
    }
  }
}

Custom Collector (Isolation / Testing)

All interfaces accept an optional collector parameter for DI:

from nanowatch import watch, Collector

test_collector = Collector()

@watch(collector=test_collector)
def my_fn():
    ...

my_fn()
print(test_collector.stats("my_fn"))

Reset

nanowatch.reset()   # clears the global collector

Precision

All measurements use time.perf_counter_ns, Python's highest-resolution monotonic clock. Results are stored as raw integers (nanoseconds) and converted only for display.


Project Structure

src/nanowatch/
  core/
    timer.py          # Timer, TimingRecord
    collector.py      # Collector, default_collector
  interfaces/
    decorators.py     # @watch, watch_block, watch_call
    mixin.py          # WatchedMixin
    middleware.py     # WsgiMiddleware, AsgiMiddleware
    line_profiler.py  # LineProfiler
  output/
    formatter.py      # console + file output

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

nanowatch-0.1.3.tar.gz (15.9 kB view details)

Uploaded Source

Built Distribution

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

nanowatch-0.1.3-py3-none-any.whl (15.6 kB view details)

Uploaded Python 3

File details

Details for the file nanowatch-0.1.3.tar.gz.

File metadata

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

File hashes

Hashes for nanowatch-0.1.3.tar.gz
Algorithm Hash digest
SHA256 3931158d4f50bff35bd87dab8f081e9de415a7106285e1c399b9513c336d3394
MD5 7cbbbe460af68decd397b944d06ca7cb
BLAKE2b-256 ff81e556843509f627f4b2935f4feba5b9b21020258101a2bfb853d44dd20ffe

See more details on using hashes here.

Provenance

The following attestation bundles were made for nanowatch-0.1.3.tar.gz:

Publisher: publish.yml on munjed-ab/nanowatch

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

File details

Details for the file nanowatch-0.1.3-py3-none-any.whl.

File metadata

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

File hashes

Hashes for nanowatch-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 c20141b5400f61e88c71ebb0ccf67f3b52292279862da5fca2efdd6c834076d8
MD5 a37f6ba7762c836d7c172ca0fa15c636
BLAKE2b-256 854dc28beb3ca36ddda4bfbcb7a356ef8493c5ce4e21abca7fb6016a9274d1b5

See more details on using hashes here.

Provenance

The following attestation bundles were made for nanowatch-0.1.3-py3-none-any.whl:

Publisher: publish.yml on munjed-ab/nanowatch

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