Skip to content
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

Inference in the else block of a try-except-else #14527

Open
rlazarus opened this issue Jan 26, 2023 · 2 comments
Open

Inference in the else block of a try-except-else #14527

rlazarus opened this issue Jan 26, 2023 · 2 comments
Labels
bug mypy got something wrong topic-type-narrowing Conditional type narrowing / binder

Comments

@rlazarus
Copy link

Bug Report

In the else branch of a try-except-else, mypy ought to be able to infer that no except branch was taken.

To Reproduce

https://mypy-play.net/?mypy=latest&python=3.11&gist=306f5b87e1b2eadc3f7692718a21f6b5

import random


def maybe_int() -> int:
    if random.choice([True, False]):
        return 42
    else:
        raise ValueError


def main() -> None:
    try:
        x = maybe_int()
    except ValueError:
        x = None
    else:
        x += 1  # mypy error occurs here

Expected Behavior

At the last line of the example, I expected mypy to infer that the except branch hadn't been taken, and therefore x was of type int, just as it is at the end of the try branch.

Actual Behavior

test.py:17: error: Unsupported operand types for + ("None" and "int")  [operator]
test.py:17: note: Left operand is of type "Optional[int]"

Your Environment

  • Mypy version used: 0.991 (compiled: yes)
  • Mypy command-line flags: none
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: 3.10.7
@rlazarus rlazarus added the bug mypy got something wrong label Jan 26, 2023
@rlazarus
Copy link
Author

just as it is at the end of the try branch

Actually, that led me to try this variation on the above, with a line inserted at the end of the try:

https://mypy-play.net/?mypy=latest&python=3.11&gist=ad178be339fc35221f03e69826844754

#[...]
    try:
        x = maybe_int()
        x += 1  # With this line added here...
    except ValueError:
        x = None
    else:
        x += 1  # ...there's no longer a mypy error on this line!

I wanted to double-check my intuition that x += 1 should be valid at the end of the try block (where x should be of type int). Of course, it is -- but not only that, it also causes the inference in the else block to work as I'd expected. I can't immediately see why that would be.

@AlexWaygood AlexWaygood added the topic-type-narrowing Conditional type narrowing / binder label Jan 26, 2023
@fayyazul-centaurlabs
Copy link

throwing my hat in the ring here as well.

class Foo:
    x = 1


def main() -> None:
    try:
        foo = Foo() 
        reveal_type(foo) # note: Revealed type is "Foo"
        
    except:
        foo = None 
        reveal_type(foo) # note: Revealed type is "None"
    
    else:
        reveal_type(foo) # note: Revealed type is "Union[Foo, None]"
        print(foo.x)     # error: Item "None" of "Optional[Foo]" has no attribute "x"

this is with mypy version 1.8.0 on python 3.9.18

what's weird is that the assignment in the except clause is only allowed if it is None. If, for example, it was

except:
    foo = 1

then mypy throws Incompatible types in assignment (expression has type "int", variable has type "Foo")
which I guess could be a valid error here. Though, since the try(+else) and except clauses are mutually exclusive I'd argue it's fine to allow assignment with different types (and it already kinda does allow that by allowing you to set foo to Foo() in the try clause and then None in the except clause but I'm guessing there's some special handling for None.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-type-narrowing Conditional type narrowing / binder
Projects
None yet
Development

No branches or pull requests

3 participants