Skip to content

attrs plugin does not support generic type #18973

Open
@danarmak

Description

@danarmak

Bug Report

The mypy attrs plugin has custom support for attrs.evolve, which has different code branches for regular and generic types (starting here).

It admits attrs.has as a type guard for attrs.evolve if the original type is Any, but not if it's a generic type parameter T with no type bound.

To Reproduce

On python 3.12 (so lacking copy.replace):

def replace[T](value: T, **kwargs: Any) -> T:
    if attrs.has(type(value)):
        return attrs.evolve(value, **kwargs)
    else:
        raise NotImplementedError(f"replace is not implemented for {type(value)}")

Mypy reports, error: Argument 1 to "evolve" has a variable type "T" not bound to an attrs class [misc]

(In the actual code I'd do something else in other branches; this is intended as a replacement for copy.replace on python < 3.13, supporting different types besides attrs classes.)

Using the attr.AttrInstance protocol also doesn't work, which means I can't define my own type guard wrapping attrs.has. This code results in the same mypy error:

def replace[T: attrs.AttrsInstance](value: T, **kwargs: Any) -> T:
    return attrs.evolve(value, **kwargs)

However, this code works (and is a viable workaround):

def replace[T](value: T, **kwargs: Any) -> T:
    val2: Any = value
    if attrs.has(type(val2)):
        return attrs.evolve(val2, **kwargs)
    else:
        raise NotImplementedError(f"replace is not implemented for {type(value)}")

Expected Behavior

Mypy should honor attrs.has as a type guard for attrs.evolve, whether the original type is generic or any other kind.

  • Mypy version used: 1.15.0
  • Python version used: 3.12.10

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions