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

Make None compatible with SupportsBool protocol #15889

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion mypy/subtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,9 @@ def visit_none_type(self, left: NoneType) -> bool:
# None is compatible with Hashable (and other similar protocols). This is
# slightly sloppy since we don't check the signature of "__hash__".
# None is also compatible with `SupportsStr` protocol.
return not members or all(member in ("__hash__", "__str__") for member in members)
return not members or all(
member in ("__hash__", "__str__", "__bool__") for member in members
)
return False
else:
return True
Expand Down
18 changes: 17 additions & 1 deletion test-data/unit/check-protocols.test
Original file line number Diff line number Diff line change
Expand Up @@ -2857,7 +2857,7 @@ c1: SupportsClassGetItem = C()

[case testNoneVsProtocol]
# mypy: strict-optional
from typing_extensions import Protocol
from typing_extensions import Literal, Protocol

class MyHashable(Protocol):
def __hash__(self) -> int: ...
Expand Down Expand Up @@ -2890,6 +2890,22 @@ class SupportsStr(Protocol):
def ss(s: SupportsStr) -> None: pass
ss(None)

class SupportsBool(Protocol):
def __bool__(self) -> bool: ...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, add two more protocols to the test: def __bool__(self) -> Literal[True]: ... and def __bool__(self) -> Literal[False]: ...

Other than that - it looks good to me.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for review! I add tests with Literal return types, take a look, please


class SupportsBoolLiteralTrue(Protocol):
def __bool__(self) -> Literal[True]: ...

class SupportsBoolLiteralFalse(Protocol):
def __bool__(self) -> Literal[False]: ...

def sb(s: SupportsBool) -> None: pass
sb(None)
def sblt(s: SupportsBoolLiteralTrue) -> None: pass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you're right, it's not correct behavior, but tests passed. How I can fix it? Maybe we have other bug?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None is quite complicated, because mypy uses pre-types.NoneType logic for None.
Check out checkmember and NoneType handling there.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm implemented check into visit_none_type method, because __bool__ of Protocol not handled by analyze_none_member_access

sblt(None)
def sblf(s: SupportsBoolLiteralFalse) -> None: pass
sblf(None)

class HashableStr(Protocol):
def __str__(self) -> str: ...
def __hash__(self) -> int: ...
Expand Down