⚡ Sangsakala Proxy 🏠 Home
Skip to content

Support a small amount of generic converters for attrs - #22206

Open
A5rocks wants to merge 3 commits into
python:masterfrom
A5rocks:attrs-generic-converters
Open

A5rocks wants to merge 3 commits into
python:masterfrom
A5rocks:attrs-generic-converters

Conversation

@A5rocks

@A5rocks A5rocks commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

See #8625.

To be honest I don't see why we can't recurse on the converter return type along with the attribute type, and then set all the typevars we see based on the relevant part of the attribute type. I didn't do that since I don't know the right visitor to do so, but presumably I'm missing something that makes this harder?

@A5rocks A5rocks changed the title Support a small amount of #8625 Support a small amount of generic converters for attrs Oct 14, 2026
@github-actions

This comment has been minimized.

@A5rocks

A5rocks commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

@JukkaL what do you think of this approach? (TBH I don't know which operations are safe or not)

@JukkaL JukkaL left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! I found a few issues when trying this out.

Variadic generics can trigger a crash:

# Crash: zip(..., strict=True) in Attribute.argument raises ValueError
from typing import Generic, TypeVar

from attrs import define, field
from typing_extensions import TypeVarTuple, Unpack

T = TypeVar("T")
Ts = TypeVarTuple("Ts")

class V(Generic[Unpack[Ts]]):
    def __init__(self, *x: Unpack[Ts]) -> None: ...

def foo(x: T) -> V[T]: ...

@define
class B:
    v: V[int, str] = field(converter=foo)

Also this is a regression from master:

from typing import Generic, TypeVar

from attrs import define, field

T = TypeVar("T")
S = TypeVar("S")

def ident(x: T) -> T:
    return x

# The field type S is not an Instance, so the new mapping is skipped.
@define
class A(Generic[S]):
    b: S = field(converter=ident)

# master reveals "b: S", this PR "b: T"
reveal_type(A.__init__)

The fix doesn't work when there is subclassing:

from typing import Generic, TypeVar

from attrs import define, field

T = TypeVar("T")

class G(Generic[T]):
    def __init__(self, x: T) -> None: ...

@define
class C:
    c: G[int] = field(converter=G)

# Fix lost in subclasses: inherited attributes are rebuilt via
# Attribute.deserialize, which doesn't restore Converter.ret_type.
@define
class E(C):
    e: int = 0

# C gets "c: int" but E gets "c: T" (unbound), so every argument is rejected with
# 'expected "T"', even a valid E(1).
reveal_type(C.__init__)
reveal_type(E.__init__)
E("x")

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

core (https://github.com/home-assistant/core)
+ homeassistant/helpers/device_registry.py:1040: error: Unused "type: ignore" comment  [unused-ignore]
+ homeassistant/helpers/device_registry.py:1043: error: Unused "type: ignore" comment  [unused-ignore]
+ homeassistant/helpers/device_registry.py:1045: error: Unused "type: ignore" comment  [unused-ignore]
+ homeassistant/helpers/device_registry.py:1074: error: Unused "type: ignore" comment  [unused-ignore]
+ homeassistant/helpers/device_registry.py:1076: error: Unused "type: ignore" comment  [unused-ignore]
+ homeassistant/helpers/device_registry.py:2179: error: Unused "type: ignore" comment  [unused-ignore]
+ homeassistant/helpers/device_registry.py:2181: error: Unused "type: ignore" comment  [unused-ignore]
+ homeassistant/helpers/device_registry.py:3231: error: Unused "type: ignore" comment  [unused-ignore]
+ homeassistant/helpers/device_registry.py:3232: error: Unused "type: ignore" comment  [unused-ignore]
+ homeassistant/helpers/device_registry.py:4472: error: Unused "type: ignore" comment  [unused-ignore]
+ homeassistant/helpers/device_registry.py:4489: error: Unused "type: ignore" comment  [unused-ignore]
+ homeassistant/helpers/device_registry.py:4540: error: Unused "type: ignore" comment  [unused-ignore]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants