cattrs icon indicating copy to clipboard operation
cattrs copied to clipboard

Type Error with tagged Unions (working code but mypy/pyright error)

Open Gentle opened this issue 2 months ago • 3 comments

functionally this code does what I expect it to (asserts go through) but there is a type error in the signature of Converter.structure, it accepts cl: type[T] and UnionType does not match that

from typing import Literal, Union
from dataclasses import dataclass
import cattrs


@dataclass
class Add:
    t: Literal["ADD"]
    v: int


@dataclass
class Sub:
    t: Literal["SUB"]
    v: int


Op = Union[Add, Sub]
Op2 = Add | Sub

assert cattrs.structure(dict(t="ADD", v=10), Op) == Add(t="ADD", v=10)
assert cattrs.structure(dict(t="SUB", v=5), Op2) == Sub(t="SUB", v=5)

if I change this https://github.com/python-attrs/cattrs/blob/main/src/cattrs/converters.py#L587 to

     def structure(self, obj: UnstructuredValue, cl: type[T] | UnionType) -> T:

the type error goes away but I'm not sure if that is the right solution

Gentle avatar Oct 15 '25 14:10 Gentle

There's not really a way to improve this on our own. We need PEP 747 to be accepted and implemented: https://peps.python.org/pep-0747/

The good news is that it's almost certainly going to be accepted, and the Mypy PR is in the final stages. I think pyright might already support it (when imported from a newer version of typing_extensions).

I don't want to add support until the PEP is formally accepted though. It's on our immediate radar though ;)

For now you'll have to use a # type: ignore unfortunately :/

Tinche avatar Oct 15 '25 14:10 Tinche

that was quick, thanks for the link, great to know you are aware and tracking this issue :)

Gentle avatar Oct 15 '25 14:10 Gentle

I just noticed that pyrefly can handle this, the type for x here is inferred as Add | Sub by the LSP

x = cattrs.structure(dict(t="ADD", v=10), Op)

Gentle avatar Oct 15 '25 20:10 Gentle