wirebench
Describe real electronic circuits in Python. The framework won't let a design that wouldn't physically work compile — so when your tests are green, the breadboard build matches.
KiCad's ERC catches defective wiring after you've drawn the schematic. wirebench prevents you from constructing the wrong design in the first place — no burnt CMOS outputs from two drivers fighting on one net, no half-evenings tracing a regulator that never had a ground wired, no second order at Mouser because the cable housings don't fit the header you placed. Wire two chip outputs together and wire() raises. Leave a regulator's input floating and the Circuit subclass refuses to instantiate. Try to mate a male pin header to a JST plug and mate() says no. The defects ERC would flag after you've drawn the schematic are flagged before the Python design even imports cleanly.
If you've programmed an Arduino but felt like wiring the surrounding circuit is a black art — every project a hunt for the magic combination of pull-ups, current-limiting resistors, decoupling caps, and "did I just wire the LED backwards?" — wirebench is where every line of code maps to a physical operation. You write Python that says "a 330 Ω resistor in series with a red LED between VCC and GND." The wires in the code are the wires you'll need on the breadboard, and the design either constructs cleanly or it doesn't — there's no middle state where pytest passes but the bench build smokes.
Hello, world
from wirebench import Resistor, LED, Rail, Circuit, wire, export
class HelloLED(Circuit):
def __init__(self) -> None:
self.vcc = Rail(True) # 5 V supply rail
self.gnd = Rail(False) # 0 V ground
self.r1 = Resistor(330, refdes_number=1) # R1: 330 Ω current limiter
self.d1 = LED('red', refdes_number=1) # D1: red LED
wire(self.vcc.out, self.r1.t1) # VCC → R1 pin 1
wire(self.r1.t2, self.d1.anode) # R1 pin 2 → LED anode
wire(self.d1.cathode, self.gnd.out) # LED cathode → GND
super().__init__()
design = HelloLED()
export(design, 'bom', 'hello.bom.csv')
export(design, 'kicad', 'hello.net')
export(design, 'spice', 'hello.cir')
export(design, 'mermaid', 'hello.mmd')
This is a buildable circuit. The wire-list in the Python file maps one-for-one to the jumpers on your breadboard. Drop the BOM into Mouser or Digikey, order two parts, and you have everything you need.
Components are stored as self.<name> attributes so the framework can auto-collect them — super().__init__() walks self.__dict__ and picks up every Resistor, LED, Chip, and Rail you've placed. The two-stage pattern (declare parts → wire them → finalise with super().__init__()) reads like a bench-style buildout: take parts off the shelf, wire them together, close the assembly.
What comes out:
| Format | Extension | What it's for |
|---|---|---|
| BOM CSV | .bom.csv |
Paste into a parts cart at Mouser / Digikey / Tayda |
| KiCad netlist | .net |
Import into KiCad's Pcbnew to lay out a PCB |
| SPICE deck | .cir |
Simulate in ngspice or LTspice before building |
| Mermaid | .mmd |
Embed in a README to document what you built |
| Graphviz DOT | .dot |
Render to SVG / PNG with dot -Tsvg |
| Yosys JSON | .yosys.json |
Render to a browser-friendly schematic via netlistsvg |
| Assembly Guide | .md |
Read at the bench — recipe-style breadboard build instructions |
Every demo in demos/ ships with all seven exports pre-generated in its docs/ subfolder — open any *.svg to see the rendered schematic, or any *.md to read the bench-assembly guide.
What it prevents
Three categories of real-world harm wirebench rules out before the design ever reaches a breadboard — each caught in milliseconds, each with the kind of error message that names the offending part and tells you what to fix.
Burnt outputs
u1 = SN74HC04(refdes_number=1)
wire(u1.y_1, u1.y_2)
# ShortCircuitError: wire() has multiple drivers ('y_1', 'y_2') — short circuit
Two CMOS outputs fighting on the same net will burn out one or both. You don't need to know which — wirebench knows, and wire() raises before the call returns. The chip never gets the chance to suffer the experiment.
A wasted parts order
mate(Header2xNMale(...), JSTPHCableHousing(...))
# IncompatibleMateError: Header2xNMale mates with Header2xNFemale, not JSTPHCableHousing
Pitch, pin count, and gender all checked at mate() time. You don't put through a JST cable order, wait five days, and discover the housings don't fit the header on your board — the wrong combination never gets past the Python.
Hours of fault-tracing
class CrossedRails(Circuit):
def __init__(self):
self.vcc1 = Rail(True)
self.vcc2 = Rail(True)
self.r1 = Resistor(330, refdes_number=1)
wire(self.vcc1.out, self.r1.t1)
wire(self.vcc2.out, self.r1.t1) # legal in isolation
super().__init__()
# ShortCircuitError: Short circuit on logical net — multiple drivers: 'Rail.out', 'Rail.out'
Each wire() call is fine in isolation — the contention only emerges when the framework walks the combined logical net at super().__init__(). This is the same algorithm KiCad's ERC runs, except KiCad waits for you to click Run ERC and wirebench waits no longer than __init__ returning. Without that walk, you'd find the conflict the slow way: build the design, watch the supplies fight, scope every node looking for the one that doesn't sit where it should.
The same logic prevents other classes of harm hobbyists don't always know to look for: forbidden runtime states (S=1, R=1 on an SR latch) that would lock a latch into undefined behaviour, ground-domain crossings without an isolator that would defeat the point of having isolation, chips with declared output pins that nothing internal drives — every quiet failure mode where the circuit looks right and doesn't work. Every error message names the offending part by refdes and pin number, so you know what to fix before you reach for a soldering iron.
When pytest is green, the topology is sound.
A real design
demos/water_alarm/ is the simplest end-to-end example — four chips, two LEDs, a pair of probes mounted in a tank. The source is water_alarm.py; open docs/WaterAlarm.svg for the rendered schematic, or docs/WaterAlarm.bom.csv for the parts list. The same folder shape applies to every demo. See docs/learning-path.md for the suggested order to work through them.
What it doesn't do
- Solve Ohm's law. Logic-level only. For continuous-voltage simulation, export to SPICE and let ngspice handle it.
- Catch parameter mistakes. The framework prevents defective topology — shorts, floating nets, mismatched connectors, forbidden states, cross-domain wiring — but it won't save you from a 330 Ω current limiter sized for a high-current LED, a 25 V capacitor on a 30 V rail, or a regulator dissipating 4 W with no heatsink. Use SPICE, arithmetic, or the per-component
GOTCHASstrings for those. - Simulate firmware. Model firmware as a private cell inside a chip subclass (see
demos/digital_thermometer/); the cell is your code. - Lay out a PCB. KiCad netlist export gets you to the start of layout; KiCad does the rest.
See docs/design-principles.md for why the framework is shaped the way it is.
Install
Requires Python 3.10+:
uv venv && uv pip install -e ".[dev]"
# or
python -m venv .venv && source .venv/bin/activate && pip install -e ".[dev]"
Run tests
pytest
Full coverage of the framework, components, every export format with byte-deterministic golden files, end-to-end round-trips, and property-based stress on the load-bearing logic.
Going further
demos/— every demo is a complete study artifact (source + all six exports + rendered schematic).docs/learning-path.md— suggested order for working through the demos.docs/design-principles.md— why the framework prevents what it prevents.docs/component-library-data.md— catalogue of all 122 modelled components with datasheet links, pin maps, and footprints.docs/— implementation specs for every major work package.CLAUDE.md— design philosophy in full, for contributors.
Licensing
Released under the PolyForm Noncommercial License 1.0.0 — free for non-commercial use, including personal study, hobby projects, academic research, and use by educational, charitable, or public institutions.
Commercial use requires a separate paid license. Contact the project maintainer for terms.
Metadata
Release files for wirebench 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| wirebench-0.1.0.tar.gz | 197.0 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| wirebench-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 556.4 kB
Release files / wirebench-0.1.0.tar.gz
| Download URL | wirebench-0.1.0.tar.gz |
|---|---|
| Size | 197.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9faf30439f1748cd3ca52b895dcb146b3569a3d13ae2bb78531c2460c9358b82
|
|
BLAKE2b-256 checksum How to use checksums |
0c1243c3cfb39b1cd4d1f5c80062f74a731e863b8c46ea840a0c584b48d60e0a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 May 13, 2026.
Transparency logRelease files / wirebench-0.1.0-py3-none-any.whl
| Download URL | wirebench-0.1.0-py3-none-any.whl |
|---|---|
| Size | 359.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8bc4c9d2d43a1651722e172762237c00fe2884d0458ca45287527d9fc5418542
|
|
BLAKE2b-256 checksum How to use checksums |
cdeb1d4e4fffc2d7d7a7e78b662ac1442524c93ff9477bdfd89637fe9a96cd1a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 May 13, 2026.
Transparency logSecurity for wirebench 0.1.0
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for wirebench-0.1.0.tar.gz
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 May 13, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.