pyrung
Ladder logic in Python that reads like ladder, scans like a PLC, and deploys to real hardware.
pyrung turns Python's with block into a ladder rung — condition on the rail, instructions in the body.
from pyrung import Bool, PLC, Program, rung, out
Button = Bool()
Light = Bool()
with Program() as logic:
with rung(Button):
out(Light)
with PLC(logic) as plc:
Button.value = True
plc.step()
assert Light.value is True
- Documentation: https://ssweber.github.io/pyrung/
- LLM docs index: https://ssweber.github.io/pyrung/llms.txt
- New to ladder logic? Know Python? Learn Ladder Logic.
Why?
Ladder is still the dominant programming language in North American manufacturing, but ladder programmers have no git, no pytest, no CI. They draw logic in a vendor GUI, download to hardware, and hope. pyrung lets you write and test logic in Python first, then deploy it — same source, three paths:
pyrung_to_ladder()encodes your rungs for ClickNick, a companion editor for Click Programming Software- Generate a self-contained CircuitPython scan loop for a P1AM-200
- Run as an emulated Click over Modbus — spin up two pyrung programs and test them talking to each other, no hardware required
The code reads like a ladder diagram. with rung(Or(Start, Motor), ~Stop): out(Motor) is a seal-in circuit, and it looks like one. There's no if/else. Power flows through the rung or it doesn't. The scan cycle is deterministic, timers accumulate the same way, rung order matters the same way. What you learn in pyrung transfers directly to a real ladder editor.
Who's this for?
Controls engineers who want to test Click PLC logic without hardware. The Click dialect handles address mapping, memory bank validation, and nickname and ladder file export. Have an existing project? ClickNick imports it.
Python developers entering industrial automation. pyrung teaches you ladder logic in the language and tools you already have. The VS Code debugger lets you step through scans and watch power flow rung by rung. Start with the learning guide.
Makers and P1AM-200 users who want a real scan loop without writing the plumbing. The CircuitPython dialect generates a complete program from the same logic you write and test on your laptop.
Quick start
# Requires Python 3.11+
uv add pyrung
Download the starter project (pyrung-starter-VERSION.zip) from the GitHub releases page for ready-to-run examples with Click CSV round-trip. For the VS Code debugger, grab pyrung-debug-VERSION.vsix from the same page and install it:
code --install-extension pyrung-debug-VERSION.vsix
A motor with start/stop logic
from pyrung import Bool, Program, rung, latch, reset
Start = Bool()
Stop = Bool()
Running = Bool()
with Program() as logic:
with rung(Start):
latch(Running)
with rung(Stop):
reset(Running)
Test it
from pyrung import PLC
with PLC(logic) as plc:
Start.value = True
plc.step()
assert Running.value is True
# Release start — motor stays latched
Start.value = False
plc.step()
assert Running.value is True
Stop.value = True
plc.step()
assert Running.value is False
Map to Click hardware when you're ready
from pyrung.click import ClickBlocks, TagMap
x, y, c, t, ct, sc, ds, dd, dh, df, xd, yd, xd0u, yd0u, td, ctd, sd, txt = ClickBlocks()
mapping = TagMap({
Start: x[1], # Physical input → X001
Stop: x[2], # Physical input → X002
Running: y[1], # Physical output → Y001
})
mapping.validate(logic) # Checks against Click constraints
mapping.to_nickname_file("motor.csv") # For Click programming software
What's included
Core engine
Coils, latches, timers, counters, branching, subroutines, structured tags, and edge detection. Every scan is a pure function — same inputs, same outputs — so you can fork, rewind, and diff any state in history.
Click PLC dialect
Hardware address mapping, memory bank validation, Modbus instructions, and nickname and ladder file export.
CircuitPython dialect
Generate a self-contained scan loop for the P1AM-200 with 35 supported I/O modules, SD-backed retentive storage, watchdog, and Modbus TCP.
Analysis and verification
plc.cause() and plc.effect() trace causal chains through scan history — why did this tag change, and what would it take to recover? always() exhaustively checks a property over all reachable states, with counterexample traces when it fails. never() proves a bad state is unreachable. Automated fault coverage proves every device has an alarm path. Lock files catch behavioral regressions in PRs.
VS Code debugger
Step through scans rung by rung, set breakpoints, force tags, diff states, and time-travel through scan history. Data View for live tag watching, Graph View for dependency visualization, and session capture for mining invariants into pytest tests.
Disclaimers
- Simulation is best-effort. pyrung models Click PLC behavior as closely as practical, but it is not a certified simulator. You are responsible for verifying your program on real hardware before production use.
- Modbus is unauthenticated. The emulated Click Modbus interface and CircuitPython Modbus TCP server listen on the network with no encryption or access control — standard for Modbus, but keep them off untrusted networks.
Metadata
Release files for pyrung 0.12.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 | |
|---|---|---|---|
| pyrung-0.12.0.tar.gz | 4.0 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyrung-0.12.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 5.6 MB
Release files / pyrung-0.12.0.tar.gz
| Download URL | pyrung-0.12.0.tar.gz |
|---|---|
| Size | 4.0 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8f3b4f7d21157635171235eeb50170ef2fad5aab1d08116baab180ee8ea07f77
|
|
BLAKE2b-256 checksum How to use checksums |
909ea6e93e0a2938137d1a316022e315163e774d871e704bc5d350feaca87b60
|
| 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 1, 2026.
Transparency logRelease files / pyrung-0.12.0-py3-none-any.whl
| Download URL | pyrung-0.12.0-py3-none-any.whl |
|---|---|
| Size | 1.6 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bac8a7413019951d40493d2f9489396a1e3503a0ed95d6a760be1795ecd3f550
|
|
BLAKE2b-256 checksum How to use checksums |
ec25615b9eb02479bf1716fdde88030fb5641584f70b67547f467656befb8f1b
|
| 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 1, 2026.
Transparency log