From 3a8b53520318647ae748e2fadb9430ab54097f48 Mon Sep 17 00:00:00 2001 From: Ilya Konstantinov Date: Sat, 8 Apr 2023 23:23:14 -0400 Subject: [PATCH] Fix attrs.evolve on bound TypeVar --- mypy/plugins/attrs.py | 4 +- test-data/unit/check-attr.test | 72 ++++++---------------------------- 2 files changed, 14 insertions(+), 62 deletions(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index ed9b80603ba08..acc4fbb1e3b55 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -9,7 +9,7 @@ from mypy.applytype import apply_generic_arguments from mypy.checker import TypeChecker from mypy.errorcodes import LITERAL_REQ -from mypy.expandtype import expand_type, expand_type_by_instance +from mypy.expandtype import expand_type from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type from mypy.messages import format_type_bare from mypy.nodes import ( @@ -982,8 +982,6 @@ def evolve_function_sig_callback(ctx: mypy.plugin.FunctionSigContext) -> Callabl return ctx.default_signature assert isinstance(attrs_type, Instance) - attrs_init_type = expand_type_by_instance(attrs_init_type, attrs_type) - # AttrClass.__init__ has the following signature (or similar, if having kw-only & defaults): # def __init__(self, attr1: Type1, attr2: Type2) -> None: # We want to generate a signature for evolve that looks like this: diff --git a/test-data/unit/check-attr.test b/test-data/unit/check-attr.test index 61517b6917f62..45c673b269c58 100644 --- a/test-data/unit/check-attr.test +++ b/test-data/unit/check-attr.test @@ -1970,47 +1970,19 @@ reveal_type(ret) # N: Revealed type is "Any" [typing fixtures/typing-medium.pyi] -[case testEvolveGeneric] -import attrs -from typing import Generic, TypeVar - -T = TypeVar('T') - -@attrs.define -class A(Generic[T]): - x: T - - -a = A(x=42) -reveal_type(a) # N: Revealed type is "__main__.A[builtins.int]" -a2 = attrs.evolve(a, x=42) -reveal_type(a2) # N: Revealed type is "__main__.A[builtins.int]" -a2 = attrs.evolve(a, x='42') # E: Argument "x" to "evolve" of "A[int]" has incompatible type "str"; expected "int" -reveal_type(a2) # N: Revealed type is "__main__.A[builtins.int]" - -[builtins fixtures/attr.pyi] -[typing fixtures/typing-medium.pyi] - -[case testEvolveTypeVarWithAttrsUpperBound] +[case testEvolveTypeVarBound] import attrs from typing import TypeVar - @attrs.define class A: x: int - @attrs.define class B(A): pass - TA = TypeVar('TA', bound=A) -TInt = TypeVar('TInt', bound=int) -TAny = TypeVar('TAny') -TNone = TypeVar('TNone', bound=None) - def f(t: TA) -> TA: t2 = attrs.evolve(t, x=42) @@ -2021,42 +1993,28 @@ def f(t: TA) -> TA: f(A(x=42)) f(B(x=42)) -def g(t: TInt) -> None: - _ = attrs.evolve(t, x=42) # E: Argument 1 to "evolve" has a variable type "TInt" not bound to an attrs class - -def h(t: TAny) -> None: - _ = attrs.evolve(t, x=42) # E: Argument 1 to "evolve" has a variable type "TAny" not bound to an attrs class - -def q(t: TNone) -> None: - _ = attrs.evolve(t, x=42) # E: Argument 1 to "evolve" has a variable type "TNone" not bound to an attrs class - [builtins fixtures/attr.pyi] -[typing fixtures/typing-medium.pyi] -[case testEvolveTypeVarWithAttrsGenericUpperBound] +[case testEvolveTypeVarBoundNonAttrs] import attrs -from typing import Generic, TypeVar - -Q = TypeVar('Q', bound=str) - -@attrs.define -class A(Generic[Q]): - x: Q - +from typing import TypeVar -T = TypeVar('T', bound=A[str]) +TInt = TypeVar('TInt', bound=int) +TAny = TypeVar('TAny') +TNone = TypeVar('TNone', bound=None) +def f(t: TInt) -> None: + _ = attrs.evolve(t, x=42) # E: Argument 1 to "evolve" has a variable type "TInt" not bound to an attrs class -def f(t: T) -> T: - t = attrs.evolve(t, x=42) # E: Argument "x" to "evolve" of "T" has incompatible type "int"; expected "str" - return t +def g(t: TAny) -> None: + _ = attrs.evolve(t, x=42) # E: Argument 1 to "evolve" has a variable type "TAny" not bound to an attrs class -f(A(x='42')) +def h(t: TNone) -> None: + _ = attrs.evolve(t, x=42) # E: Argument 1 to "evolve" has a variable type "TNone" not bound to an attrs class [builtins fixtures/attr.pyi] -[typing fixtures/typing-medium.pyi] -[case testEvolveTypeVarWithAttrsValueRestrictions] +[case testEvolveTypeVarConstrained] import attrs from typing import TypeVar @@ -2064,15 +2022,12 @@ from typing import TypeVar class A: x: int - @attrs.define class B: x: str # conflicting with A.x - T = TypeVar('T', A, B) - def f(t: T) -> T: t2 = attrs.evolve(t, x=42) # E: Argument "x" to "evolve" of "B" has incompatible type "int"; expected "str" reveal_type(t2) # N: Revealed type is "__main__.A" # N: Revealed type is "__main__.B" @@ -2083,7 +2038,6 @@ f(A(x=42)) f(B(x='42')) [builtins fixtures/attr.pyi] -[typing fixtures/typing-medium.pyi] [case testEvolveVariants] from typing import Any