Skip to main content

OEP Python client

日本語

The host side of Open Embedded Probe (OEP). It speaks the v1 protocol of oep-spec (docs/oep-core.ja.md and the standard interfaces docs/oep-if-*.ja.md, a candidate being settled). The wire numbers come from oep_client.registry, a verbatim copy of oep-spec's generated generated/oep-v1/oep_v1_registry.py. This is an experimental stage: breaking changes are expected and no compatible API is promised. For a map of the specification, start with oep-spec's docs/review-guide.ja.md.

It follows OEP's division of work: the knowledge of the target lives in the host. The probe knows only its wires and DMI / DP-AP transfers; the CH32 flash controller, the RAM loader, the RP2350 boot ROM, the Cortex-M debug registers and so on are here.

pip install oep-client-python     # PyPI (import oep_client); a checkout: pip install -e <checkout>
uv run pytest                     # in a checkout

import oep_client is all it takes. The registry is copied from oep-spec with tools/sync_registry.sh. PyPI's oep-client is another project, so the distribution is named oep-client-python.

Releases: run the GitHub Actions workflow Release (workflow_dispatch, version X.Y.Z or X.Y.ZbN). tools/prepare_release.py sets the version in pyproject.toml, uv.lock and oep_client.__version__ and turns CHANGELOG.md's Unreleased into that version; after the tests and the build it commits, tags, makes the GitHub Release and publishes to PyPI (Trusted Publishing). Record changes under Unreleased in CHANGELOG.md, (EN) and (JA).

Modules (oep_client)

Module Contents
host requests and results, the session id and the lock, call() (raises unless it worked), pipelining, the errors (OepError / Rejected / Failed)
link transports: serial ports (always COBS + CRC as 0x00 <COBS> 0x00, bytes outside frames skipped as noise, opened exclusively), USB vendor bulk / HID and TCP (length frames, the §5.1 resync); matching by corr and resending; open_host(target)
core interfaces by name (cached), confirm, the probe's describe (labels, the transport list), taking the lock (take), the pin plan, the Interface base
riscv oep.wire.rvswd / oep.wire.swio, oep.target.riscv-dm, finding the reset line, attach through GPIO
console oep.target.console (position streams) and ConsoleIO, read as bytes
fixture oep.fixture.gpio / uart (revision 1)
config oep.probe.config (slots, binds, plan / label / idle items, get / set / save / erase, the live slot and bind state)
capture oep.fixture.capture (revision 1, oep-spec oep-if-capture). Every segment read goes to the Host.on_capture callbacks as a CaptureRecord (the hook for run recorders; no wireskein dependency)
esp32_targets the custom interfaces io.github.ch32-riscv-ug.esp32.i2c-target / spi-target (the ESP32 I2C / SPI targets of oep-probe-arduino)
decode decoding capture channels (I2C)
registry generated from oep-spec's number table (never edited; copied again from oep-spec)
arm oep.wire.swd, oep.target.arm-adi, MEM-AP, halting and calling functions on a Cortex-M
ch32_flash writing a CH32 (a RAM loader, page by page)
rp2350 flash and reboot through the RP2350 boot ROM
uiapduino into and out of the UIAPduino bootloader
catalog / names / interfaces / dump the capability list and describe shapes, display
fake / endpoint / fake_serial / fake_serve the fake probe (below)
target one place to import the main ones from

Example

from oep_client import core, link, riscv, ch32_flash

hst = link.open_host("/run/board-identify/by-id/esp32-series-30eda0e31108")   # pipelined
# a serial port (always COBS), "tcp://127.0.0.1:PORT" (a broker), "usb" / "usb:303a:0002[:SERIAL]" (vendor, then HID)
core.take(hst, 30000, owner="flash script")   # the only way in: force; else wait out the lease, name the holder
wire = riscv.Wire(hst, "oep.wire.rvswd")
conn, _ = wire.attach(halt=True)
dm = riscv.RiscvDm(hst, conn)
dm.reset_halt()
result = ch32_flash.program(hst, dm, open("sketch.bin", "rb").read(), ch32_flash.PROFILES["x035"])
dm.reset(confirm=True)
wire.detach(conn)
hst.end()

