Write ladder logic in Python. Simulate it. Test it. Deploy it -- Currently targets AutomationDirect CLICK PLC and CircuitPython ProductivityOpen P1AM-200.
Project description
pyrung
Write ladder logic in Python. Simulate it. Test it. Deploy to Click PLCs or P1AM-200 hardware.
pyrung turns Python's with block into a ladder rung — condition on the rail, instructions in the body.
from pyrung import Bool, PLCRunner, Program, Rung, out
Button = Bool("Button")
Light = Bool("Light")
with Program() as logic:
with Rung(Button):
out(Light)
runner = PLCRunner(logic)
with runner.active():
Button.value = True
runner.step()
assert Light.value is True
- Documentation: https://ssweber.github.io/pyrung/
- LLM docs index: https://ssweber.github.io/pyrung/llms.txt
Why?
AutomationDirect Click PLCs have no built-in simulator. You write logic, download it to hardware, and hope. pyrung lets you test first — same tag names, deterministic scans, real assertions. When it works, encode it with pyrung_to_ladder() and paste via clicknick.
Or skip the Click editor entirely — generate a CircuitPython scan loop for a ProductivityOpen P1AM-200 and run your tested logic on open hardware.
Or run your logic as an emulated Click over Modbus to test send/receive, no hardware required. You can even spin up two pyrung programs and test them talking to each other.
Quick start
# Requires Python 3.11+
uv add pyrung
A motor with start/stop logic
from pyrung import Bool, Program, Rung, latch, reset
Start = Bool("Start")
Stop = Bool("Stop")
Running = Bool("Running")
with Program() as logic:
with Rung(Start):
latch(Running)
with Rung(Stop):
reset(Running)
Test it
from pyrung import PLCRunner
runner = PLCRunner(logic)
with runner.active():
Start.value = True
runner.step()
assert Running.value is True
# Release start — motor stays latched
Start.value = False
runner.step()
assert Running.value is True
Stop.value = True
runner.step()
assert Running.value is False
Map to Click hardware when you're ready
from pyrung.click import TagMap, x, y
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
Pure f(state) → new_state scan cycle with immutable snapshots. Coils, latches, timers, counters, branching, subroutines, structured tags, edge detection, and more. Built to match real Click behavior — no surprises when you move to hardware.
Click PLC dialect
Hardware address mapping, memory bank validation, Modbus instructions, and nickname file export. Run any program as an emulated Click over Modbus for integration testing.
CircuitPython dialect
Generate a self-contained CircuitPython scan loop from the same program you already tested. Targets the ProductivityOpen P1AM-200 with 35 supported I/O modules, SD-backed retentive storage, watchdog, Modbus TCP, and RUN/STOP control.
VS Code debugger
Step through scans rung by rung, set breakpoints, force tags, diff states, and time-travel through scan history.
Learn more
| Core Concepts | Scan cycle, SystemState, tags, blocks |
| Instruction Reference | Full DSL reference |
| Tag Structures | UDTs, named arrays, cloning, block config |
| Runner Guide | Execution, time modes, history, fork |
| Testing Guide | Unit testing with deterministic time |
| Forces & Debug | Force values, breakpoints, time travel |
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyrung-0.2.2.tar.gz.
File metadata
- Download URL: pyrung-0.2.2.tar.gz
- Upload date:
- Size: 988.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ddf8968d1ba0af0a84b7d518c010bf6e6b4d6ea3586fca6da75256869bd57630
|
|
| MD5 |
0b92162b072ac93e28807281fcb391a4
|
|
| BLAKE2b-256 |
1ac67c24c4e050cabf48c5c4d8be95d0e586258b4cd69403247c7c505d7d46eb
|
Provenance
The following attestation bundles were made for pyrung-0.2.2.tar.gz:
Publisher:
publish.yml on ssweber/pyrung
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyrung-0.2.2.tar.gz -
Subject digest:
ddf8968d1ba0af0a84b7d518c010bf6e6b4d6ea3586fca6da75256869bd57630 - Sigstore transparency entry: 1218556653
- Sigstore integration time:
-
Permalink:
ssweber/pyrung@b94caaca8623d72ae49edf8316691c12afa153bb -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/ssweber
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b94caaca8623d72ae49edf8316691c12afa153bb -
Trigger Event:
release
-
Statement type:
File details
Details for the file pyrung-0.2.2-py3-none-any.whl.
File metadata
- Download URL: pyrung-0.2.2-py3-none-any.whl
- Upload date:
- Size: 328.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d9853fc6250ee73a994478b2d0a2da8d88a764a1b0fb699f7028d525b9e84e54
|
|
| MD5 |
12663592863cb489f556ec5ef401c426
|
|
| BLAKE2b-256 |
a82ec70333af61ad9f87508b7684c41ebcd32ce0ce9e995bfdd5e684afa1f6a9
|
Provenance
The following attestation bundles were made for pyrung-0.2.2-py3-none-any.whl:
Publisher:
publish.yml on ssweber/pyrung
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyrung-0.2.2-py3-none-any.whl -
Subject digest:
d9853fc6250ee73a994478b2d0a2da8d88a764a1b0fb699f7028d525b9e84e54 - Sigstore transparency entry: 1218556754
- Sigstore integration time:
-
Permalink:
ssweber/pyrung@b94caaca8623d72ae49edf8316691c12afa153bb -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/ssweber
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b94caaca8623d72ae49edf8316691c12afa153bb -
Trigger Event:
release
-
Statement type: