Differences between udataclasses and dataclasses

Field attributes must have values

In MicroPython and CircuitPython, fhe following code does not work:

from udataclasses import dataclass

@dataclass
class Product:
    name: str
    quantity: int

The attributes must either be given a default value, or you can use field() with no arguments:

from udataclasses import dataclass

@dataclass
class Product:
    name: str = field()
    quantity: int = 0
Explanation

MicroPython accepts type annotations as valid syntax, but discards them. There is no way in the runtime to access type annotations, e.g. using inspect.get_annotations() in standard Python.

A line that gives an attribute a type annotation without providing a value is effectively the same as an empty line in MicroPython. Take a look at the following MicroPython REPL session:

>>> class C:
...     a: int
...     b: int = 0
...
>>> C.__dict__.keys()
dict_keys(['__module__', 'b', '__qualname__'])

The code knows about the attribute b, but there is no mention of a. Because of this, udataclasses fields have to be assigned a value in order for us to be able to detect the field.

Fields are sorted alphabetically instead of by source order

Methods we generate such as __repr__, and functions such as fields sort fields alphabetically by name, rather than preserving the order in your source code. For example:

from udataclasses import dataclass, field

@dataclass
class Product:
    quantity: int = 0
    name: str = field()

# This prints 'Product(name="bolts", quantity=2)'
print(Product(quantity=2, name="bolts"))
Explanation

MicroPython does not store class attributes in creation order, so @dataclass cannot retain the order of the fields in the order they were listed in the user’s source code. In order to provide a consistent order, @dataclass automatically sorts the field names alphabetically in its output.

__init__ arguments are keyword-only

Classes decorated with @dataclass will not allow passing positional arguments to __init__. For example:

from udataclasses import dataclass, field

@dataclass
class Product:
    name: str = field()
    quantity: int = 0

# The commented-out line below raises a TypeError
# Product("bolts", 2)

# This next line works however:
Product(name="bolts", quantity=2)
Explanation

We sort fields alphabetically. If we allowed positional __init__ arguments, the order of those arguments would not match the field order in your code. Therefore, allowing positional arguments would be very error-prone. Instead we only allow arguments to be passed in by keyword, which has no ordering constraints.

Field.type has the wrong value

MicroPython does not store type annotations anywhere, so there is no way for udataclasses to know the correct type for a field. Instead, Field.type is hardcoded to object.

Missing features from dataclasses

We don’t currently support every feature that the standard dataclasses has. These are a work in progress. We aim to have feature parity with at least Python 3.9’s version of dataclasses where possible. See https://github.com/dhrosa/udataclasses/issues for progress on these missing features.