Metadata-Version: 2.4
Name: wirebench
Version: 0.1.0
Summary: A Python framework for modelling real electronic circuits with physical fidelity.
License: # PolyForm Noncommercial License 1.0.0
        
        <https://polyformproject.org/licenses/noncommercial/1.0.0>
        
        ## Acceptance
        
        In order to get any license under these terms, you must agree
        to them as both strict obligations and conditions to all
        your licenses.
        
        ## Copyright License
        
        The licensor grants you a copyright license for the
        software to do everything you might do with the software
        that would otherwise infringe the licensor's copyright
        in it for any permitted purpose. However, you may
        only distribute the software according to [Distribution
        License](#distribution-license) and make changes or new works
        based on the software according to [Changes and New Works
        License](#changes-and-new-works-license).
        
        ## Distribution License
        
        The licensor grants you an additional copyright license
        to distribute copies of the software. Your license
        to distribute covers distributing the software with
        changes and new works permitted by [Changes and New Works
        License](#changes-and-new-works-license).
        
        ## Notices
        
        You must ensure that anyone who gets a copy of any part of
        the software from you, with or without changes, also gets
        the text of this license or a link to
        <https://polyformproject.org/licenses/noncommercial/1.0.0>.
        
        ## Changes and New Works License
        
        The licensor grants you an additional copyright license to
        make changes and new works based on the software for any
        permitted purpose.
        
        ## Patent License
        
        The licensor grants you a patent license for the software
        that covers patent claims the licensor can license, or
        becomes able to license, that you would infringe by using
        the software.
        
        ## Noncommercial Purposes
        
        Any noncommercial purpose is a permitted purpose.
        
        ## Personal Uses
        
        Personal use for research, experiment, and testing for
        the benefit of public knowledge, personal study, private
        entertainment, hobby projects, amateur pursuits, or
        religious observance, without any anticipated commercial
        application, is use for a permitted purpose.
        
        ## Noncommercial Organizations
        
        Use by any charitable organization, educational institution,
        public research organization, public safety or health
        organization, environmental protection organization,
        or government institution is use for a permitted purpose
        regardless of the source of funding or obligations
        resulting from the funding.
        
        ## Fair Use
        
        You may have "fair use" rights for the software under the
        law. These terms do not limit them.
        
        ## No Other Rights
        
        These terms do not allow you to sublicense or transfer any of
        your licenses to anyone else, or prevent the licensor from
        granting licenses to anyone else. These terms do not imply
        any other licenses.
        
        ## Patent Defense
        
        If you make any written claim that the software infringes or
        contributes to infringement of any patent, your patent license
        for the software granted under these terms ends immediately. If
        your company makes such a claim, your patent license ends
        immediately for work on behalf of your company.
        
        ## Violations
        
        The first time you are notified in writing that you have
        violated any of these terms, or done anything with the software
        not covered by your licenses, your licenses can nonetheless
        continue if you come into full compliance with these terms,
        and take practical steps to correct past violations, within
        32 days of receiving notice. Otherwise, all your licenses
        end immediately.
        
        ## No Liability
        
        ***As far as the law allows, the software comes as is, without
        any warranty or condition, and the licensor will not be liable
        to you for any damages arising out of these terms or the use
        or nature of the software, under any kind of legal claim.***
        
        ## Definitions
        
        The **licensor** is the individual or entity offering these
        terms, and the **software** is the software the licensor makes
        available under these terms.
        
        **You** refers to the individual or entity agreeing to these
        terms.
        
        **Your company** is any legal entity, sole proprietorship,
        or other kind of organization that you work for, plus all
        organizations that have control over, are under the control of,
        or are under common control with that organization. **Control**
        means ownership of substantially all the assets of an entity,
        or the power to direct its management and policies by vote,
        contract, or otherwise. Control can be direct or indirect.
        
        **Your licenses** are all the licenses granted to you for the
        software under these terms.
        
        **Use** means anything you do with the software requiring one
        of your licenses.
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: networkx>=3.0
Requires-Dist: pydantic>=2.6
Provides-Extra: dev
Requires-Dist: coverage[toml]>=7.0; extra == 'dev'
Requires-Dist: hypothesis>=6.0; extra == 'dev'
Requires-Dist: mutmut>=3.0; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-cov>=7.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# 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

```python
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](https://github.com/nturley/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

```python
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

```python
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

```python
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`](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 `GOTCHAS` strings 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`](docs/design-principles.md) for why the framework is shaped the way it is.

## Install

Requires Python 3.10+:

```bash
uv venv && uv pip install -e ".[dev]"
# or
python -m venv .venv && source .venv/bin/activate && pip install -e ".[dev]"
```

## Run tests

```bash
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/`](demos/) — every demo is a complete study artifact (source + all six exports + rendered schematic).
- [`docs/learning-path.md`](docs/learning-path.md) — suggested order for working through the demos.
- [`docs/design-principles.md`](docs/design-principles.md) — why the framework prevents what it prevents.
- [`docs/component-library-data.md`](docs/component-library-data.md) — catalogue of all 122 modelled components with datasheet links, pin maps, and footprints.
- [`docs/`](docs/) — implementation specs for every major work package.
- [`CLAUDE.md`](CLAUDE.md) — design philosophy in full, for contributors.

## Licensing

Released under the [PolyForm Noncommercial License 1.0.0](LICENSE) — 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.
