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

Allow booleans to be narrowed to literal types #10389

Merged
merged 8 commits into from
Nov 7, 2021

Conversation

ethan-leba
Copy link
Contributor

@ethan-leba ethan-leba commented Apr 30, 2021

Description

This PR allows boolean types to be narrowed to literals, in the same fashion
that Enums can be narrowed.

Closes #6113, closes #11043

Test Plan

New unit tests included!

@ethan-leba ethan-leba changed the title Boolean narrowing Allow booleans to be narrowed to literal types Apr 30, 2021
@github-actions

This comment has been minimized.

@ethan-leba ethan-leba marked this pull request as ready for review April 30, 2021 13:45
mypy/checker.py Outdated
if isinstance(target, LiteralType) and target.is_enum_literal():
enum_name = target.fallback.type.fullname
if (isinstance(target, LiteralType) and
(target.is_enum_literal() or target.fallback.type.fullname == "builtins.bool")):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

is there a better way to check if the type is a bool than looking for this string?

Copy link
Collaborator

Choose a reason for hiding this comment

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

isinstance(target.value, bool)?

Copy link
Collaborator

@JukkaL JukkaL left a comment

Choose a reason for hiding this comment

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

Thanks for the PR! Left some comments (not a full review).

Note that since we'll hopefully have a release soon, we probably won't merge this PR until after the release branch has been cut, since changes to type inference are a bit risky just before a release.


if bool_val is not False:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should if bool_val and if not bool_val also do narrowing? These can't be overridden for bool.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, that seems useful, i'll look into it 👍

@github-actions

This comment has been minimized.

@JelleZijlstra
Copy link
Member

The homeassistant line 414 error seems like a bug. The code is at https://github.com/home-assistant/core/blob/dev/homeassistant/config_entries.py#L403. There is an assertion that result is a bool, but somehow mypy still thinks it's Any according to the error message.

@ethan-leba

This comment has been minimized.

@ethan-leba
Copy link
Contributor Author

The homeassistant line 414 error seems like a bug. The code is at https://github.com/home-assistant/core/blob/dev/homeassistant/config_entries.py#L403. There is an assertion that result is a bool, but somehow mypy still thinks it's Any according to the error message.

It looks the error is stemming from the type binder here:
https://github.com/python/mypy/blob/master/mypy/binder.py#L201

A (narrowed) Any type which does not have the same type on each side of a
conditional will be converted back to Any. Not sure about the context of this
decision, is this something we want to change?

@ethan-leba ethan-leba requested a review from JukkaL June 9, 2021 13:18
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@ethan-leba
Copy link
Contributor Author

@JukkaL ping

@github-actions

This comment has been minimized.

@ethan-leba
Copy link
Contributor Author

Hi @hauntsaninja, is there any interest in re-reviewing this PR? If so, I can rebase, etc.

@hauntsaninja
Copy link
Collaborator

hauntsaninja commented Sep 29, 2021

Thanks, yes! This PR is great and something I'm very interested in merging (although I would feel more comfortable doing so if @JukkaL had time to okay).

I fixed the broken test and merge conflict, hopefully it's enough to get CI green.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions
Copy link
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

websockets (https://github.com/aaugustin/websockets.git)
+ src/websockets/legacy/protocol.py:1076: error: unused "type: ignore" comment

pandas (https://github.com/pandas-dev/pandas.git)
+ pandas/io/stata.py:1423: error: Incompatible types in assignment (expression has type "Union[str, str]", variable has type "Union[Literal['>'], Literal['<']]")  [assignment]

isort (https://github.com/pycqa/isort.git)
+ isort/api.py:181: error: Redundant cast to "TextIO"
+ isort/api.py:299: error: Redundant cast to "TextIO"
+ isort/api.py:469: error: Redundant cast to "TextIO"

poetry (https://github.com/python-poetry/poetry.git)
- poetry/mixology/failure.py:143: error: Item "RootCause" of "Union[RootCause, NoVersionsCause, DependencyCause, ConflictCause, PythonCause, PlatformCause, PackageNotFoundCause]" has no attribute "other"
- poetry/mixology/failure.py:143: error: Item "NoVersionsCause" of "Union[RootCause, NoVersionsCause, DependencyCause, ConflictCause, PythonCause, PlatformCause, PackageNotFoundCause]" has no attribute "other"
- poetry/mixology/failure.py:143: error: Item "DependencyCause" of "Union[RootCause, NoVersionsCause, DependencyCause, ConflictCause, PythonCause, PlatformCause, PackageNotFoundCause]" has no attribute "other"
- poetry/mixology/failure.py:143: error: Item "PythonCause" of "Union[RootCause, NoVersionsCause, DependencyCause, ConflictCause, PythonCause, PlatformCause, PackageNotFoundCause]" has no attribute "other"
- poetry/mixology/failure.py:143: error: Item "PlatformCause" of "Union[RootCause, NoVersionsCause, DependencyCause, ConflictCause, PythonCause, PlatformCause, PackageNotFoundCause]" has no attribute "other"
- poetry/mixology/failure.py:143: error: Item "PackageNotFoundCause" of "Union[RootCause, NoVersionsCause, DependencyCause, ConflictCause, PythonCause, PlatformCause, PackageNotFoundCause]" has no attribute "other"
- poetry/mixology/failure.py:144: error: Item "RootCause" of "Union[RootCause, NoVersionsCause, DependencyCause, ConflictCause, PythonCause, PlatformCause, PackageNotFoundCause]" has no attribute "conflict"
- poetry/mixology/failure.py:144: error: Item "NoVersionsCause" of "Union[RootCause, NoVersionsCause, DependencyCause, ConflictCause, PythonCause, PlatformCause, PackageNotFoundCause]" has no attribute "conflict"
- poetry/mixology/failure.py:144: note: (Skipping most remaining errors due to unresolved imports or missing stubs; fix these first)
+ poetry/mixology/failure.py:143: note: (Skipping most remaining errors due to unresolved imports or missing stubs; fix these first)

@hauntsaninja
Copy link
Collaborator

Thanks again for this PR, super useful!

@Dreamsorcerer
Copy link
Contributor

This change seems to break basic object-oriented programming (but, could be a wider issue with type narrowing). e.g.

assert not thing.frozen
thing.freeze()
assert thing.frozen  # Mypy: thing.frozen is Literal[False]

I'd suggest disabling this feature for attributes, or if not too complex, reset the type narrowing after a method of that object is called.

tushar-deepsource pushed a commit to DeepSourceCorp/mypy that referenced this pull request Jan 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants