Exemplo
Decide whether attrs still buys anything over dataclasses on modern Python, and what changes in each direction
sha256:db459e2a5619e5bc96877e0fc6aefd6229be3342cc28685624eacd9e170bf4b7
PUBLISHED
L3_CONTRACT_PASS
MIT-0
Caso
- Objetivo
- Decide whether attrs still buys anything over dataclasses on modern Python, and what changes in each direction MIGRATION
- Pacotes
- attrs 26.1.0
- Ambiente
- python 3.12
- Criado
- 2026-08-14T12:07:55Z
Contrato
- assert neither library enforces an annotation: a dataclass stores a str in an int field and so does an @define class with no validator
- assert an attrs validator runs at __init__, with instance_of raising TypeError and gt raising ValueError, and that ge, lt and le raise ValueError too
- assert the converter runs before the validator, so converter=int and instance_of(int) coexist and "9000" is stored as 9000
- assert @define runs converter and validator again on assignment, where the dataclass keeps whatever was assigned
- assert __post_init__ is not the equivalent hook: it runs at __init__ and again through dataclasses.replace, and never on assignment
- assert on_setattr=NO_OP keeps the __init__ conversion and drops the assignment one, so the field holds either type
- assert attrs.validators.disabled() switches off the validators of every class in the process, so a validator is not a boundary
- assert @define is slotted, so an undeclared attribute raises AttributeError and has no __dict__, while the dataclass accepts it
- assert @dataclass(slots=True) exists and that both decorators return a new class object, not the one that was decorated
- assert attrs puts __weakref__ in __slots__ and @dataclass(slots=True) does not, so weakref.ref works on the attrs class and raises TypeError on the dataclass
- assert the slotted dataclass breaks zero-argument super() on Python 3.12 with TypeError, and attrs' slotted subclass with the same body does not
- assert the cause is the __class__ closure cell, which attrs rebinds to the class it returns and the stdlib leaves on the discarded original, and that super(Child, self) works around it
- assert attrs' FrozenInstanceError reports module attr.exceptions and is a different class from dataclasses.FrozenInstanceError, with neither a subclass of the other
- assert catching dataclasses.FrozenInstanceError does not catch the attrs error and catching the attrs error does not catch the dataclass one
- assert both frozen errors subclass AttributeError, the narrowest single except clause that covers both
- assert a mandatory field after a defaulted one is refused at class definition by both, ValueError from attrs and TypeError from dataclasses
- assert kw_only=True lifts that rule in both libraries
- assert an attrs field named _token takes the init keyword token and refuses _token, while the dataclass field does the reverse
- assert attrs.evolve keys on the init parameter name and re-runs the validator, while dataclasses.replace keys on the field name and runs no validator of its own
- assert an init=False field is refused by replace with ValueError naming the field and by evolve with TypeError for an unknown keyword
- assert the libraries do not recognise each other: is_dataclass and attrs.has are False, dataclasses.fields raises TypeError and attrs.fields raises NotAnAttrsClassError
- assert a fresh interpreter run under -W error imports attr with no warning at all, and attr.__version__ matches the installed attrs
- assert the namespaces moved one way only: define, field, frozen, evolve and fields are the same objects on attr and attrs, while s, ib, attrib and attributes exist only on attr
- assert a shared name is not always a shared object: attr.asdict is not attrs.asdict and only the old one takes retain_collection_types, and attr.exceptions is not attrs.exceptions
- assert an @attr.s class is unslotted and converts at __init__ only, leaving object.__setattr__ in place where @define installs its own
Arquivos
- csx.json
- requirements.txt
- src/__init__.py
- src/models.py
- test/contract.py
Seeder de origem
Recibos de verificação
- python 3.12 · CONTAINER_RUN · compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS · pypi@1 · 2026-08-14 · ed25519:a2ec939a4c60e243
- · CONTAINER_RUN · compile:SKIPPED · contract:FAIL · load:SKIPPED · resolve:PASS · pypi@1 · 2026-08-14 · ed25519:7da3f610facce8bf