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

Infer generic type arguments for slice expressions #18160

Merged
merged 2 commits into from
Nov 19, 2024

Conversation

brianschubert
Copy link
Collaborator

Fixes #18149

Slices were made generic in python/typeshed#11637. Currently, all slice expressions are inferred to have type slice[Any, Any, Any]. This PR fills in the generic type arguments more appropriately using start/stop/stride expression types.

Given

class Foo:
    def __getitem__[T](self, item: T) -> T: return item

x = Foo()
reveal_type(x[1:])

Before:

main.py:5: note: Revealed type is "builtins.slice[Any, Any, Any]"

After:

main.py:5: note: Revealed type is "builtins.slice[builtins.int, None, None]"

@brianschubert
Copy link
Collaborator Author

As an alternative approach, I toyed with using the slice.__new__ overloads from typeshed to infer the type arguments. However, this had a big impact on the tests (since all tests involving slice expressions now needed a fixture defining slice), and I'm not sure if there are any benefits from doing it that way.

--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -5616,7 +5616,12 @@ class ExpressionChecker(ExpressionVisitor[Type]):
             if index:
                 t = self.accept(index)
                 self.chk.check_subtype(t, expected, index, message_registry.INVALID_SLICE_INDEX)
-        return self.named_type("builtins.slice")
+        args = [
+            TempNode(NoneType()) if arg is None else arg
+            for arg in [e.begin_index, e.end_index, e.stride]
+        ]
+        slice_type = TypeType(self.named_type("builtins.slice"))
+        return self.check_call(slice_type, args, [nodes.ARG_POS] * 3, e, [None] * 3)[0]

This comment has been minimized.

Copy link
Collaborator

@hauntsaninja hauntsaninja left a comment

Choose a reason for hiding this comment

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

Nice! Can we record t in the loop so we avoid type checking the index expressions twice?

@brianschubert
Copy link
Collaborator Author

brianschubert commented Nov 18, 2024

Sure, done!

(FWIW, that's how I originally wrote it 😉. I thought to try separating this logic from that loop since I figured it may be removed soon, now that generic slices let us do better checking on slice indices than the hardcoded SupportsIndex/None checks in that loop)

Copy link
Contributor

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

pandas-stubs (https://github.com/pandas-dev/pandas-stubs)
+ tests/test_frame.py:2246: error: Expression is of type "tuple[Index[int], slice[None, None, None]]", not "tuple[Index[int], slice[Any, Any, Any]]"  [assert-type]
- tests/test_frame.py:226: error: Expression is of type "Any", not "DataFrame"  [assert-type]
- tests/test_frame.py:227: error: Expression is of type "Any", not "DataFrame"  [assert-type]

@hauntsaninja hauntsaninja merged commit 6759dbd into python:master Nov 19, 2024
19 checks passed
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.

Change in inference of overloads involving Hashable and slice now that slice is generic
2 participants