Skip to main content

Siren

Minimal Python debug helper with automatic cleanup.

A tiny debugging utility for Python that prints variables with file/line context, traces function calls, measures execution time, and safely removes debug calls from your code.

PyPI - Version PyPI - Python Version License: MIT


Install

pip install siren-debug

The package also installs siren-clean (remove debug calls), siren-autoload (use siren without importing it), and several other siren-* tools. Run siren (no arguments) for a full list of every command and what it does, filterable by area: siren pro, siren snippet.


Quick Start

from siren import siren

x = 10
user = {"name": "Alex", "items": [1, 2, 3]}

siren(x)
siren(user)
[🧜‍ SIREN core.py:10] x = 10
[🧜‍ SIREN core.py:11] user = {'name': 'Alex', 'items': [1, 2, 3]}

Siren automatically uses pprint for complex objects, and picks up the file/line it was called from.


Features

  • Works with Python 2.7 and 3.6+
  • Zero external dependencies for the free tier (certifi is the only dependency, pulled in for reliable HTTPS in the pro tier's siren-login/siren-events)
  • Prints values with file and line number
  • Uses pprint automatically for complex data
  • Function tracing with @siren.trace, object diffing with siren.diff, an interactive siren.breakpoint(), memory snapshots with siren.memory(), and colored traceback capture with siren.catch
  • Quiet mode, conditional logging, and file logging
  • Removes siren(...) calls automatically with siren-clean
  • Use siren anywhere without importing it via siren-autoload
  • Project/file scaffolding with siren-scaffold, .env drift checks with siren-env
  • Terminal snippet manager (siren-snippet) and a dependency-free HTTP client (siren-http)
  • Local code-quality checks with siren-quality (dead code, lint, cyclomatic complexity)
  • siren lists every command and what it does, filterable by area (siren pro, siren snippet)
  • Performance metrics with siren.profile/siren.profile_block and siren-perf top to rank real bottlenecks over time (pro tier)
  • Works in scripts, CLI tools, Django, Flask, FastAPI, and more
  • Colored output with emoji for easy visual scanning

Usage

Call siren(...) with one or more values. It returns them unchanged, so it can be inlined:

from siren import siren

siren(x, data, user)
result = siren(compute())  # still returns compute()'s value

Label — tag a call for easier scanning:

siren(value, label="BEFORE SAVE")

Timer — measure execution time for a call:

siren(x, timeit=True)
# [🧜‍ SIREN core.py:10] x = 10
# [🧜‍ SIREN TIME] 0.000123s

Quiet mode — suppress output without removing the call:

siren(x, quiet=True)      # this call only, still returns x
siren.set_quiet(True)     # every call, until set_quiet(False)

Conditional logging — only print when a condition holds:

siren(x, if_equals=5)        # only if x == 5
siren(items, if_len_gt=100)  # only if len(items) > 100
siren(items, if_len_lt=5)    # only if len(items) < 5
siren(result, if_true=True)  # only if result is truthy
siren(error, if_false=True)  # only if error is falsy

Logging to file — mirror output to a file:

siren.set_logfile("debug.log")
siren(x)  # prints to stdout AND writes to debug.log

Inspect configuration:

config = siren.get_config()
print(config)  # {"quiet": False, "logfile": None, "enabled": True}

Function tracing

@siren.trace logs a function's calls, arguments, return value, execution time, and exceptions automatically:

from siren import trace

@siren.trace
def add(a, b):
    return a + b

add(2, 3)
[🧜‍ SIREN core.py:10] Calling add(a=2, b=3)
[🧜‍ SIREN core.py:11] Returned from add -> 5 [int] (0.000123s)

Configuration options (all default to True):

Option Effect
timeit Show execution time
show_args Show function arguments
show_return Show return value
show_type Show return type in brackets
@siren.trace(timeit=True, show_args=False, show_type=False)
def multiply(a, b):
    return a * b

Exceptions are logged before being re-raised, so @siren.trace never swallows an error:

@siren.trace
def divide(a, b):
    return a / b

divide(5, 0)  # Logs exception before raising

Diff, breakpoint, memory, and catch

siren.diff compares two dicts, lists, tuples, or any comparable objects:

before = {"name": "Alice", "age": 30}
after = {"name": "Alice", "age": 31, "city": "NYC"}

siren.diff(before, after)
[🧜‍ SIREN test.py:10] DIFF
[🧜‍ SIREN test.py:11] [~] age: 30 → 31 (changed)
[🧜‍ SIREN test.py:12] [+] city: NYC (new)

siren.breakpoint() pauses execution and prints local variables:

x = 42
data = {"items": [1, 2, 3]}

siren.breakpoint()  # Pauses and displays all locals
# Press Ctrl+C to continue, or type 'd' to drop into pdb

siren.memory() prints current/peak traced memory usage (requires Python 3.4+; prints a clear message instead of failing on Python 2):

siren.memory()          # [🧜‍ SIREN MEMORY ...] current=1.2MB peak=1.5MB
siren.memory(top=5)     # also print the top 5 allocation sites

siren.catch is a context manager that prints a colored traceback on exception and re-raises it — it never swallows errors:

with siren.catch():
    risky_call()

Cleaning debug calls

Run siren-clean in a project folder to remove all siren(...) calls and their import lines — comments and string literals are left untouched:

siren-clean

Before:

from siren import siren
siren(x)
print("hello")
siren(data)

After:

print("hello")

Autoload (no per-file imports)

By default you still need from siren import siren in every file that uses it. If you'd rather call siren(x) anywhere in a project without importing it each time, enable autoload once per environment (virtualenv, Docker image, CI job, etc.):

siren-autoload on
siren-autoload status   # check whether it's enabled
siren-autoload off      # disable again

This writes a .pth file into the current environment's site-packages, injecting siren into Python's builtins as soon as any interpreter starts in that environment — no import needed anywhere, including in Django apps, Flask views, scripts, or the shell. It's opt-in per environment, so it won't silently affect environments where you didn't run on.


Beyond debugging

Siren also ships a handful of small, dependency-free CLI tools for everyday project work.

Scaffolding — siren-scaffold

Generate a small file or project skeleton:

siren-scaffold script my_tool       # a single script with a main() guard
siren-scaffold package my_package   # a package dir with __init__.py, core.py, and tests/
siren-scaffold class Widget         # a plain class
siren-scaffold dataclass Point      # a plain-Python value object (no dataclasses module needed)
siren-scaffold test Widget          # a unittest.TestCase stub

It refuses to overwrite existing files.

.env drift check — siren-env

siren-env diff                                    # compares .env.example against .env
siren-env diff --example .env.sample --env .env.local

Reports keys present in one file but missing from the other, and exits non-zero on drift — usable as a CI check.

Snippets — siren-snippet

echo "print('hello')" | siren-snippet save greet --tag python
siren-snippet save query --file query.sql --tag sql   # from a file instead of stdin
siren-snippet show greet
siren-snippet copy greet                               # sends it straight to the clipboard
siren-snippet edit greet                                # opens it in $EDITOR
siren-snippet rename greet hello
siren-snippet list [--tag sql]
siren-snippet tags                                      # every tag in use, with counts
siren-snippet search select                              # matches by name, tag, or content
siren-snippet remove greet

save refuses to overwrite an existing snippet unless you pass --force — this also applies to rename.

Snippets can hold {{placeholder}} markers, filled in on the way out instead of when saved:

echo 'SELECT * FROM {{table}};' | siren-snippet save query --tag sql
siren-snippet copy query --var table=users   # copies "SELECT * FROM users;"
siren-snippet show query --var table=users   # same, printed instead of copied

Back up or move your snippets between machines with export/import (content, tags, and timestamps all round-trip; import skips names that already exist unless you pass --force):

siren-snippet export backup.json
siren-snippet import backup.json

Snippets are stored as plain text files under ~/.siren/snippets/, with tags/timestamps tracked separately in ~/.siren/snippets/_index.json (so any snippet saved before this existed keeps working unchanged, just without tags).

HTTP client — siren-http

A tiny httpie-like client built on urllib only:

siren-http GET https://api.example.com/items
siren-http POST https://api.example.com/items --json '{"name": "x"}' -H "Authorization: Bearer TOKEN"
siren-http GET https://api.example.com/items --save my-request   # save it as a local collection
siren-http replay my-request                                     # resend a saved request
siren-http list                                                  # list saved requests

You can also log every HTTP call your own code makes through requests or httpx, without touching that code — requests/httpx are not siren dependencies, they're only imported when you call these:

siren.patch_requests()    # every requests.Session call now logs method/url/status/duration
siren.patch_httpx()       # same, for httpx.Client (sync only)
siren.unpatch_requests()
siren.unpatch_httpx()

Code quality — siren-quality

Local checks built on the stdlib ast module (no pyflakes/radon/etc dependency):

siren-quality deadcode .     # unused imports and module-level defs never referenced in the same file
siren-quality lint .         # bare `except:`, leftover pdb.set_trace()/breakpoint(), TODO/FIXME comments
siren-quality complexity .   # cyclomatic complexity per function, flags anything above --threshold (default 10)

deadcode is a same-file heuristic — it can't see usage from other files, so treat its findings as candidates to double-check, not certainties.


Pro tier

Everything above is free and runs entirely offline. The siren-debug package also ships a couple of pro-tier commands that talk to a small backend (separate, closed-source repo) for a paid feature: exception capture with a searchable history, instead of only a local siren.catch().

siren-login signup you@example.com   # creates an account + API key, stored in ~/.siren/credentials.json
siren-login status                    # check your plan/license
siren-login logout
try:
    risky()
except Exception:
    siren.report()   # sends the exception (with traceback) to your workspace
siren-events list        # recent exceptions reported from any of your machines
siren-events show <id>   # full traceback for one of them

siren.report() never raises on its own — if you're not logged in, or the backend can't be reached, it prints a message and returns None instead of breaking your error handling. Point the CLI at a different backend with SIREN_API_URL (defaults to the hosted one). The hosted backend runs on Render's free tier, so it sleeps after inactivity — the first request after a while can take 30-60s to wake it up.

Subscribing:

siren-login upgrade                  # currency auto-detected from your system locale
siren-login upgrade --currency usd   # override it (brl / usd / eur)

Team workspaces — invite a teammate (creates their account if they don't have one yet, and hands you their API key to pass along since there's no email delivery yet):

siren-login invite teammate@example.com

Notifications — post to a Slack/Discord incoming webhook whenever an exception is captured for your workspace:

siren-login set-webhook https://hooks.slack.com/services/...
siren-login set-webhook              # no URL clears it

Performance metrics — time a function or a block of code; samples are buffered locally and uploaded in the background (never a blocking call per invocation), so siren-perf top can rank real bottlenecks across runs instead of one call on one machine:

@siren.profile
def slow_thing():
    ...

with siren.profile_block("db-query"):
    ...

siren.perf_flush()   # force an upload before a short script/job exits
siren-perf top                # ranked by total time spent, last 24h by default
siren-perf top --hours 168    # last 7 days
siren-perf list --name slow_thing

Tune volume on hot paths with @siren.profile(sample_rate=0.1) (sample 10% of calls) or @siren.profile(min_duration_ms=50) (only report calls slower than 50ms).


Framework examples

Django
from django.http import JsonResponse
from siren import siren

def my_view(request):
    user_data = request.GET.dict()
    siren(user_data, label="REQUEST_PARAMS")

    result = process_data(user_data)
    siren(result)

    return JsonResponse(result)
Flask
from flask import Flask, request
from siren import siren, trace

app = Flask(__name__)

@app.route("/api/users")
def get_users():
    query = request.args.get("q")
    siren(query, label="SEARCH_QUERY")

    users = search_users(query)
    return {"users": users}

@siren.trace
def search_users(query):
    # Function entry/exit will be logged automatically
    return [{"id": 1, "name": "Alice"}]
FastAPI
from fastapi import FastAPI
from siren import siren, trace

app = FastAPI()

@app.get("/items/{item_id}")
async def get_item(item_id: int, q: str = None):
    siren({"item_id": item_id, "q": q}, label="QUERY_PARAMS")

    item = await fetch_item(item_id)
    return item

@siren.trace(timeit=True)
async def fetch_item(item_id: int):
    # Execution time and arguments will be logged
    return {"id": item_id, "name": "Item"}

Why use Siren?

Debug prints are easy to add, but hard to remove later. Siren gives you a fast debug workflow and a safe cleanup step so your temporary debug code does not stay in production.


Project

License

MIT

Release files for siren-debug 0.8.1

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

Source distribution (sdist)

Source distribution for siren-debug 0.8.1
File Size Uploaded
siren_debug-0.8.1.tar.gz 50.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for siren-debug 0.8.1
File Interpreter ABI Platform
siren_debug-0.8.1-py2.py3-none-any.whl Python 2, Python 3 none any Details

Total release size: 91.6 kB

Release files / siren_debug-0.8.1.tar.gz

Download URL siren_debug-0.8.1.tar.gz
Size 50.9 kB
Tags Source
SHA-256 checksum
How to use checksums
8ac872362e64d224e17fedbbaaf5c1ed70235b2668fd4ee3976382f48eabe11d
BLAKE2b-256 checksum
How to use checksums
6f1b4ac7e9d8e0da0aa3ab44826138d661f1fa8bb0f567d1e8d03a9107d9e96a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 25, 2026.

Transparency log

Release files / siren_debug-0.8.1-py2.py3-none-any.whl

Download URL siren_debug-0.8.1-py2.py3-none-any.whl
Size 40.7 kB
Tags Python 2 Python 3
SHA-256 checksum
How to use checksums
59049c4acd2504e26dc5c566e5882ecbc7cf71e33428236b1caf54dbda15d990
BLAKE2b-256 checksum
How to use checksums
926463a2cc306fcd4e31c3ed68964c0a54c28609f2f3adaad1b84a4362cb9573
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.8.1 This release

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.3

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

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