Skip to content

Commit

Permalink
Fix issues with attr code. (python#4628)
Browse files Browse the repository at this point in the history
More meesages, no unbound types, better tests.

In response to comments in python#4607
  • Loading branch information
euresti authored and yedpodtrzitko committed Mar 13, 2018
1 parent e27f014 commit 08aeea9
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 33 deletions.
13 changes: 10 additions & 3 deletions mypy/plugins/attrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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

Expand Down
6 changes: 3 additions & 3 deletions test-data/unit/check-attr.test
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down
80 changes: 53 additions & 27 deletions test-data/unit/check-incremental.test
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 08aeea9

Please sign in to comment.