Skip to main content

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 @dataclass and @dataclass(frozen=True).
  • Positional/keyword construction, required fields, defaults, and field(default=...) or field(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, or MISSING. __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_dataclass are 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 --check is 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)

Source distribution for portable-dataclass-mpy 0.1.0
File Size Uploaded
portable_dataclass_mpy-0.1.0.tar.gz 37.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for portable-dataclass-mpy 0.1.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.1.0 This release

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