The oep command

oep dump --port <probe>                      # what the probe offers (--fake p4-x035: no hardware)
oep config show <probe>                      # the settings and the live slot / bind state
oep config slot <probe> --name x035 --wire rvswd --pins 2,54 --attach at-boot --retry 1 --mechanism dmseq
oep config bind <probe> --port 1 --mode last-reset --stream slot:x035
oep config save <probe>                      # kept over a restart (also: remove, erase)

<probe> is a serial port, tcp://HOST:PORT or usb[:VID:PID[:SERIAL]]. A change takes the lock (owner "oep config") and ends the session after it; it takes effect at once and, after save, stays over a restart.

A run on hardware: ArduinoCore-CH32's tests/manual/oep_smoke/ (oep_smoke.py, oep_probe_checks.py).

The fake probe (a working spec)

endpoint.Endpoint is a fake probe that answers as oep-spec says; ch32rv, this client and the probe firmware are checked against it (when the spec changes, this is brought in line before the firmware). fake holds example declarations (profiles p4-x035, esp32-v003, p4-bench = a made-up jig with three slots and two seats), fake_serial the byte side of a serial port (COBS candidates, raw bytes and binds, held during a session and resumed after it).

Other programs' tests run fake_serve as a child process:

python -m oep_client.fake_serve --pty --profile p4-bench --slot x035 --bind last-reset \
    --console 'uptime %d\r\n' --every 100
# first line: PTY /dev/pts/N (PORT n with --tcp 0); it ends when stdin closes

The pty is a serial port (the host opens it with TIOCEXCL); --tcp PORT is --framing cobs (a serial port) or --framing length (the vendor bulk / TCP form). Faults: --drop N (the N-th answer is not sent, once; the request did run, so a resend gets the remembered result), --noise TEXT (noise before every answer), --corrupt N (the N-th answer's CRC broken once). --uart-plan / --uart-rx give the first fixture UART a plan and RX bytes, --run-hook a host's own model of riscv-dm run. The rest: --help.

Metadata

Release files for oep-client-python 0.0.8

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for oep-client-python 0.0.8
File Size Uploaded
oep_client_python-0.0.8.tar.gz 145.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for oep-client-python 0.0.8
File Interpreter ABI Platform
oep_client_python-0.0.8-py3-none-any.whl Python 3 none any Details

Total release size: 269.3 kB

Release files / oep_client_python-0.0.8.tar.gz

Download URL oep_client_python-0.0.8.tar.gz
Size 145.7 kB
Tags Source
SHA-256 checksum
How to use checksums
73750ef622327217ad6af1b0b1ce4fbd03623be9a7aa494a3f222f32ddebf5a4
BLAKE2b-256 checksum
How to use checksums
16824399892e86380de726533b78d3f2d7027c2b1970eb476431ffdf22f00763
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 30, 2026.

Transparency log

Release files / oep_client_python-0.0.8-py3-none-any.whl

Download URL oep_client_python-0.0.8-py3-none-any.whl
Size 123.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bcdb4003fd4570be5da6cdddcc309812f6521d7ec03f8a246e9fb7e739c6d855
BLAKE2b-256 checksum
How to use checksums
bf241d18f1e56bfcadd6c55ec6b8467b6de94afb4f6e557c89ed743d1778e995
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 30, 2026.

Transparency log

Release history Release notifications | RSS feed

0.0.20

2 release files

0.0.19

2 release files

0.0.18

2 release files

0.0.17

2 release files

0.0.16

2 release files

0.0.15

2 release files

0.0.14

2 release files

0.0.13

2 release files

0.0.12

2 release files

0.0.11

2 release files

0.0.10

2 release files

0.0.9

2 release files

This release

0.0.8 This release

2 release files

0.0.7

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page