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

bool(obj) does not infer from obj.__bool__() #15523

Open
finite-state-machine opened this issue Jun 26, 2023 · 3 comments
Open

bool(obj) does not infer from obj.__bool__() #15523

finite-state-machine opened this issue Jun 26, 2023 · 3 comments
Labels
bug mypy got something wrong topic-type-narrowing Conditional type narrowing / binder

Comments

@finite-state-machine
Copy link

Bug Report

(Although there are other issues that allude to this problem, I haven't been able to locate an issue which specifically addresses truthiness where __bool__() is defined and @final. The search term "truthy bool" may be helpful for finding related issues.)

Implicit truthiness does not (always?) take an object's __bool__() into account, even when that method is @final.

To Reproduce

This example is posted to mypy-play.net.

from typing import *

@final
class SomeClass:
    def __bool__(self) -> Literal[True]:
        return True

instance: SomeClass

reveal_type(instance.__bool__())    # 'Literal[True]' - correct
reveal_type(bool(instance))         # 'bool' - incorrect (should be 'Literal[True]')

if instance:
    reveal_type(instance)           # 'SomeClass' - correct
else:
    assert_never(instance)          # ...'SomeClass' - incorrect (should be silent)

(Whether the class or method is decorated @final makes no difference.)

Expected Behavior

if instance: ... and bool(instnace) should take into account that SomeClass has a @final __bool__() with return type Literal[True], and consistently treat instances of SomeClass as definitely truthy.

In general, implicit truthiness for an object should take into account the truthiness of the return type of any @final __bool__() the object's class defines.

Comments inline in the above code highlight differences from expected behaviour.

Actual Behavior

main.py:10: note: Revealed type is "Literal[True]"
main.py:11: note: Revealed type is "builtins.bool"
main.py:14: note: Revealed type is "__main__.SomeClass"
main.py:16: error: Argument 1 to "assert_never" has incompatible type "SomeClass"; expected "NoReturn"  [arg-type]
Found 1 error in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.4.0, master (2023-06-26)
  • Mypy command-line flags: (none)
  • Mypy configuration options from mypy.ini (and other config files): (none)
  • Python version used: 3.11
@finite-state-machine finite-state-machine added the bug mypy got something wrong label Jun 26, 2023
@AlexWaygood AlexWaygood added the topic-type-narrowing Conditional type narrowing / binder label Jun 26, 2023
@HexDecimal
Copy link
Contributor

HexDecimal commented Jul 1, 2023

I think this is a duplicate of #7008, or at least that issue was kept open because of this specific problem.

Because of the Liskov Substitution Principle a narrowed return type can't be unnarrowed by a subclass so @final isn't necessary for this example.

@finite-state-machine
Copy link
Author

In case it matters for triage: #7008 has been closed without solving this issue.

(I can confirm the issue is reproducible on the master branch as of this date.)

@m1sf17
Copy link

m1sf17 commented Jan 16, 2024

A similar situation where this issue is prevalent is where the dunder method is known to always raise an exception. Pandas logic and its corresponding issue is an example where this exists.

Both of the below examples are not identified (example):

from typing import *

@final
class NeverBoolClass:
    def __bool__(self) -> NoReturn:
        raise NotImplementedError()

@final
class NeverLenClass:
    def __len__(self) -> NoReturn:
        raise NotImplementedError()

I did notice that #10666 previously added similar checks but I'm not confident that area would be the correct one for addressing this issue. If anyone can provide some guidance as to if that is a good place to implement a fix or if it would be better suited somewhere else, I'd be happy to take a stab at the PR.

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

4 participants