Skip to main content

clankers

Get an ntfy notification when a long-running command or Python task finishes.

Configuration

Set the ntfy server and topic with environment variables:

export NTFY_URL="https://ntfy.example.com"
export NTFY_TOPIC="default"
export NTFY_TOKEN="tk_your_private_access_token"

The same variables can live in a .env file in your project directory:

NTFY_URL=https://ntfy.example.com
NTFY_TOPIC=default
NTFY_TOKEN=tk_your_private_access_token

Both NTFY_URL and NTFY_TOPIC are required; clankers does not provide deployment defaults in code. You can instead create ~/.config/clankers/config.toml (or $XDG_CONFIG_HOME/clankers/config.toml):

[ntfy]
url = "https://ntfy.example.com"
topic = "default"
token = "tk_your_private_access_token"

The loader returns normalized key/value pairs such as NTFY_URL; each backend selects and validates its own settings. Sources are merged in this order of priority:

  1. .env
  2. process environment variables (only those prefixed NTFY_)
  3. ~/.config/clankers/config.toml

Settings themselves never come from the command line — only the files they live in do. Clankers searches for .env from the working directory upward; use --dotenv to select one directly, and --config to select a different TOML file.

CLI

Prefix an arbitrary command with clankers engage:

clankers engage uv run pytest
clankers engage --config ./config.toml uv run train.py
clankers engage -m "nightly training" -- uv run train.py --epochs 100

Clankers' own flags must come before the command; everything after the first non-flag argument (or after --) belongs to the wrapped command. The wrapped command keeps its standard input and output. Clankers reports its duration and result, then returns the command's exit code. Without --message the report is the command line itself. An unreachable notification server is silently ignored.

Send a notification on its own — at the end of a shell script, or from a Makefile:

clankers rogerroger -m "deploy finished"
clankers uhoh -m "deploy failed"

clankers --version prints the installed version. Delivery problems are reported on stderr — a rejected or unreachable server never changes the exit code — and -v logs every file clankers reads and every request it makes:

$ clankers rogerroger -m "deploy finished" -v
clankers: reading .env file /home/you/project/.env
clankers: configuration provides NTFY_TOKEN, NTFY_TOPIC, NTFY_URL
clankers: publishing to https://ntfy.example.com/deploys with a 10.0s timeout
clankers: ntfy rejected the notification: 403 Forbidden {"code":40301,"error":"forbidden"}

Python

The same helper works as a context manager or decorator:

import clankers

with clankers.Engage("Training"):
    train()


@clankers.engage("Training")
def train(): ...


@clankers.engage()  # the message defaults to the function name
async def evaluate(): ...

Normal completion sends success. An exception sends failure and is re-raised unchanged. Async functions are supported as decorators as well. Configuration is read the first time a notification is sent, so decorating a function never fails at import time; a missing configuration or an unreachable ntfy server is logged and never affects the wrapped work.

Clankers logs through the standard logging module under the clankers logger: failed deliveries at WARNING, every file read and request made at DEBUG.

logging.getLogger("clankers").setLevel(logging.DEBUG)

Every entry point shares one lazily built backend, so the configuration files are read once per process. Point it somewhere else — or hand it a ready backend — with configure():

clankers.configure(config_path="./clankers.toml")

For a process that reports to more than one topic, build clankers of your own; each one owns its backend and offers the same three entry points:

training = clankers.Clanker(dotenv_path="./training.env")

with training.engage("Epoch 1"):
    ...

training.rogerroger("Checkpoint uploaded")

clankers.Engage, engage, rogerroger and uhoh always report through the shared clanker; every configuration option lives on Clanker and configure().

For work clankers does not wrap itself, send a notification by hand:

clankers.rogerroger("Checkpoint uploaded")
clankers.uhoh("Validation loss diverged", duration=4200)

Both accept the same config_path= / dotenv_path= / backend= keywords and, like everything else, only warn when the notification cannot be delivered.

Development

uv sync --group dev
make check   # ruff lint + format check + prettier
make fix     # autofix and reformat
make test    # pytest
make build   # wheel + sdist into dist/, validated with twine

Releasing

make release VERSION=0.1.0 runs scripts/release.py, which bumps the version in pyproject.toml, opens a dated section in CHANGELOG.md, commits, tags v0.1.0, pushes, and publishes a GitHub Release. The Release event triggers .github/workflows/publish.yml, which builds and uploads to PyPI via Trusted Publishing. It refuses to run unless you are on master with a clean tree in sync with origin and the tag is unused.

Use make release-dry-run VERSION=0.1.0 first to print every step without changing anything.

Describe changes under ## [Unreleased] in CHANGELOG.md before releasing — that section becomes the release notes.

One-time setup on pypi.org: add a trusted publisher for project clankers with owner radajakub, repository clankers, workflow publish.yml and environment pypi.

Metadata

Release files for clankers 1.0.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 clankers 1.0.1
File Size Uploaded
clankers-1.0.1.tar.gz 37.2 kB Details

Built distribution (wheel)

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

Total release size: 49.6 kB

Release files / clankers-1.0.1.tar.gz

Download URL clankers-1.0.1.tar.gz
Size 37.2 kB
Tags Source
SHA-256 checksum
How to use checksums
f3b71a9c77565d3656ec09742dff9d677018813f793cc19c37abd409d1b2d381
BLAKE2b-256 checksum
How to use checksums
25f7b964e88fc7f334965fe7014d6ed9488d8e5a6660f8fcfe07780dc0536022
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 18, 2026.

Transparency log

Release files / clankers-1.0.1-py3-none-any.whl

Download URL clankers-1.0.1-py3-none-any.whl
Size 12.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d2bd5da5ae6103aeead6b5774c8796d9c36f68eb5c5f1f06f379aadc68c92c78
BLAKE2b-256 checksum
How to use checksums
0788deec3e977319e48f83c10e017a7f73941d2aba7e57742450aaeab49291c7
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 18, 2026.

Transparency log

Release history Release notifications | RSS feed

3.0.2

2 release files

3.0.1

2 release files

3.0.0

2 release files

2.0.0

2 release files

This release

1.0.1 This release

2 release files

1.0.0

2 release files

0.0.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