chumicro-workspace
Host CLI for ChuMicro project workspaces: bring up a new board, write a project, ship it, and watch it run.
One command registers a board you just plugged in. Another scaffolds a project. deploy sends the project's code and the config it needs to the board and starts it, and deploy --tail streams the board's output back to your terminal while it runs. Your wifi password lives in one file you keep out of git, and the board receives a merged copy at deploy time instead of a hard-coded string in your source.
Part of the ChuMicro family, small focused Python libraries for microcontrollers and laptops. Browse all workbench tools. This is a workbench tool: it runs on your laptop, not on the board. The on-device side is
chumicro-config, which reads the msgpack this package writes.
Install
pip install chumicro-workspace
chumicro-deploy comes along with its pyserial and mpremote dependencies, plus msgpack, ruamel.yaml, and tomlkit. chumicro-repl is optional and loaded only when you use it: install it to get the interactive repl command and deploy --tail.
The starter workspace lives at ChuMicro/ChuMicro-Workbench-Template. Typical day-zero is to git clone it (or click "Use this template" on GitHub) and run python run.py setup, which creates a venv and installs this package.
Experimental (pre-release) versions and channel switching
Pre-release builds publish automatically when the package version is bumped.
pip install chumicro-workspace-experimental
Quick example
The workspace template ships a run.py shim that forwards to chumicro-workspace, so both spellings work:
# Inside a freshly cloned workspace:
python run.py setup # one-time bootstrap
python run.py bootstrap back-porch --address /dev/cu.usbmodem1101
# (probes the runtime, registers the board in devices.yml, and prints
# what to run next. Add --demo to also ship the built-in demo payload.)
# Then iterate:
python run.py new my_project # scaffold from projects/_template/
python run.py deploy my_project # ship + run on the active device
python run.py deploy my_project --tail 30 # ship + run, then tail for 30 s
python run.py repl # interactive REPL on the active device
How config flows from your edits to the device
The runtime config a project receives at boot is the deep-merge of two host-side files, both using the same [section]-keyed TOML shape:
secrets.toml projects/<name>/project_config.toml
workspace-wide credentials per-project knobs: sample period,
and device defaults, in one mqtt topic, sensor pins. Wins on
place. Keep it out of git. any key both files set.
│ │
└────────────────┬──────────────────┘
▼
merge_configs ← chumicro_workspace.merge
│ (deep per-key merge: dicts
▼ recurse, lists replace)
flatten_config ← chumicro_workspace.flatten
│ ([wifi] ssid becomes the
▼ flat key "wifi.ssid")
write_runtime_config ← chumicro_workspace.writer
│ (use_single_float=True so
▼ CircuitPython's built-in
/runtime_config.msgpack on device msgpack module accepts it)
│
▼
chumicro_config.runtime ← reads it on the device
chumicro-workspace dump-config <project> prints the merged dict your project would receive without deploying anything, which is the fastest way to find out which file a key came from.
What's included
CLI subcommands
chumicro-workspace <subcommand> (or python run.py <subcommand> from a workspace).
| Group | Commands |
|---|---|
| Bootstrap / setup | setup, update, bootstrap (workspaces are created by cloning the template repo, not by a command) |
| Authoring | new <path> (project), new <name> --library (chumicro-style library), new <name> --workbench (host-only tool) |
| Config | dump-config <project>, config-validate <project> |
| Devices | add-device, probe, discover, devices, remove-device, reset-device, rename --device |
| Deploy / run | deploy <project> [--tail SECONDS], deploy-example, demo, repl [--tail SECONDS], projects [--flat] |
| Libraries | library list|browse|add|update|remove|forget|switch-channel |
| Health | status, doctor (also runs as a fast pre-deploy gate; deploy --skip-health-check opts out) |
| Quality | test, lint, preflight (chains lint + test; respects the quality: knobs) |
| Firmware | install-firmware, upgrade-firmware, reset-board |
Every device command rewrites devices.yml in place, keeping your comments and field order. Three zones decide who wins a conflict: user-owned fields (id, description, deploy_mode) are never overwritten, probed-always fields (address, firmware_version) refresh on each probe, and hardware-once fields (runtime and the hardware: block) need an explicit --force, because a changed uid usually means you swapped boards.
shared/ vs libraries/: when to use each
Both hold code your projects can import. Pick by weight:
| Want to ship… | Drop it under | Imports look like | Notes |
|---|---|---|---|
| A 50-line helper your projects share | shared/foo.py |
from foo import bar |
No tests, no version, no scaffolding. The search path roots at shared/ itself, so there is no shared. prefix to qualify against. |
| A full chumicro-style library you might publish someday | libraries/<name>/ (via new <name> --library) |
import <name> |
Gets src/, tests/, docs/, examples/, pyproject.toml, and VERSION: the publishable-library layout. |
| A published chumicro library | libraries/<name>/ (via library add <name>) |
import chumicro_<name> |
Fetched from the release channel along with the chumicro libraries it imports. A re-fetch backs up a tree you have edited instead of clobbering it. |
| A third-party module you vendor yourself | packages/ |
import <name> |
Nothing populates this for you. Drop files in and the search path finds them last. |
The import-graph search path resolves explicit library_sources: overrides first, then shared/, then every libraries/<name>/src/ (auto-discovered), then packages/.
Boot-shim layout
When a project ships app.py with a top-level run() and no code.py / main.py of its own, deploy picks the boot-shim layout automatically and ships this on-device shape:
/code.py # synthesised shim: from app import run as _run; _run()
# (or /main.py on MicroPython; only the runtime-matching
# file is written, never both)
/app.py # your code: def run(): ...
/runtime_config.msgpack # merged config (see the pipeline above)
/lib/<chumicro_libs>/... # libraries the project imports (when --import-graph composes)
Deploy owns the entrypoint file at the device root and you own everything else. One project per board: to change which project runs, deploy the other one.
When a project ships its own code.py / main.py, deploy uses plain mode instead and copies the project's files to the device root verbatim, synthesising nothing. The filename declares which runtime the project targets, so deploying a code.py-only project to a MicroPython board (or a main.py-only project to CircuitPython) fails with a clear message before any bytes leave the host.
Where this fits
This package builds on chumicro-deploy, which owns the serial and USB transports, the firmware flashing flow, and the devices.yml schema. chumicro-repl is optional and imported only when you reach for repl or deploy --tail. Most people use chumicro-workspace and never call the lower-level tools directly. The on-device counterpart is chumicro-config, which reads the msgpack this package writes.
Public Python API
The package root keeps a deliberately narrow surface:
from chumicro_workspace import (
WorkspaceLayout, # find the workspace root, resolve project paths
WorkspaceNotFoundError, # raised when no workspace.yml sits above cwd
ProjectClassification, # how a projects/ subdirectory was classified
compose_runtime_config, # the merged config as a dict, nothing written
read_workspace_yml_template, # starter workspace.yml content
read_devices_yml_template, # starter devices.yml content
verify_examples, # AST check that a library's examples import cleanly
)
Everything else lives in a submodule and is imported by its real path:
from chumicro_workspace.pipeline import build_runtime_config
from chumicro_workspace.deploy_source import WithRuntimeConfig, project_directory_source
from chumicro_workspace.boot_shim import SHIM_ENTRYPOINT_SOURCE, project_boot_source
from chumicro_workspace.import_graph import build_search_paths, project_import_graph_source
from chumicro_workspace.onboarding import BoardState, detect_board_state
from chumicro_workspace.firmware_support import check_firmware_supported
from chumicro_workspace.health import collect_health_findings
from chumicro_workspace.quality import load_quality_config
from chumicro_workspace.recovery import detect_hints
from chumicro_workspace.scaffold import scaffold_library
Reading and writing devices.yml lives in chumicro-deploy, which owns the file's schema:
from chumicro_deploy.config.devices_yaml import (
add_device,
dump_devices,
load_devices,
update_device_address,
)
Companions
| Workbench tool | Why you'd use it alongside |
|---|---|
chumicro-deploy |
Lower-level transport and flashing. Workspace composes on top |
chumicro-repl |
Interactive and tail serial REPL |
chumicro-pytest-device |
Run tests on real boards via pytest |
Examples
This package is a CLI tool, so there is no "use it in your code" example that isn't just a CLI subcommand in Python clothing. See the user guide for end-to-end walkthroughs (the config pipeline, deploy layouts, library scaffolding, board onboarding) and chumicro-workspace --help for the full command surface.
The Python API exists so the chumicro-workspace CLI and the workspace template's run.py shim can compose against it, not as a "build your own workspace tool" surface. If you find yourself reaching for it, the CLI probably already does what you want; file an issue if it doesn't.
Contributing
Issues, bug reports, and pull requests are welcome, and so is "I ran it on this board and here's what happened", some of the most useful feedback a hardware project can get. Development happens in the ChuMicro repository, whose contributing guide covers setup and the test workflow.
Docs
📖 Stable docs · Experimental docs
Find this library
- PyPI: chumicro-workspace
- Source: workbench/workspace
- Workspace template:
ChuMicro/ChuMicro-Workbench-Template
License
Metadata
Release files for chumicro-workspace 0.54.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| chumicro_workspace-0.54.4.tar.gz | 178.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| chumicro_workspace-0.54.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 399.9 kB
Release files / chumicro_workspace-0.54.4.tar.gz
| Download URL | chumicro_workspace-0.54.4.tar.gz |
|---|---|
| Size | 178.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e85ffd0c3618ce4373b327ad7b0061901c4a7f06b140222d7e6ab61880fddd9c
|
|
BLAKE2b-256 checksum How to use checksums |
3bec0829ad732fef3d653ca29fb887485436b2c84387a97bd5fa5a1eb1537ae8
|
| 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 Aug 18, 2026.
Transparency logRelease files / chumicro_workspace-0.54.4-py3-none-any.whl
| Download URL | chumicro_workspace-0.54.4-py3-none-any.whl |
|---|---|
| Size | 221.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6c85f4081105c84c13e7216b62df834442abfc163c95ffa8379172883cd3e1ff
|
|
BLAKE2b-256 checksum How to use checksums |
cc23738d6028a68019d347b3695b5d2d1c9eeb2dc4e3c014d3122ec0d7f31acc
|
| 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 Aug 18, 2026.
Transparency log