python-attrs / python-attrs/cattrs

Using make_dict_structure_fn seems to break my other structure hooks

Open
#243 1 comment 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

  • cattrs version: 1.10.0
  • Python version: 3.9.7
  • Operating System: MacOS 11.4
Description

I'm registering two structure hooks which should help me transform my object into a dict and vice versa. One is renaming my field (from id to _id), while the other is transforming my pendulum DateTime into an isostring. For some reason the DateTime unstructure hook is getting lost if I define it after the Class structure hook. If I start with it, everything works.

What I Did
import cattr
import pendulum
from attr import define, field
from cattr.gen import make_dict_unstructure_fn, override, make_dict_structure_fn
from pendulum import DateTime


@define
class Car:
    id: int
    manufactured: DateTime

volvo = Car(id=5, manufactured=DateTime.now())

converter = cattr.Converter()

converter.register_unstructure_hook(Car, make_dict_unstructure_fn(Car, converter, id=override(rename="_id")))
converter.register_unstructure_hook(DateTime, lambda dt: dt.to_iso8601_string())

converter.register_structure_hook(Car, make_dict_structure_fn(Car, converter, id=override(rename="_id")))
converter.register_structure_hook(DateTime, lambda dt, _: pendulum.parse(dt))

print(volvo)

unstructured = converter.unstructure(volvo)
print(unstructured)

structured_again = converter.structure(unstructured, Car)
print(structured_again)

The above results in following error (note the "manufactured" field also did not get unstructured the way it should). It complains I should register a structure hook, which I actually did...:

Car(id=5, manufactured=DateTime(2022, 4, 3, 16, 22, 40, 51844, tzinfo=Timezone('Europe/Brussels')))
{'_id': 5, 'manufactured': DateTime(2022, 4, 3, 16, 22, 40, 51844, tzinfo=Timezone('Europe/Brussels'))}
Traceback (most recent call last):
...
cattr.errors.StructureHandlerNotFoundError: Unsupported type: <class 'pendulum.datetime.DateTime'>. Register a structure hook for it.

Whereas if I switch the order of the (un)structure hooks like below, everything just works:

converter.register_unstructure_hook(DateTime, lambda dt: dt.to_iso8601_string())
converter.register_unstructure_hook(Car, make_dict_unstructure_fn(Car, converter, id=override(rename="_id")))

converter.register_structure_hook(DateTime, lambda dt, _: pendulum.parse(dt))
converter.register_structure_hook(Car, make_dict_structure_fn(Car, converter, id=override(rename="_id")))

Results in ("manufactured" got unstructured correctly in an iso8601 timestamp and id got renamed into _id during unstructure and vice versa):

Car(id=5, manufactured=DateTime(2022, 4, 3, 16, 25, 12, 840444, tzinfo=Timezone('Europe/Brussels')))
{'_id': 5, 'manufactured': '2022-04-03T16:25:12.840444+02:00'}
Car(id=5, manufactured=DateTime(2022, 4, 3, 16, 25, 12, 840444, tzinfo=Timezone('+02:00')))

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 by reproducing the issue with make_dict_unstructure_fn, make_dict_structure_fn, override, and the registration order shown in the report. Trace how the generated Car hooks resolve the DateTime field, then verify that both custom DateTime hooks remain effective regardless of registration order and that the reported round trip succeeds.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.