mypy icon indicating copy to clipboard operation
mypy copied to clipboard

`assert_type()` says an int is not an int

Open jwodder opened this issue 3 years ago • 2 comments

The following code:

from typing_extensions import assert_type

assert_type(42, int)

fails to typecheck with the following message:

assert-type01.py:3: error: Expression is of type "int", not "int"

Your Environment

  • Mypy version used: both 0.960 and commit 1636a0549670e1b75e2987c16ef26ebf91dfbde9
  • Mypy command-line flags: none
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: 3.9.13
  • Operating system and version: macOS 11.6.6

jwodder avatar Jun 01 '22 21:06 jwodder

Lol, it's probably Literal[42]. So bad error message, but there should probably still be an error? Jelle will confirm.

hauntsaninja avatar Jun 02 '22 06:06 hauntsaninja

Yeah, pyright also gives an error error: "assert_type" mismatch: expected "int" but received "Literal[42]"

hauntsaninja avatar Jun 02 '22 06:06 hauntsaninja

Implementation hints: 42 has type Instance with the last_known_value attribute set to the literal type. If the string representations of types in the assert_type error are the same, we can perhaps try again while using last_known_value if it exists.

JukkaL avatar Apr 23 '23 14:04 JukkaL

Looks like I self-assigned this but then forgot about it. I'm happy to leave this to a sprint contributor.

As the author of assert_type(), I think it's reasonable for a type checker to fail on assert_type(42, int) if it infers 42 to be of type Literal[42]. assert_type() is meant to check that the types are exactly the same.

mypy's current behavior is actually different from what this issue reports:

from typing_extensions import assert_type, Literal

assert_type(42, int)  # passes
assert_type(42, Literal[42]) # also passes

I think this behavior is wrong: exactly one of these two should succeed, but not both. Which of the two is up to the type checker to decide. For both pyanalyze and pyright, 42 is inferred as type Literal[42] and therefore the first assert_type fails. Mypy could choose to instead infer 42 as being of type int, which would make the second assert fail instead.

JelleZijlstra avatar Apr 23 '23 14:04 JelleZijlstra

(venv) carl@x1:~/src/mypy$ python3 runtests.py self
run self: ['/home/carl/src/mypy/venv/bin/python3', '-m', 'mypy', '--config-file', 'mypy_self_check.ini', '-p', 'mypy']
mypy/checkexpr.py:3924: error: Never apply isinstance() to unexpanded types; use mypy.types.get_proper_type() first  [misc]
            if isinstance( source_type, mypy.types.Instance ) and not is_same_type(source_type.last_known_value, target_t...
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mypy/checkexpr.py:3924: note: If you pass on the original type after the check, always use its unexpanded version
mypy/checkexpr.py:3924: error: Argument 1 to "is_same_type" has incompatible type "Optional[LiteralType]"; expected "Type" 
[arg-type]
            if isinstance( source_type, mypy.types.Instance ) and not is_same_type(source_type.last_known_value, target_t...
                                                                                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
mypy/checkexpr.py:3930: error: Argument 1 to "assert_type_fail" of "MessageBuilder" has incompatible type
"Optional[LiteralType]"; expected "Type"  [arg-type]
                self.msg.assert_type_fail(source_type.last_known_value, target_type, expr)
                                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
Found 3 errors in 1 file (checked 170 source files)

working on it...

CarlFK avatar Apr 25 '23 18:04 CarlFK

(venv) carl@x1:~/src/ck/mypy$ python3 runtests.py self 
run self: ['/home/carl/src/mypy/venv/bin/python3', '-m', 'mypy', '--config-file', 'mypy_self_check.ini', '-p', 'mypy']
mypy/checkexpr.py:3923: error: "Type" has no attribute "last_known_value"  [attr-defined]
                    and source_type.last_known_value is not None:
                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ and (hasattr(source_type, "last_known_value") and source_type.last_known_value is not None):

Success: no issues found in 170 source files

CarlFK avatar Apr 25 '23 18:04 CarlFK