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.0.tar.gz (15.3 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.0-py3-none-any.whl (14.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: nanowatch-0.1.0.tar.gz
  • Upload date:
  • Size: 15.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for nanowatch-0.1.0.tar.gz
Algorithm Hash digest
SHA256 6acde8c8cfdb5b541573878a823422ae88bf7d66fe01368087e364c33397757d
MD5 b08402b9ab5a862e036c8ca732ef53ae
BLAKE2b-256 b0b3c2c08ba653f2a85da4dba5fdb5a3b611650c0e540afbe106579bd364ca9f

See more details on using hashes here.

File details

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

File metadata

  • Download URL: nanowatch-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 14.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for nanowatch-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d8ca1762c9e296fdef0e30a40bd270de4b62c94accc2af4133e4cd9089758671
MD5 656debfbfc9804a853afc5ccd44a599a
BLAKE2b-256 be8731cbfda6c502f7fb974efa1428cd8a9fd11bf9f708cc75f093e27502acb0

See more details on using hashes here.

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