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

Implement TypeIs (PEP 742) #16898

Merged
merged 30 commits into from
Mar 1, 2024
Merged

Conversation

JelleZijlstra
Copy link
Member

Cross-ref python/peps#3649.

This comment has been minimized.

This comment has been minimized.

This comment has been minimized.

@JelleZijlstra JelleZijlstra marked this pull request as ready for review February 11, 2024 04:40
return {expr: TypeGuardedType(node.callee.type_guard)}, {}
else:
assert node.callee.type_is is not None
return conditional_types_to_typemaps(
Copy link
Member Author

Choose a reason for hiding this comment

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

This is the crucial part of the change, which implements the new type narrowing behavior. It's the same as isinstance(), a little way up in the same method.

This comment has been minimized.

Copy link
Collaborator

@cdce8p cdce8p left a comment

Choose a reason for hiding this comment

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

Just a small disclaimer, this is the first bigger mypy PR I reviewed 😅

Overall it looks solid. Left a few minor comments.

docs/source/error_code_list2.rst Outdated Show resolved Hide resolved
docs/source/error_code_list2.rst Outdated Show resolved Hide resolved
mypy/checker.py Outdated Show resolved Hide resolved
mypy/message_registry.py Outdated Show resolved Hide resolved
mypy/subtypes.py Outdated Show resolved Hide resolved
test-data/unit/check-typeis.test Outdated Show resolved Hide resolved
test-data/unit/check-typeis.test Outdated Show resolved Hide resolved
test-data/unit/check-typeis.test Outdated Show resolved Hide resolved
test-data/unit/check-typeis.test Outdated Show resolved Hide resolved
Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
@JelleZijlstra
Copy link
Member Author

Thanks for the review! I applied the suggestions, and will deal with the larger issues in the next few days.

This comment has been minimized.

@cdce8p
Copy link
Collaborator

cdce8p commented Feb 23, 2024

Missed commenting on the PR title earlier. Could you update it to mention TypeIs instead?

@JelleZijlstra JelleZijlstra changed the title Implement TypeNarrower (PEP 742) Implement TypeIs (PEP 742) Feb 23, 2024

This comment has been minimized.

mypy/checker.py Outdated Show resolved Hide resolved
test-data/unit/check-typeis.test Outdated Show resolved Hide resolved
Copy link
Member

@sobolevn sobolevn left a comment

Choose a reason for hiding this comment

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

Very impressive work, thank you! 👏

@@ -281,5 +281,11 @@ def __hash__(self) -> int:
sub_code_of=MISC,
)

TYPE_NARROWER_NOT_SUBTYPE: Final[ErrorCode] = ErrorCode(
"type-is-not-subtype",
Copy link
Member

Choose a reason for hiding this comment

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

I propose type-narrower-not-subtype, right now this error code can be misleading for readers: type is not a subtype?

It would be consistent with:

TYPE_NARROWER_NOT_SUBTYPE: Final = ErrorMessage(
    "Narrowed type {} is not a subtype of input type {}", codes.TYPE_NARROWER_NOT_SUBTYPE
)

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe narrowed-type-not-subtype? Your suggestion sounds like TypeNarrower, which was a rejected name for teh feature.

mypy/semanal.py Outdated Show resolved Hide resolved
g(reveal_type(x)) # N: Revealed type is "Union[builtins.list[builtins.str], __main__.<subclass of "list" and "A">]"
[builtins fixtures/tuple.pyi]

[case testTypeIsMultipleCondition-xfail]
Copy link
Member

Choose a reason for hiding this comment

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

Can you please add a comment on why this is marked as xfail?

Copy link
Member Author

Choose a reason for hiding this comment

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

I copied this from the TypeGuard test. Seems like the xfail is just because we generate a name with an extra 1 at the end, which looks like a typo:

Expected:
  main:16: note: Revealed type is "__main__.<subclass of "Foo" and "Bar">"
  main:21: note: Revealed type is "__main__.<subclass of "Foo" and "Bar">" (diff)
Actual:
  main:16: note: Revealed type is "__main__.<subclass of "Foo" and "Bar">"
  main:21: note: Revealed type is "__main__.<subclass of "Foo" and "Bar">1" (diff)

I will remove the xfail and make it pass.

test-data/unit/check-typeis.test Show resolved Hide resolved
test-data/unit/check-typeis.test Show resolved Hide resolved
test-data/unit/check-typeis.test Show resolved Hide resolved
test-data/unit/check-typeis.test Show resolved Hide resolved
from typing import Callable, List, TypeVar
from typing_extensions import TypeIs

A = Callable[[object], TypeIs[List[T]]]
Copy link
Member

Choose a reason for hiding this comment

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

T is not defined, why does this test pass?

Choose a reason for hiding this comment

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

This is a general issue with test cases that I've seen in other tests as well. I'm not sure if T is coming from the builtins stubs or whatever else. If you are really interested I could quickly try and search where this has happened.

Obviously still good to point it out and change it here.

Choose a reason for hiding this comment

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

I think the root cause of this is that a normal builtins.pyi in typeshed defined _T (which does not get exported) wheres the test fixtures (like dict.pyi) typically use stuff like T (which does get exported).

It would probably be a good idea to change all fixtures, but that might be quite a bit of work.

Copy link
Member Author

Choose a reason for hiding this comment

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

Added an explicit definition of T in this test. Agree that ideally we should fix this throughout the test suite.

@sobolevn
Copy link
Member

@JelleZijlstra
Copy link
Member Author

Thanks @sobolevn! I have pushed some changes to address your review.

Copy link
Contributor

github-actions bot commented Mar 1, 2024

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

graphql-core (https://github.com/graphql-python/graphql-core)
+ src/graphql/execution/execute.py:638: error: Unused "type: ignore" comment  [unused-ignore]

@@ -1018,10 +1018,22 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]:
param_spec = template.param_spec()

template_ret_type, cactual_ret_type = template.ret_type, cactual.ret_type
if template.type_guard is not None:
if template.type_guard is not None and cactual.type_guard is not None:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice, thanks! I think this should fix a thing I ran into while testing #16939

Copy link
Member

@sobolevn sobolevn left a comment

Choose a reason for hiding this comment

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

Thanks again, one last suggestion: we can add a test case to check-incremental to ensure that cache works correctly for type narrowing. Especially, because TypeGuard is also missing from there: https://github.com/python/mypy/blob/master/test-data/unit/check-incremental.test

@JelleZijlstra
Copy link
Member Author

Thanks! I'll work on a separate PR with incremental tests for both TypeGuard and TypeIs.

@JelleZijlstra JelleZijlstra merged commit bcb3747 into python:master Mar 1, 2024
19 checks passed
@JelleZijlstra JelleZijlstra deleted the typenarrower branch March 1, 2024 14:01
@JelleZijlstra JelleZijlstra restored the typenarrower branch September 10, 2024 23:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants