Skip to main content

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

Release files for nanowatch 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for nanowatch 0.1.3
File Size Uploaded
nanowatch-0.1.3.tar.gz 15.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for nanowatch 0.1.3
File Interpreter ABI Platform
nanowatch-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 31.5 kB

Release files / nanowatch-0.1.3.tar.gz

Download URL nanowatch-0.1.3.tar.gz
Size 15.9 kB
Tags Source
SHA-256 checksum
How to use checksums
3931158d4f50bff35bd87dab8f081e9de415a7106285e1c399b9513c336d3394
BLAKE2b-256 checksum
How to use checksums
ff81e556843509f627f4b2935f4feba5b9b21020258101a2bfb853d44dd20ffe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 22, 2026.

Transparency log

Release files / nanowatch-0.1.3-py3-none-any.whl

Download URL nanowatch-0.1.3-py3-none-any.whl
Size 15.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c20141b5400f61e88c71ebb0ccf67f3b52292279862da5fca2efdd6c834076d8
BLAKE2b-256 checksum
How to use checksums
854dc28beb3ca36ddda4bfbcb7a356ef8493c5ce4e21abca7fb6016a9274d1b5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 22, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.3 This release

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page