Skip to content

Commit

Permalink
Special case assignment to local variable '_': always infer 'Any' (#…
Browse files Browse the repository at this point in the history
…4833)

Fixes #465
  • Loading branch information
gvanrossum committed Apr 16, 2018
1 parent 6a8460f commit 264dca7
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 0 deletions.
4 changes: 4 additions & 0 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1826,6 +1826,10 @@ def analyze_lvalue(self, lval: Lvalue, nested: bool = False,
lval.kind = LDEF
lval.fullname = lval.name
self.add_local(v, lval)
if lval.name == '_':
# Special case for assignment to local named '_': always infer 'Any'.
typ = AnyType(TypeOfAny.special_form)
self.store_declared_types(lval, typ)
elif not self.is_func_scope() and (self.type and
lval.name not in self.type.names):
# Define a new attribute within class body.
Expand Down
1 change: 1 addition & 0 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ class TypeOfAny(Enum):
from_error = 'from_error'
# Is this a type that can't be represented in mypy's type system? For instance, type of
# call to NewType...). Even though these types aren't real Anys, we treat them as such.
# Also used for variables named '_'.
special_form = 'special_form'
# Does this Any come from interaction with another Any?
from_another_any = 'from_another_any'
Expand Down
115 changes: 115 additions & 0 deletions test-data/unit/check-inference.test
Original file line number Diff line number Diff line change
Expand Up @@ -2303,3 +2303,118 @@ class C:
def f(self, x) -> None:
# TODO: It would be better for the type to be Any here
self.a.y # E: "None" has no attribute "y"

-- Special case for assignment to '_'
-- ----------------------------------

[case testUnusedTargetLocal]
def foo() -> None:
_ = 0
_ = ''

[case testUnusedTargetNotGlobal]
_ = 0
_ = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int")

[case testUnusedTargetNotClass]
class C:
_ = 0
_ = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int")

[case testUnusedTargetTupleUnpacking]
def foo() -> None:
_, _ = (0, '')
_ = 0
_ = ''
def bar() -> None:
t = (0, '')
_, _ = t
_ = 0
_ = ''

[case testUnusedTargetMultipleTargets]
def foo() -> None:
_ = x = 0
_ = y = ''
_ = 0
_ = ''
def bar() -> None:
x = _ = 0
y = _ = ''
_ = 0
_ = ''
x + 0
y + ''
x + '' # E: Unsupported operand types for + ("int" and "str")
y + 0 # E: Unsupported operand types for + ("str" and "int")

[case testUnusedTargetNotImport]
import d, c, b, a
[file _.py]
def f(): pass
[file m.py]
def f(): pass
_ = f
_ = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[[], Any]")
[file a.py]
def foo() -> None:
import _
_.f()
_ = 0 # E: Incompatible types in assignment (expression has type "int", variable has type Module)
[file b.py]
def foo() -> None:
import m as _
_.f()
_ = 0 # E: Incompatible types in assignment (expression has type "int", variable has type Module)
[file c.py]
def foo() -> None:
from m import _
_()
_ = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[[], Any]")
[file d.py]
def foo() -> None:
from m import f as _
_()
_ = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[[], Any]")
[builtins fixtures/module.pyi]

[case testUnusedTargetNotClass]
def foo() -> None:
class _:
pass
_().method() # E: "_" has no attribute "method"

[case testUnusedTargetNotDef]
def foo() -> None:
def _() -> int:
pass
_() + '' # E: Unsupported operand types for + ("int" and "str")

[case testUnusedTargetForLoop]
def f() -> None:
a = [(0, '', 0)]
for _, _, x in a:
x = 0
x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int")
_ = 0
_ = ''
[builtins fixtures/list.pyi]

[case testUnusedTargetWithClause]
class C:
def __enter__(self) -> int: pass
def __exit__(self, *args): pass
def f() -> None:
with C() as _: pass
_ = 0
_ = ''

[case testUnusedTargetNotExceptClause]
# Things don't work for except clauses.
# This is due to the implementation, but it's just as well.
def f() -> None:
try: pass
except BaseException as _:
_ = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "BaseException")
_ = '' # E: Incompatible types in assignment (expression has type "str", variable has type "BaseException")
[builtins fixtures/exception.pyi]

0 comments on commit 264dca7

Please sign in to comment.