Skip to content

Update attrs plugin to work with new semantic analyzer #6435

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
ComparisonExpr, StarExpr, EllipsisExpr, RefExpr, PromoteExpr,
Import, ImportFrom, ImportAll, ImportBase, TypeAlias,
ARG_POS, ARG_STAR, LITERAL_TYPE, MDEF, GDEF,
CONTRAVARIANT, COVARIANT, INVARIANT,
CONTRAVARIANT, COVARIANT, INVARIANT, TypeVarExpr
)
from mypy import nodes
from mypy.literals import literal, literal_hash
Expand Down Expand Up @@ -1614,6 +1614,10 @@ def determine_type_of_class_member(self, sym: SymbolTableNode) -> Optional[Type]
if isinstance(sym.node, TypeInfo):
# nested class
return type_object_type(sym.node, self.named_type)
if isinstance(sym.node, TypeVarExpr):
# Use of TypeVars is rejected in an expression/runtime context, so
# we don't need to check supertype compatibility for them.
return AnyType(TypeOfAny.special_form)
return None

def check_compatibility(self, name: str, base1: TypeInfo,
Expand Down
14 changes: 11 additions & 3 deletions mypy/plugins/attrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
Context, Argument, Var, ARG_OPT, ARG_POS, TypeInfo, AssignmentStmt,
TupleExpr, ListExpr, NameExpr, CallExpr, RefExpr, FuncBase,
is_class_var, TempNode, Decorator, MemberExpr, Expression,
SymbolTableNode, MDEF, JsonDict, OverloadedFuncDef, ARG_NAMED_OPT, ARG_NAMED
SymbolTableNode, MDEF, JsonDict, OverloadedFuncDef, ARG_NAMED_OPT, ARG_NAMED,
TypeVarExpr
)
from mypy.plugins.common import (
_get_argument, _get_bool_argument, _get_decorator_bool_argument, add_method
Expand Down Expand Up @@ -43,6 +44,8 @@
'attr.attr',
} # type: Final

SELF_TVAR_NAME = '_AT' # type: Final


class Converter:
"""Holds information about a `converter=` argument"""
Expand Down Expand Up @@ -112,7 +115,7 @@ def argument(self, ctx: 'mypy.plugin.ClassDefContext') -> Argument:
if self.converter.is_attr_converters_optional and init_type:
# If the converter was attr.converter.optional(type) then add None to
# the allowed init_type.
init_type = UnionType.make_simplified_union([init_type, NoneTyp()])
init_type = UnionType.make_union([init_type, NoneTyp()])

if not init_type:
ctx.api.fail("Cannot determine __init__ type from converter", self.context)
Expand Down Expand Up @@ -512,8 +515,13 @@ def _add_cmp(ctx: 'mypy.plugin.ClassDefContext', adder: 'MethodAdder') -> None:
# AT = TypeVar('AT')
# def __lt__(self: AT, other: AT) -> bool
# This way comparisons with subclasses will work correctly.
tvd = TypeVarDef('AT', 'AT', -1, [], object_type)
tvd = TypeVarDef(SELF_TVAR_NAME, ctx.cls.info.fullname() + '.' + SELF_TVAR_NAME,
-1, [], object_type)
tvd_type = TypeVarType(tvd)
self_tvar_expr = TypeVarExpr(SELF_TVAR_NAME, ctx.cls.info.fullname() + '.' + SELF_TVAR_NAME,
[], object_type)
ctx.cls.info.names[SELF_TVAR_NAME] = SymbolTableNode(MDEF, self_tvar_expr)

