python-attrs / cattrs

Composable custom class converters for attrs, dataclasses and friends.
https://catt.rs
MIT License
781 stars 108 forks source link

Support TypeAliasType for structuring #478

Closed declaresub closed 6 months ago

declaresub commented 6 months ago

Structuring in cattrs supports union types represented by a TypeAlias, but not defined via type. Here's a bit of example code.

import attrs
import cattrs.preconf.json
from typing import Literal, TypeAlias

converter = cattrs.preconf.json.make_converter()
@attrs.define
class Foo:
    _type: Literal['foo'] = 'foo'

@attrs.define
class Bar:
    _type: Literal['bar'] = 'bar'

FooBar: TypeAlias = Foo | Bar

data = {'_type': 'foo'}
obj = converter.structure(data, FooBar)

This does what one expects. But the next bit raises a StructureHandlerNotFoundError.

type FooBar1 = Foo | Bar
obj = converter.structure(data, FooBar1)

It would be nice if structure also accepted TypeAliasTypes.

Even though I know better, I suggest that implementation looks straightforward -- I made this change in converters,py around line 330:


    def structure(self, obj: Any, cl: Type[T]) -> T:
        from typing import TypeAliasType

        _cl = cl
        while isinstance(_cl, TypeAliasType):
            _cl = _cl.__value__

        """Convert unstructured Python data structures to structured data."""
        return self._structure_func.dispatch(_cl)(obj, _cl)

after which I was able to structure data with type FooBar1. I note that I was unable to construct a type for which the while loop above would fail to terminate.

Tinche commented 6 months ago

Howdy,

I agree the new type aliases are really cool. I've landed support for them on the main branch a few weeks ago, so you can either wait for the 24.1.0 release, use the main branch (I try to keep it very stable) or look at https://github.com/python-attrs/cattrs/pull/452 for the changes to apply to a converter to have it support type aliases.

Almost all problems in cattrs are solved by registering hooks and this one is no different ;)