python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.57k stars 2.84k forks source link

Variable of type `Any` narrows to any arbitrary type using `TypeVar` #18112

Closed stephenskett closed 3 weeks ago

stephenskett commented 3 weeks ago

Bug Report

N.B. I think this bug may be related to Issue #12290, but thought I'd report it anyway, in case it is considered sufficiently different to be worth investigating separately.

It appears to be possible to arbitrarily "override" the type of an untyped variable (i.e. type Any) to any concrete type via a function annotated using a TypeVar, even if the type inside the function scope is not narrowed at all (as per the example doesnt_actually_check_anything function below). [Obviously not common to write a function that doesn't do anything except override a type; but common-sense dictates that MyPy should be able to detect this!]

To Reproduce

from typing import TypeVar, Type, Any

T = TypeVar("T")

class A:
    foo: str = "foo"

class B:
    bar: str = "bar"

def doesnt_actually_check_anything(obj: Any, expd_type: Type[T]) -> T:
    # N.B. Could equally be a check condition that doesn't achieve
    # proper type-narrowing in this scope -- the result is the same.
    return obj

def do_something_with_an_A(obj: A) -> None:
    print(obj.foo)

b = B()
b_but_narrowed_to_A = doesnt_actually_check_anything(b, A)

# No MyPy error, but will raise an exception at runtime
print(b_but_narrowed_to_A.foo)

# Triggers MyPy error, but actually ok
#print(b_but_narrowed_to_A.bar)

# No MyPy error, but will raise an exception at runtime
do_something_with_an_A(b_but_narrowed_to_A)  

Expected Behavior

MyPy detects non-narrowed type on l.14 and produces e.g. return-value error.

Actual Behavior

MyPy passes. If l.26 is uncommented, an attr-defined error is detected.

Your Environment

JelleZijlstra commented 3 weeks ago

If --warn-return-any (part of --strict) is given, this errors: issue18112.py:14: error: Returning Any from function declared to return "T" [no-any-return].

If those strict flags are not given, mypy is correct to accept this code. Using Any is unsafe; that's what Any is for.

stephenskett commented 3 weeks ago

Oh, whoops! Should've spotted that. Thanks for clarifying, and keep up the great work! 🙌