args = [Argument(Var('other', tvd_type), tvd_type, None, ARG_POS)]
for method in ['__lt__', '__le__', '__gt__', '__ge__']:
adder.add_method(method, args, bool_type, self_type=tvd_type, tvd=tvd)
Expand Down
1 change: 0 additions & 1 deletion mypy/test/hacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
# Files to not run with new semantic analyzer.
new_semanal_blacklist = [
'check-async-await.test',
'check-attr.test',
'check-bound.test',
'check-callable.test',
'check-classes.test',
Expand Down
27 changes: 15 additions & 12 deletions test-data/unit/check-attr.test
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,10 @@ class A:
reveal_type(A) # E: Revealed type is 'def (a: builtins.int) -> __main__.A'
reveal_type(A.__eq__) # E: Revealed type is 'def (self: __main__.A, other: builtins.object) -> builtins.bool'
reveal_type(A.__ne__) # E: Revealed type is 'def (self: __main__.A, other: builtins.object) -> builtins.bool'
reveal_type(A.__lt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
reveal_type(A.__le__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
reveal_type(A.__gt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
reveal_type(A.__ge__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
reveal_type(A.__lt__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
reveal_type(A.__le__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
reveal_type(A.__gt__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
reveal_type(A.__ge__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'

A(1) < A(2)
A(1) <= A(2)
Expand Down Expand Up @@ -577,7 +577,10 @@ class C:

[builtins fixtures/list.pyi]

-- This is tricky with new analyzer:
-- We need to know the analyzed type of a function while still processing top-level.
[case testAttrsUsingBadConverter]
# flags: --no-new-semantic-analyzer --no-strict-optional
import attr
from typing import overload
@overload
Expand All @@ -596,11 +599,11 @@ class A:
bad_overloaded: int = attr.ib(converter=bad_overloaded_converter)
reveal_type(A)
[out]
main:15: error: Cannot determine __init__ type from converter
main:15: error: Argument "converter" has incompatible type "Callable[[], str]"; expected "Callable[[Any], str]"
main:16: error: Cannot determine __init__ type from converter
main:16: error: Argument "converter" has incompatible type overloaded function; expected "Callable[[Any], int]"
main:17: error: Revealed type is 'def (bad: Any, bad_overloaded: Any) -> __main__.A'
main:16: error: Argument "converter" has incompatible type "Callable[[], str]"; expected "Callable[[Any], str]"
main:17: error: Cannot determine __init__ type from converter
main:17: error: Argument "converter" has incompatible type overloaded function; expected "Callable[[Any], int]"
main:18: error: Revealed type is 'def (bad: Any, bad_overloaded: Any) -> __main__.A'
[builtins fixtures/list.pyi]

[case testAttrsUsingUnsupportedConverter]
Expand Down Expand Up @@ -667,10 +670,10 @@ class C(A, B): pass
@attr.s
class D(A): pass

reveal_type(A.__lt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
reveal_type(B.__lt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
reveal_type(C.__lt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
reveal_type(D.__lt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
reveal_type(A.__lt__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
reveal_type(B.__lt__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
reveal_type(C.__lt__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
reveal_type(D.__lt__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'

A() < A()
B() < B()
Expand Down
16 changes: 8 additions & 8 deletions test-data/unit/check-incremental.test
Original file line number Diff line number Diff line change
Expand Up @@ -2951,10 +2951,10 @@ from a import A
reveal_type(A) # E: Revealed type is 'def (a: builtins.int) -> a.A'
reveal_type(A.__eq__) # E: Revealed type is 'def (self: a.A, other: builtins.object) -> builtins.bool'
reveal_type(A.__ne__) # E: Revealed type is 'def (self: a.A, other: builtins.object) -> builtins.bool'
reveal_type(A.__lt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
reveal_type(A.__le__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
reveal_type(A.__gt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
reveal_type(A.__ge__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
reveal_type(A.__lt__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
reveal_type(A.__le__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
reveal_type(A.__gt__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
reveal_type(A.__ge__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'

A(1) < A(2)
A(1) <= A(2)
Expand Down Expand Up @@ -2990,10 +2990,10 @@ class A:
main:2: error: Revealed type is 'def (a: builtins.int) -> a.A'
main:3: error: Revealed type is 'def (self: a.A, other: builtins.object) -> builtins.bool'
main:4: error: Revealed type is 'def (self: a.A, other: builtins.object) -> builtins.bool'
main:5: error: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
main:6: error: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
main:7: error: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
main:8: error: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
main:5: error: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
main:6: error: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
main:7: error: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
main:8: error: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
main:17: error: Unsupported operand types for < ("A" and "int")
main:18: error: Unsupported operand types for <= ("A" and "int")
main:19: error: Unsupported operand types for > ("A" and "int")
Expand Down