diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index f2385f87de0f..aa1696ffeda7 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -64,11 +64,13 @@ def argument(self, ctx: 'mypy.plugin.ClassDefContext') -> Argument: and converter.type and isinstance(converter.type, CallableType) and converter.type.arg_types): - init_type = converter.type.arg_types[0] + init_type = ctx.api.anal_type(converter.type.arg_types[0]) else: + ctx.api.fail("Cannot determine type of converter function", self.context) init_type = AnyType(TypeOfAny.from_error) elif self.converter_name == '': # This means we had a converter but it's not of a type we can infer. + # Error was shown in _get_converter_name init_type = AnyType(TypeOfAny.from_error) if init_type is None: @@ -317,12 +319,13 @@ def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext', elif convert: ctx.api.fail("convert is deprecated, use converter", rvalue) converter = convert - converter_name = _get_converter_name(converter) + converter_name = _get_converter_name(ctx, converter) return Attribute(lhs.name, ctx.cls.info, attr_has_default, init, converter_name, stmt) -def _get_converter_name(converter: Optional[Expression]) -> Optional[str]: +def _get_converter_name(ctx: 'mypy.plugin.ClassDefContext', + converter: Optional[Expression]) -> Optional[str]: """Return the full name of the converter if it exists and is a simple function.""" # TODO: Support complex converters, e.g. lambdas, calls, etc. if converter: @@ -334,6 +337,10 @@ def _get_converter_name(converter: Optional[Expression]) -> Optional[str]: and converter.node.type.arg_types): return converter.node.fullname() # Signal that we have an unsupported converter. + ctx.api.fail( + "Unsupported converter function, only named functions are currently supported", + converter + ) return '' return None diff --git a/test-data/unit/check-attr.test b/test-data/unit/check-attr.test index c0547b28920f..678facb7b3d6 100644 --- a/test-data/unit/check-attr.test +++ b/test-data/unit/check-attr.test @@ -542,9 +542,9 @@ def factory(default: int): ... @attr.s class C: - x: str = attr.ib(converter=thing.do_it) - y: str = attr.ib(converter=lambda x: x) - z: str = attr.ib(converter=factory(8)) + x: str = attr.ib(converter=thing.do_it) # E: Unsupported converter function, only named functions are currently supported + y: str = attr.ib(converter=lambda x: x) # E: Unsupported converter function, only named functions are currently supported + z: str = attr.ib(converter=factory(8)) # E: Unsupported converter function, only named functions are currently supported reveal_type(C) # E: Revealed type is 'def (x: Any, y: Any, z: Any) -> __main__.C' [builtins fixtures/list.pyi] diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 20ab89ef35c0..599722e993f9 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -3766,19 +3766,53 @@ main:2: error: Revealed type is 'def (x: Union[builtins.int, builtins.None]) -> main:2: error: Revealed type is 'def (x: Union[builtins.int, builtins.None]) -> a.a.A' [case testAttrsIncrementalConverterManyStyles] +import a +[file a.py] from base import Base -reveal_type(Base) +Base(1, 'str', True) +Base(None, None, None) + from subclass import A, B -reveal_type(A) -reveal_type(B) +A(1, 'str', True) +A(None, None, None) +B(1, 'str', True, 1, 'str', True) +B(None, None, None, None, None, None) + +from submodule.base import SubBase +SubBase(1, 'str', True) +SubBase(None, None, None) + +from submodule.subclass import AA, BB +AA(1, 'str', True) +AA(None, None, None) +BB(1, 'str', True, 1, 'str', True) +BB(None, None, None, None, None, None) + +from submodule.subsubclass import SubAA, SubBB +SubAA(1, 'str', True) +SubAA(None, None, None) +SubBB(1, 'str', True, 1, 'str', True) +SubBB(None, None, None, None, None, None) + +[file a.py.2] +# Now with errors. +from base import Base +Base(1, 1, True) + +from subclass import A, B +A(1, 1, True) +B(1, 'str', True, 1, 1, True) + from submodule.base import SubBase -reveal_type(SubBase) +SubBase(1, 1, True) + from submodule.subclass import AA, BB -reveal_type(AA) -reveal_type(BB) +AA(1, 1, True) +BB(1, 'str', True, 1, 1, True) + from submodule.subsubclass import SubAA, SubBB -reveal_type(SubAA) -reveal_type(SubBB) +SubAA(1, 1, True) +SubBB(1, 'str', True, 1, 1, True) [file foo.py] from typing import Optional @@ -3866,23 +3900,15 @@ class SubBB(SubBase): zz: bool = attr.ib(converter=bar.maybe_bool) [builtins fixtures/list.pyi] [out1] -main:2: error: Revealed type is 'def (x: Union[builtins.int, builtins.None], y: Union[builtins.str, builtins.None], z: Union[builtins.bool, builtins.None]) -> base.Base' -main:4: error: Revealed type is 'def (x: Union[builtins.int, builtins.None], y: Union[builtins.str, builtins.None], z: Union[builtins.bool, builtins.None]) -> subclass.A' -main:5: error: Revealed type is 'def (x: Union[builtins.int, builtins.None], y: Union[builtins.str, builtins.None], z: Union[builtins.bool, builtins.None], xx: Union[builtins.int, builtins.None], yy: Union[builtins.str, builtins.None], zz: Union[builtins.bool, builtins.None]) -> subclass.B' -main:7: error: Revealed type is 'def (x: Union[builtins.int, builtins.None], y: Union[builtins.str, builtins.None], z: Union[builtins.bool, builtins.None]) -> submodule.base.SubBase' -main:9: error: Revealed type is 'def (x: Union[builtins.int, builtins.None], y: Union[builtins.str, builtins.None], z: Union[builtins.bool, builtins.None]) -> submodule.subclass.AA' -main:10: error: Revealed type is 'def (x: Union[builtins.int, builtins.None], y: Union[builtins.str, builtins.None], z: Union[builtins.bool, builtins.None], xx: Union[builtins.int, builtins.None], yy: Union[builtins.str, builtins.None], zz: Union[builtins.bool, builtins.None]) -> submodule.subclass.BB' -main:12: error: Revealed type is 'def (x: Union[builtins.int, builtins.None], y: Union[builtins.str, builtins.None], z: Union[builtins.bool, builtins.None]) -> submodule.subsubclass.SubAA' -main:13: error: Revealed type is 'def (x: Union[builtins.int, builtins.None], y: Union[builtins.str, builtins.None], z: Union[builtins.bool, builtins.None], xx: Union[builtins.int, builtins.None], yy: Union[builtins.str, builtins.None], zz: Union[builtins.bool, builtins.None]) -> submodule.subsubclass.SubBB' -[out2] -main:2: error: Revealed type is 'def (x: Union[builtins.int, builtins.None], y: Union[builtins.str, builtins.None], z: Union[builtins.bool, builtins.None]) -> base.Base' -main:4: error: Revealed type is 'def (x: Union[builtins.int, builtins.None], y: Union[builtins.str, builtins.None], z: Union[builtins.bool, builtins.None]) -> subclass.A' -main:5: error: Revealed type is 'def (x: Union[builtins.int, builtins.None], y: Union[builtins.str, builtins.None], z: Union[builtins.bool, builtins.None], xx: Union[builtins.int, builtins.None], yy: Union[builtins.str, builtins.None], zz: Union[builtins.bool, builtins.None]) -> subclass.B' -main:7: error: Revealed type is 'def (x: Union[builtins.int, builtins.None], y: Union[builtins.str, builtins.None], z: Union[builtins.bool, builtins.None]) -> submodule.base.SubBase' -main:9: error: Revealed type is 'def (x: Union[builtins.int, builtins.None], y: Union[builtins.str, builtins.None], z: Union[builtins.bool, builtins.None]) -> submodule.subclass.AA' -main:10: error: Revealed type is 'def (x: Union[builtins.int, builtins.None], y: Union[builtins.str, builtins.None], z: Union[builtins.bool, builtins.None], xx: Union[builtins.int, builtins.None], yy: Union[builtins.str, builtins.None], zz: Union[builtins.bool, builtins.None]) -> submodule.subclass.BB' -main:12: error: Revealed type is 'def (x: Union[builtins.int, builtins.None], y: Union[builtins.str, builtins.None], z: Union[builtins.bool, builtins.None]) -> submodule.subsubclass.SubAA' -main:13: error: Revealed type is 'def (x: Union[builtins.int, builtins.None], y: Union[builtins.str, builtins.None], z: Union[builtins.bool, builtins.None], xx: Union[builtins.int, builtins.None], yy: Union[builtins.str, builtins.None], zz: Union[builtins.bool, builtins.None]) -> submodule.subsubclass.SubBB' +[out2] +tmp/a.py:3: error: Argument 2 to "Base" has incompatible type "int"; expected "Optional[str]" +tmp/a.py:6: error: Argument 2 to "A" has incompatible type "int"; expected "Optional[str]" +tmp/a.py:7: error: Argument 5 to "B" has incompatible type "int"; expected "Optional[str]" +tmp/a.py:10: error: Argument 2 to "SubBase" has incompatible type "int"; expected "Optional[str]" +tmp/a.py:13: error: Argument 2 to "AA" has incompatible type "int"; expected "Optional[str]" +tmp/a.py:14: error: Argument 5 to "BB" has incompatible type "int"; expected "Optional[str]" +tmp/a.py:17: error: Argument 2 to "SubAA" has incompatible type "int"; expected "Optional[str]" +tmp/a.py:18: error: Argument 5 to "SubBB" has incompatible type "int"; expected "Optional[str]" [case testAttrsIncrementalConverterInFunction] import attr @@ -3895,9 +3921,9 @@ def foo() -> None: reveal_type(A) [builtins fixtures/list.pyi] [out1] -main:8: error: Revealed type is 'def (x: str?) -> __main__.A@5' +main:8: error: Revealed type is 'def (x: builtins.str) -> __main__.A@5' [out2] -main:8: error: Revealed type is 'def (x: str?) -> __main__.A@5' +main:8: error: Revealed type is 'def (x: builtins.str) -> __main__.A@5' [case testAttrsIncrementalConverterInSubmoduleForwardRef] from a.a import A