Bug: decimal.Decimal crashes in preconf converters
Problem
decimal.Decimal is a very common stdlib type in financial and e-commerce applications, but none of the preconf converters handle it. Both unstructure and structure operations fail.
Reproduction
import decimal
import attrs
from cattrs.preconf.json import make_converter
@attrs.define
class Order:
amount: decimal.Decimal
conv = make_converter()
o = Order(amount=decimal.Decimal('19.99'))
# Unstructure passes through the raw Decimal object — not JSON serializable
conv.dumps(o)
# → TypeError: Object of type Decimal is not JSON serializable
# Structure fails entirely
conv.structure({'amount': '19.99'}, Order)
# → StructureHandlerNotFoundError: Unsupported type: <class 'decimal.Decimal'>. Register a structure hook for it.
Same crash with the pyyaml preconf:
from cattrs.preconf.pyyaml import make_converter
conv = make_converter()
conv.dumps(Order(amount=decimal.Decimal('19.99')))
# → RepresenterError: cannot represent an object: Decimal('19.99')
Expected Behavior
The json and pyyaml preconf converters should handle decimal.Decimal out of the box:
- Unstructure:
Decimal → str (preserving full precision — NOT float, which loses precision for values like Decimal('0.1'))
- Structure:
str → Decimal
conv.dumps(Order(amount=Decimal('19.99')))
# → '{"amount": "19.99"}'
conv.loads('{"amount": "19.99"}', Order)
# → Order(amount=Decimal('19.99'))
Why str and not float
Using float(Decimal('19.99')) introduces floating-point precision loss:
>>> float(Decimal('19.99'))
19.99 # looks ok
>>> float(Decimal('0.1')) + float(Decimal('0.2'))
0.30000000000000004 # precision lost
Serializing as a string preserves the exact value and is the standard practice for monetary amounts.
Suggested Fix
In cattrs/preconf/json.py and cattrs/preconf/pyyaml.py configure_converter:
from decimal import Decimal
converter.register_unstructure_hook(Decimal, str)
converter.register_structure_hook(Decimal, lambda v, _: Decimal(v))
Environment
cattrs latest
- Python 3.12
Bug:
decimal.Decimalcrashes in preconf convertersProblem
decimal.Decimalis a very common stdlib type in financial and e-commerce applications, but none of the preconf converters handle it. Bothunstructureandstructureoperations fail.Reproduction
Same crash with the
pyyamlpreconf:Expected Behavior
The json and pyyaml preconf converters should handle
decimal.Decimalout of the box:Decimal → str(preserving full precision — NOTfloat, which loses precision for values likeDecimal('0.1'))str → DecimalWhy
strand notfloatUsing
float(Decimal('19.99'))introduces floating-point precision loss:Serializing as a string preserves the exact value and is the standard practice for monetary amounts.
Suggested Fix
In
cattrs/preconf/json.pyandcattrs/preconf/pyyaml.pyconfigure_converter:Environment
cattrslatest