python-attrs / python-attrs/cattrs

decimal.Decimal not handled in preconf converters (json, pyyaml)

Open Beginner friendly
#761 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
1.1k
Forks
159
Avg merge
12h 21m
Merged PRs (30d)
6

Description

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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in configure_converter in cattrs/preconf/json.py and cattrs/preconf/pyyaml.py, then run the Decimal reproduction shown in the issue for both preconfs. Done means unstructure emits Decimal as a precise string and structure rebuilds Decimal values, with the JSON and PyYAML round trips succeeding.

Written by the indexing model from the issue text.

Assessment

Tech stack
json, python, yaml
Domain
tooling
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.