Portable Dataclass MPY
Published and maintained by 404 Builds. Created by MrCodeGameAndAnime.
A small MicroPython dataclass runtime with field metadata generated on host CPython. Write ordinary module-level dataclasses, generate their ordered field manifest on CPython, then copy the runtime and manifest onto MicroPython.
The host package uses native CPython dataclasses. Only the deployed device overlay
replaces dataclasses; keep it off normal CPython import paths.
Install
Install a released version from PyPI:
python -m pip install portable-dataclass-mpy==0.1.0
To install a downloaded wheel or a source checkout:
python -m pip install path/to/portable_dataclass_mpy-0.1.0-py3-none-any.whl
# From a checkout:
python -m pip install ./root
Host library requirement: Python 3.8 or later. Development/tests use Python 3.12. The package includes the host CLI and the device runtime as package data; installing it does not replace the host's standard-library dataclasses.
Quick start
In root/examples/basic/models.py:
from dataclasses import dataclass, field
@dataclass
class Reading:
value: int
unit: str = "C"
notes: list = field(default_factory=list)
From the repository root, prepare a self-contained device directory:
python root/examples/basic/prepare.py --output root/build/basic-device
This runs the generator and copies the installed runtime, model, and demo into
root/build/basic-device. It also checks that the generated manifest is current.
With MicroPython 1.29.0 on PATH:
micropython -c "import sys; sys.path.insert(0, 'root/build/basic-device'); import demo; demo.main()"
Expected output:
21 C ['bench']
[]
Use your executable's path if it is not on PATH. On a board, copy the four prepared
files (dataclasses.py, _dataclass_fields.py, models.py, demo.py) to a device
import directory and run demo.main(). Qualify your board/build separately.
Generate metadata for your project
portable-dataclass-metadata --source-root src --scan-root my_package --output device/_dataclass_fields.py
portable-dataclass-metadata --source-root src --scan-root my_package --output device/_dataclass_fields.py --check
Scan every deployed module that declares a dataclass. Repeat --scan-root for
multiple packages or files. Relative scan roots prefer --source-root, with an
existing working-directory-relative path as fallback; absolute paths also work.
The import root should be the parent of the consumer package.
Use repeated source-relative --exclude globs for unrelated files, for example
--exclude "tests/**" --exclude "legacy.py". Patterns match the complete relative
path, not gitignore rules; use **/test_*.py for nested test names. Backslashes
are normalized. Recursive scans omit directories named .venv, venv, and
__pycache__. Excluded dependencies still execute if a candidate imports them.
Selected invalid Python remains an error. Broad decorated-class/name detection
can select unrelated code; narrow scan roots or exclusions handle that case.
The generator imports trusted code in a fresh process. Bytecode writes and
terminal input are disabled; --timeout SECONDS changes the 30-second default.
This is not a security sandbox. Transport uses system temp storage, so consumer
roots and their parents need not be writable.
Exit codes: 0 success/current, 1 missing/stale metadata in check mode,
2 discovery, validation, timeout, or output errors. Discovery failure preserves
existing output. Replacement is atomic; containing-directory crash durability is
not promised. Run host --check before every deployment.
Copy portable_dataclass_metadata/micropython/dataclasses.py from the installed
package beside the generated _dataclass_fields.py, with that directory ahead
of device imports. The preparation example shows the complete procedure.
Supported subset
- Module-level
@dataclassand@dataclass(frozen=True). - Positional/keyword construction, required fields, defaults, and
field(default=...)orfield(default_factory=...)(never both). - Ordered field names, same-class value equality, inherited fields/defaults/factories.
- Class-defined custom constructors/equality/hashes follow native preservation rules; inherited implementations are replaced by generated methods as native dataclasses do.
- Frozen value hashing by default; mutable default unhashability and native explicit/ implicit hash rules. Explicit custom hashes are retained.
- Mutable initialization respects
__setattr__; frozen initialization uses the object setter. Post-init presence is captured at decoration, with normal hook dispatch at construction. Custom constructors control their own post-init calls. - Frozen exact instances and declared fields reject changes with
FrozenInstanceError(AttributeError). Undecorated subclasses may add/delete other attributes through parent hooks. Frozen decorators reject class-defined__setattr__/__delattr__.
Unscanned dependencies retain native dataclass options. Consumer decorator/field
calls are checked, including module-level re-exports and Field specifications
supplied by helpers. Other explicit options are rejected even if they spell a
CPython default, such as eq=True or field(repr=True).
Known limitations
- This is a subset, with no generated repr or public
fields,asdict,replace,is_dataclass, orMISSING.__dataclass_fields__is an ordered tuple of names, not CPython's Field dictionary. Type annotations are not validated at runtime. - ClassVar, InitVar, keyword-only/compare/init field options, and removing an
inherited factory are unsupported. Nested/function-local classes, original-name
functional decoration, and
make_dataclassare unsupported and rejected when detected. - Aliased functional re-exports and dormant local definitions using aliased decorators may evade discovery. Use module-level decorator syntax. Duplicate decorated names, including conditional redefinitions, are rejected.
- Empty scans can succeed with an empty manifest. Choose scan roots deliberately.
- Runtime schema freshness cannot be recovered from stripped MicroPython annotations;
host
--checkis required. The runtime validates manifest identity mode and missing keys. - Frozen interception requires a MicroPython build supporting custom setattr/delattr and object setters. Other ports and embedded boards need their own qualification.
Verification
| Environment | Evidence |
|---|---|
| CPython 3.12.14, Windows | Full normal/optimized suite, native-reference comparisons, wheel deployment |
| MicroPython 1.29.0, Windows port | Runtime/parity matrix and installed-wheel deployment |
| MicroPython 1.29.0, Unix port | Independent auditor built v1.29.0 and reports parity/suite verification |
The recorded local qualification has 72 checks: 71 passed and one Unix-only permission check
skipped, both normally and with PYTHONOPTIMIZE=1. The independent Unix audit reports
70 passed/two skipped and separately verifies an unwritable parent as non-root;
it did not test packaging. Other host Python versions remain unqualified.
From a checkout:
python -B -m unittest discover -s root/tests -v
For full release verification, set MICROPYTHON_EXECUTABLE and LIBRARY_WHEEL, then:
python -B root/tests/verify_release.py
Host-only discovery can skip device/wheel checks. Release verification requires them; only unavailable symlink creation and inapplicable POSIX permission checks may skip. Dotted test-module invocation is not supported. Embedded checks survive optimization.
These results predate the repository layout changes. Current packaging must be rebuilt and verified before publication.
More information
MIT License. Copyright 2026 MrCodeGameAndAnime. See LICENSE.md for the full notice.
Metadata
Release files for portable-dataclass-mpy 0.1.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 | |
|---|---|---|---|
| portable_dataclass_mpy-0.1.0.tar.gz | 37.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| portable_dataclass_mpy-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 53.2 kB
Release files / portable_dataclass_mpy-0.1.0.tar.gz
| Download URL | portable_dataclass_mpy-0.1.0.tar.gz |
|---|---|
| Size | 37.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
04014697776fd8e3d042f072268d9bd278c06738ba9cb3f089ea75d86b9e45da
|
|
BLAKE2b-256 checksum How to use checksums |
5e4d6066c3e1ef39a5a65e85b0bae0cdcb4b28903b943d180cac939318b12206
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / portable_dataclass_mpy-0.1.0-py3-none-any.whl
| Download URL | portable_dataclass_mpy-0.1.0-py3-none-any.whl |
|---|---|
| Size | 15.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
81d2af6f8247e9e79dd3688bf2369ffcd4488a5a3e04462ff64042785a15e4ff
|
|
BLAKE2b-256 checksum How to use checksums |
44dd88de8884149bc36d4a6f73de52df5aa7f0de02f177de9989f07e47bf443e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|