simantic 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,219 @@
1
+ Metadata-Version: 2.5
2
+ Name: simantic
3
+ Version: 0.2.0
4
+ Summary: Python SDK and pytest plugin for the Simantic circuit and firmware simulators
5
+ Project-URL: Homepage, https://simantic.dev
6
+ Project-URL: Source, https://github.com/simantic-dev/simantic-py
7
+ Project-URL: Issues, https://github.com/simantic-dev/simantic-py/issues
8
+ Author: Simantic
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: circuit,eda,firmware,kicad,pytest,simulation,spice
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Framework :: Pytest
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
18
+ Requires-Python: >=3.11
19
+ Requires-Dist: pythonnet>=3.0.5
20
+ Requires-Dist: pyyaml>=6
21
+ Description-Content-Type: text/markdown
22
+
23
+ # simantic
24
+
25
+ Python control of the [Simantic](https://simantic.dev) simulators — the
26
+ firmware engine hosted in your process, circuits via `analog-cli`. Everything
27
+ the CLIs can do, as objects and method calls: start a board or a multi-machine scenario, advance virtual
28
+ time by exact amounts, inject UART/GPIO/CAN/radio, read memory and RTOS state,
29
+ and run as many simulations in parallel as you have cores. pytest is one way
30
+ to use it, not a requirement.
31
+
32
+ > **Alpha — not stable.** Version 0.1.x. The API, the CLI surface, and the
33
+ > report schema may change without a deprecation period, and any release may
34
+ > break the previous one. Pin an exact version (`simantic==0.1.0`) if you
35
+ > depend on it. Not recommended for production pipelines yet.
36
+
37
+ ```bash
38
+ pip install simantic
39
+ ```
40
+
41
+ That is the whole setup for Python. The first `Sim(...)` fetches the
42
+ simulation engine (Simantic.Core plus a private .NET runtime — nothing else
43
+ to install) into `~/.simantic/engine/<version>/`, checksum-verified against
44
+ the public release manifest. `simantic install` fetches it up front, along
45
+ with the `sim` and `analog-cli` binaries if you also want the command-line
46
+ tools.
47
+
48
+ A Simantic account (`simantic auth`) is needed for one thing: resolving MCU
49
+ models by name (`mcu="STM32F401RE"`), which are fetched from your account
50
+ and cached in `~/.sim_cache`. A platform file you supply (`repl=`) needs no
51
+ account at all.
52
+
53
+ `simantic auth` opens a browser tab to sign in — like `gh auth login` — and
54
+ stores the resulting token in `~/.sim_id`, the same file the CLIs use, so one
55
+ login covers all of them. In a script or CI, pass `--token` or pipe one in
56
+ (`echo $TOKEN | simantic auth`) instead of opening a browser. Create a token
57
+ on the dashboard's `/account/api` page. `--no-browser` falls back to an
58
+ interactive prompt for a pasted token.
59
+
60
+ Every download — engine or binary — is verified against the checksum in the
61
+ release manifest. The package on PyPI contains only Python; the simulators
62
+ are never in the wheel.
63
+
64
+ Already have the binaries? Point `$SIMANTIC_ANALOG_CLI` and `$SIMANTIC_SIM`
65
+ at them, or put them on PATH — both take precedence over a managed install.
66
+ `simantic status` shows what is authenticated and which binary each name
67
+ resolves to.
68
+
69
+ ## Drive a simulation
70
+
71
+ A `Sim` is a live simulation you control. Time advances only when you ask, so
72
+ a script is deterministic and your think-time is free:
73
+
74
+ ```python
75
+ from simantic import Sim
76
+
77
+ with Sim(elf="fw.elf", mcu="STM32F401RE", uart="usart2") as sim:
78
+ sim.expect("ready")
79
+ sim.inject_gpio("gpioc", 13, True) # press the user button
80
+ m = sim.expect("button pressed")
81
+ assert m.virtual_seconds < 0.010 # within 10 virtual ms
82
+ assert sim.read_u32("press_count") == 1
83
+ ```
84
+
85
+ The same class runs multi-machine scenarios with scripted peers
86
+ (`Sim(scenario={...})`). The engine lives in your process (one emulation per
87
+ process), so a parameter sweep is a `ProcessPoolExecutor` over plain
88
+ functions. See
89
+ [docs/session-api.md](docs/session-api.md) and `examples/`.
90
+
91
+ One-shot runs ("run 5 s, give me the transcript") are `run_firmware(...)`.
92
+
93
+ ## Using it from pytest (optional)
94
+
95
+ `Sim` needs no plugin — construct it inside any test. If you also keep
96
+ manifests, installing the package registers two collectors that turn them
97
+ into individually addressable pytest items:
98
+
99
+ - `*.sim.toml` — one item per `[[test]]` table (analog)
100
+ - `test.yaml` — one item per fixture (firmware)
101
+
102
+ ```console
103
+ $ pytest hardware/ firmware/
104
+ hardware/psu/psu.sim.toml::schematic-erc PASSED
105
+ hardware/psu/psu.sim.toml::rails-op PASSED
106
+ hardware/psu/psu.sim.toml::startup-settling FAILED
107
+ hardware/psu/psu.sim.toml::board-drc SKIPPED (no .kicad_pcb)
108
+ firmware/tests/gpio-loopback/test.yaml::gpio-loopback PASSED
109
+ ```
110
+
111
+ Because these are ordinary pytest items you get `-k` filtering, `--junitxml`
112
+ for CI, xdist parallelism, and per-test durations. Failures print the
113
+ runner's own explanation rather than a Python traceback:
114
+
115
+ ```
116
+ startup-settling (tran): fail
117
+ FAIL settle-time: V(OUT) measured 0.0082 (expected max 0.006, margin -0.0022)
118
+ ```
119
+
120
+ Tests that cannot run in the current environment skip rather than fail — a
121
+ missing binary, an unconfigured server, an analysis the installed CLI does
122
+ not support, a check inapplicable to the project. A red run means a
123
+ simulation ran and disagreed with its expectations.
124
+
125
+ ## Library
126
+
127
+ ### Circuits
128
+
129
+ ```python
130
+ import simantic
131
+
132
+ report = simantic.run_tests("hardware/psu")
133
+ print(f"{report.summary.passed}/{report.summary.total} passed")
134
+
135
+ for m in report.test("rails-op").measurements:
136
+ print(m.describe()) # out-dc: V(OUT) measured 1.597 (expected eq 1.597 +/- 0.02, margin 0.02)
137
+ ```
138
+
139
+ A failing test is data, not an exception: it arrives in the report with its
140
+ measured value, declared bounds, and margin. Only conditions that prevent a
141
+ run at all — bad project, missing `kicad-cli`, invalid testplan — raise
142
+ `AnalogCliError`.
143
+
144
+ ### Firmware
145
+
146
+ The shortest path is a pytest fixture — no manifest, no flags:
147
+
148
+ ```python
149
+ def test_firmware_boots(pyrite):
150
+ run = pyrite("build/zephyr.elf", board="stm32f401",
151
+ expect=["Hello World!"], expect_absent=["FAULT"])
152
+ assert run.passed, run.failure_report()
153
+ ```
154
+
155
+ `pyrite` runs the ELF offline on the pure-Rust backend and hands back the
156
+ UART transcript. The fixture skips when no binary is installed, so a suite
157
+ stays green on a machine that has not run `smtc install pyrite`.
158
+
159
+ The same runner is available as a plain function, and `sim` has its own:
160
+
161
+ ```python
162
+ run = simantic.run_firmware(
163
+ "build/zephyr.elf",
164
+ mcu="STM32F401RE", # resolved by the backend through your account
165
+ expect=["RESULT: PASS"],
166
+ expect_absent=["RESULT: FAIL"],
167
+ )
168
+ assert run.passed, run.failure_report()
169
+ ```
170
+
171
+ `sim` emits no structured report — the only observable is UART text — so the
172
+ verdict is substring matching, the same contract `test.yaml` manifests use.
173
+ Pass `repl=` instead of `mcu=` for a platform file you author yourself.
174
+
175
+ MCU models are not distributed with this package: `mcu=` resolves them
176
+ through your account. If you have a local model library, set
177
+ `$SIMANTIC_MCU_LIB` to resolve from it instead — which is also what applying
178
+ a fixture's `overlay` fragment requires.
179
+
180
+ Some installations need a separate simulation server. When one does, the SDK
181
+ raises `ServerNotConfigured` and the pytest plugin skips, rather than
182
+ reporting a firmware failure.
183
+
184
+ ## Telemetry
185
+
186
+ When you are authenticated, a completed pytest session reports its **shape**
187
+ to your account: how many simulator tests ran, how many passed, failed, or
188
+ skipped, plus this package's version, your Python version, OS, and CPU
189
+ architecture. One request per `pytest` invocation, never per test.
190
+
191
+ It also counts **which calls you make** — SDK functions, MCP tool names, and
192
+ `smtc` subcommands, by name only. These are buffered in
193
+ `~/.simantic/usage.jsonl` and uploaded as counts at most once an hour, so no
194
+ simulation ever waits on the network. You can read that file at any time; it
195
+ is one JSON object per line and contains nothing but call names.
196
+
197
+ It does **not** send file paths, project names, test names, firmware, or
198
+ simulation output. Those are yours.
199
+
200
+ ```bash
201
+ export SIMANTIC_TELEMETRY=0 # or DO_NOT_TRACK=1
202
+ ```
203
+
204
+ `smtc status` prints exactly what is sent and whether it is on. Reporting is
205
+ best-effort: if it fails, is blocked, or you are offline, your tests are
206
+ unaffected and nothing is printed.
207
+
208
+ ## Compatibility
209
+
210
+ Speaks the `analog-cli.test-report/1` schema. Additive fields within that
211
+ revision are tolerated; a breaking revision raises `ReportError` rather than
212
+ silently misreading a report.
213
+
214
+ Multi-machine `test.yaml` fixtures — those with a `machines:` map — need the
215
+ `--scenario` runner and are not driven yet; they report as skips.
216
+
217
+ ## License
218
+
219
+ MIT. The simulators it drives are separate software under their own terms.
@@ -0,0 +1,21 @@
1
+ simantic/__init__.py,sha256=nRcQNKGWNTcMBdGuVkwyTejxadfI-OyI80aRVGcBaaE,2305
2
+ simantic/__main__.py,sha256=RsPiml36e1H0q0vIoUA-S_Phwxn1Gls_j2goR7IRFJA,336
3
+ simantic/_cli.py,sha256=BpL1DOAX5WDz5BvSzaDnk5HH5Jmsz-JQO1J4YgeGOKI,4823
4
+ simantic/_locate.py,sha256=Czsu0x1mHSLuL2A3Srusbe3_SNiLcKcMkW_9LCsMKJ4,2258
5
+ simantic/agent.py,sha256=1gcvT0jEtnwgQ8WxTFUe33TCIrujJPYqh8kEM9NzJZo,6102
6
+ simantic/analog.py,sha256=kWeNV1-tM08lZqQM4WY3ASvZhTQSX6nXJZi9O7baswE,3249
7
+ simantic/auth.py,sha256=dxTgBk-QbhvlLd5y0mx-CYITHxXgQ0pup48DZt6MwE8,7777
8
+ simantic/engine.py,sha256=IxH_1pUlwbmDIfUu3sLWT-VUo62Qsvt1zURb8ycFnQ8,3383
9
+ simantic/fixtures.py,sha256=6z8SWrEqoET-Ze6cpcazlnCUqkg_-0J5OLWE4RNjwlQ,4679
10
+ simantic/install.py,sha256=ZzPJCGOGov8Klt-l4Frx7Czee3g5ZwXa46wlpsIvYgw,11143
11
+ simantic/mcu.py,sha256=IWfeqZQWrunuV10ClWdBVsNiYMylpcncs_UHzebfb1U,5932
12
+ simantic/pyrite.py,sha256=oe4h6Jm_y_BfQYPGESRzzJ9QpRJaG1cgO1yhXMCVPc4,2448
13
+ simantic/pytest_plugin.py,sha256=WlyGTw1iR7N49uKou7utD55QnJ4dRZVQSEKbbLaHrA8,7524
14
+ simantic/report.py,sha256=SBrZXaPMrN_nZWNnxoZ_yVgpnaJOyj62H0H5vUc6v4o,6185
15
+ simantic/session.py,sha256=wR05NWScWmzONcQCA0y_roa0REOeg0ITqN_fKPqey9g,17856
16
+ simantic/telemetry.py,sha256=MAhDtVqj87q0hH-QpJi-m11kw4QdeAllViI3uVW-9Sg,7473
17
+ simantic-0.2.0.dist-info/METADATA,sha256=3auIVM1epovDQvcDtPBKxq0zltEOPUVVqo80ZrKoWuk,8807
18
+ simantic-0.2.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
19
+ simantic-0.2.0.dist-info/entry_points.txt,sha256=uH213AdoDjOcclE5pHI5FLz0mRVWkxJu6cRbnvmOpRk,120
20
+ simantic-0.2.0.dist-info/licenses/LICENSE,sha256=VRIZiU3s-IRqoBOKW4KinZI6Y4Gf7H8xrXcM3WljfOk,1065
21
+ simantic-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,6 @@
1
+ [console_scripts]
2
+ simantic = simantic._cli:main
3
+ smtc = simantic._cli:main
4
+
5
+ [pytest11]
6
+ simantic = simantic.pytest_plugin
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Simantic
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.