Skip to content

Mypy could not do condition inference #19029

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

Closed
kaelzhang opened this issue May 5, 2025 · 5 comments
Closed

Mypy could not do condition inference #19029

kaelzhang opened this issue May 5, 2025 · 5 comments
Labels
feature topic-type-narrowing Conditional type narrowing / binder

Comments

@kaelzhang
Copy link

kaelzhang commented May 5, 2025

Bug Report

To Reproduce

The min sample:

class MyClass:
    a_property: int = 1

    def do_something (
        self,
        source: Optional['MyClass'] = None
    ):
        is_not_none = source is not None

        if is_not_none:
            # My ass even knows that `source` must be a `MyClass` instance here.
            self.a_property = source.a_property

Expected Behavior

It should pass the test

Actual Behavior

Item "None" of "MyClass | None" has no attribute "a_property"  [union-attr]

The only way to avoid this error output is to replace each is_not_none with source is not None. It makes the code silly sometimes, and forces me to revamp the logic just for mypy which is a totally waste of my time.

Your Environment

  • Mypy version used: 1.15.0
  • Mypy command-line flags: mypy foo.py
  • Mypy configuration options from mypy.ini (and other config files): n/a
  • Python version used: 3.12.7
@kaelzhang kaelzhang added the bug mypy got something wrong label May 5, 2025
@JelleZijlstra JelleZijlstra added topic-type-narrowing Conditional type narrowing / binder feature and removed bug mypy got something wrong labels May 5, 2025
@USEONGEE
Copy link

USEONGEE commented May 5, 2025

Hello, I want to solve this problem. Can I?

@JelleZijlstra
Copy link
Member

PRs are welcome but this is likely a complicated feature to add.

Also this issue is probably a duplicate but I haven't looked.

@erictraut
Copy link

I think this is a duplicate of #9229.

This feature is formally called "aliased conditional expressions". The first time I saw it implemented was in the TypeScript compiler in 2021. I later implemented it in pyright.

As Jelle said, it's tricky to get this feature right because a static analyzer must prove that there is no statement that can invalidate the condition on any code path between the assignment of the condition variable and its later use for narrowing.

Consider the following three examples:

def func1(x: int | None):
    is_int = x is not None

    for _ in range(2):
        if is_int: # Safe to narrow x to `int`
            reveal_type(x) # int

def func2(x: int | None):
    is_int = x is not None

    for _ in range(2):
        if is_int: # Not safe to narrow x to `int`
            reveal_type(x) # int | None
        x = None

def func3(x: int | None):
    is_int = x is not None

    for _ in range(2):
        if is_int: # Not safe to narrow x to `int`
            reveal_type(x) # int | None
        is_int = True

@USEONGEE
Copy link

USEONGEE commented May 5, 2025

Thanks

1 similar comment
@kaelzhang
Copy link
Author

Thanks

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

No branches or pull requests

4 participants