python / python/mypy

Mypy emits two conflicting results for reveal_type

Open
#10,739 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug topic-reveal-type topic-union-types
Dominant language
Python
Stars
20.6k
Forks
3.3k
PR merge metrics
PR metrics pending

Description

Bug Report

My colleague @ahuhn and I encountered a situation where mypy emits two different outputs for one reveal_type call:

from decimal import Decimal
from typing import Union

def f(elem: Union[int, str, Decimal]) -> None:
    if isinstance(elem, int):
        return

    for _ in range(3):
        if isinstance(elem, (Decimal, int)) and elem > 10:
            continue
        reveal_type(elem)

    return None

Running mypy emits two lines for the reveal_type call, one correct and the other incorrect. In the incorrect reveal statement, the type narrowing at the top of the function is lost:

$ mypy example.py
example.py:11: note: Revealed type is "Union[builtins.str, decimal.Decimal]"   # expected
example.py:11: note: Revealed type is "Union[builtins.int, builtins.str, decimal.Decimal]"   # wrong

Expected Behavior
Expected only one piece of output for one reveal_type, and for that output to maintain the type narrowing from the top of the function.

Your Environment

We encountered this on mypy 0.812, but it also repros on latest master (currently 416f57b) using python 3.8.6.

A variation of interest: if you add a line of code after the reveal_type, then the second reveal_type output broadens to include Any:

from decimal import Decimal
from typing import Union

def f(elem: Union[int, str, Decimal]) -> None:
    if isinstance(elem, int):
        return

    for _ in range(3):
        if isinstance(elem, (Decimal, int)) and elem > 10:
            continue
        reveal_type(elem)
        elem += 1   # new compared to the example above

    return None

mypy:

$ mypy example.py
example.py:11: note: Revealed type is "Union[builtins.str, decimal.Decimal]"  # correct
example.py:11: note: Revealed type is "Union[builtins.int, Any, builtins.str, decimal.Decimal]"  # even wider, now including Any
example.py:12: error: Unsupported operand types for + ("str" and "int")   # expected and correct
example.py:12: note: Left operand is of type "Union[str, Decimal]"
example.py:12: note: Left operand is of type "Union[int, Any, str, Decimal]"
Found 1 error in 1 file (checked 1 source file)

(In this example, it's trivial to get around the problem by removing the redundant isinstance(int) check, but in our actual code, it wasn't so straightforward.)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the provided Python reproductions with mypy, including the variant with elem += 1, and compare the duplicate reveal_type outputs. The fix is done when each reveal_type call emits one result that preserves the initial narrowing and the accompanying type-checking diagnostics remain correct.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.