Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Attribute of type A | B | None becomes object after narrowing checks #12009

Closed
bluetech opened this issue Jan 18, 2022 · 3 comments
Closed

Attribute of type A | B | None becomes object after narrowing checks #12009

bluetech opened this issue Jan 18, 2022 · 3 comments
Labels
bug mypy got something wrong topic-join-v-union Using join vs. using unions

Comments

@bluetech
Copy link
Contributor

bluetech commented Jan 18, 2022

Bug Report

When doing narrowing of a class attribute with a particular type shape A | B | None, the type of the attribute becomes object after the narrowing condition chain.

To Reproduce

The following is the most minimal reproduction I was able to create.

class A: pass
class B: pass
class C:
    attr: A | B | None

c = C()
if c.attr is None: pass
elif isinstance(c.attr, A): pass
elif isinstance(c.attr, B): pass  # Optional line - no difference if removed
reveal_type(c.attr)

Expected Behavior

Revealed type is A | B | None.

Actual Behavior

Revealed type is object.

I made some additional tests:

  • pyright gives A | B | None (good).
  • Doing x = c.x and then doing the narrowing and reveal_type on attr gives A | B | None (good).
  • Replacing the None with A | B | D gives A | B | D (good). Also if I replace if isinstance(c.attr, D) with if type(c.attr) is D (good). So the None seems essential.
  • With just attr: A | None, gives A | None (good). So more than one type besides the None seems essential.
  • Changing the elif -> if gives A | B | None (good). So the elif seems essential.

Your Environment

  • Mypy version used: master, 0.931, 0.900 (didn't try earlier versions).
  • Mypy command-line flags: --strict.
  • Mypy configuration options from mypy.ini (and other config files):
  • Python version used: 3.9, 3.10
  • Operating system and version: Arch Linux
@bluetech bluetech added the bug mypy got something wrong label Jan 18, 2022
@erictraut
Copy link

This is yet another case where mypy's use of join rather than union produces undesirable results.

Here are some of the other bugs that would be fixed if this change were made:

#11934
#11440
#11618
#10740
#10442
#7884
#7835
#7616
#6968
#6079
#5512
#5128
#4134
#3339

I'm sorry to sound like a broken record, but I really think there should be a serious discussion about moving from join to union. Pyright uses only union, never join, and it avoids all of these issues.

@JelleZijlstra
Copy link
Member

I agree with Eric that we should strongly consider moving away from join. I am going to open a new issue to propose that.

@brianschubert
Copy link
Collaborator

Fixed by #18138, which should be released with mypy v1.14.0. The revealed type is now Union[SCRATCH.A, SCRATCH.B, None] as expected.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
bug mypy got something wrong topic-join-v-union Using join vs. using unions
Projects
None yet
Development

No branches or pull requests

5 participants