Skip to content

Special case assignment to '_': always infer 'Any' #4833

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 9 commits into from
Apr 16, 2018
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
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]