Skip to main content

wiringguard

ci pypi python

Prove the guard you added is still called.

You added a permission check. An audit-log call. A thumbnail step that stops a video going out with whatever frame the platform picks. Six months later a refactor leaves the import in place and drops the call. Every test still passes — the tests exercise the happy path, and the guard was never the thing under test.

pip install wiringguard
# pyproject.toml
[[tool.wiringguard.rules]]
name     = "every upload sets a custom cover"
kind     = "must-call"
where    = "uploaders/*.py"
function = "upload_*"
call     = "ensure_cover"
$ wiringguard
uploaders/youtube.py:205: [every upload sets a custom cover]
    upload_video() no longer calls ensure_cover() — the name still appears in the
    file, but only as an import or a mention, never as a call

1 rule(s) | 1 violation(s)

Why not grep

Because grep passes. That output above is from a real file where the string ensure_cover is still present — in a docstring — while the call itself had been replaced. A watchdog built on if "ensure_cover" in source reports green over code that no longer does the thing, and it reports green forever.

The insidious version is even quieter: the refactor keeps from covers import ensure_cover at the top and removes the one call. Now the symbol is genuinely imported, your editor shows no warning, the linter shows no unused import if something else touches it, and every text search finds it.

wiringguard parses the file. A call is a call.

The two kinds of rule

must-call — the guard is still invoked

[[tool.wiringguard.rules]]
name     = "writes go through the audit log"
kind     = "must-call"
where    = "app/repository/*.py"
function = "save_*"
call     = "audit.record"

function accepts a glob, or "*" for every function in the matched files. If the function was renamed or deleted, that is reported too — a rule quietly guarding nothing is the failure this tool exists to end, not a pass.

only-in — nobody grew a second path

[[tool.wiringguard.rules]]
name  = "one place may publish"
kind  = "only-in"
call  = "client.videos.insert"
files = ["uploaders/youtube.py", "uploaders/legacy_*.py"]

A second module that quietly grew its own copy of a privileged operation is how an ungated side door gets born. This finds it.

Rules match on the suffix of a dotted call path, with intermediate calls collapsed: client.videos().insert(...) matches videos.insert or client.videos.insert, and does not match db.insert. Name as much of the path as you need to be unmistakable.

In CI

- run: pip install wiringguard
- run: wiringguard
exit meaning
0 every rule still holds
1 something came unwired
2 could not check — no config, an empty ruleset, a bad path

2 is deliberately separate. A tool whose whole subject is "a check that silently stopped checking" must never let "I could not look" be read as "I looked and it was fine". For the same reason, an empty ruleset is refused rather than exiting 0, and files that fail to parse are reported instead of skipped.

How it compares

scope
import-linter, pytest-archon which modules may import which
semgrep general pattern matching, one pattern at a time
wiringguard which functions must call what, and who may call it at all

Import contracts do not tell you whether the function is actually invoked. This is the call-site half of the same idea, in about a hundred lines of AST.

Configuration

wiringguard.toml at the project root, or a [tool.wiringguard] section in pyproject.toml. Vendored directories (.venv, site-packages, node_modules, caches, build, dist) are skipped. Sources are read as utf-8-sig, so a byte-order mark does not turn a check into a silent skip.

Requirements

Python 3.9+. No dependencies on 3.11+ (tomli below that). Linux, macOS and Windows.

License

MIT

Metadata

Release files for wiringguard 0.1.0

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

Source distribution (sdist)

Source distribution for wiringguard 0.1.0
File Size Uploaded
wiringguard-0.1.0.tar.gz 11.9 kB Details

Built distribution (wheel)

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

Total release size: 23.5 kB

Release files / wiringguard-0.1.0.tar.gz

Download URL wiringguard-0.1.0.tar.gz
Size 11.9 kB
Tags Source
SHA-256 checksum
How to use checksums
0b8be8ec5c878b82f89120215c865e798a75bc2f1d681ee5fc7e8b63e2499647
BLAKE2b-256 checksum
How to use checksums
8ed48ec001637540c305e78024539d375cfde73a8b7bbf1150cd24c00413fa09
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 Aug 19, 2026.

Transparency log

Release files / wiringguard-0.1.0-py3-none-any.whl

Download URL wiringguard-0.1.0-py3-none-any.whl
Size 11.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
98b14af1708b97cd46f79bbbc40be95dea5f6dc3e122e8a4a88811a17e9c5095
BLAKE2b-256 checksum
How to use checksums
0d73c7cf057a57abe6cc8c66038f86b310358c38ed0951caa793518d7602f6a7
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 Aug 19, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

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