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

Improve operator.itemgetter.__call__ generic following mypy 1.11 fix #13489

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
37 changes: 37 additions & 0 deletions stdlib/@tests/test_cases/check_SupportsGetItem.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from __future__ import annotations

from _typeshed import SupportsDunderGT, SupportsDunderLT, SupportsGetItem
from collections.abc import Callable
from operator import itemgetter
from typing import Any, TypeVar, assert_type

_T = TypeVar("_T")


# This should be equivalent to itemgetter().__call__
def standalone_call(obj: SupportsGetItem[Any, _T]) -> _T: ...


# Expected type of itemgetter(1).__call__
expected_type_itemgetter_call: Callable[[SupportsGetItem[int, _T]], _T] # pyright: ignore[reportGeneralTypeIssues]

# Expecting itemgetter(1) to be assignable to this
# based on the example below: min({"first": 1, "second": 2}.items(), key=itemgetter(1))
# That example and assigning to this variable are what failed in https://github.com/python/mypy/issues/14032
expected_assignable_to: Callable[[tuple[str, int]], SupportsDunderLT[Any] | SupportsDunderGT[Any]]


# Regression tests for https://github.com/python/mypy/issues/14032
assert_type(itemgetter("")({"first": 1, "second": 2}), int)
assert_type(min({"first": 1, "second": 2}, key=itemgetter(1)), str)
assert_type(min({"first": 1, "second": 2}.items(), key=itemgetter(1)), tuple[str, int])
assert_type(standalone_call({"first": 1, "second": 2}), int)
assert_type(min({"first": 1, "second": 2}, key=standalone_call), str)
assert_type(min({"first": 1, "second": 2}.items(), key=standalone_call), tuple[str, int])

expected_itemgetter_call_type = itemgetter(1).__call__
expected_itemgetter_call_type = itemgetter(1)
expected_assignable_to = itemgetter(1)

expected_itemgetter_call_type = standalone_call
expected_assignable_to = standalone_call
7 changes: 2 additions & 5 deletions stdlib/operator.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,8 @@ class itemgetter(Generic[_T_co]):
# __key: _KT_contra in SupportsGetItem seems to be causing variance issues, ie:
# TypeVar "_KT_contra@SupportsGetItem" is contravariant
# "tuple[int, int]" is incompatible with protocol "SupportsIndex"
# preventing [_T_co, ...] instead of [Any, ...]
#
# A suspected mypy issue prevents using [..., _T] instead of [..., Any] here.
# https://github.com/python/mypy/issues/14032
def __call__(self, obj: SupportsGetItem[Any, Any]) -> Any: ...
# preventing [_T_co, _T] instead of [Any, _T]
def __call__(self, obj: SupportsGetItem[Any, _T]) -> _T: ...

@final
class methodcaller:
Expand Down
2 changes: 0 additions & 2 deletions stubs/six/@tests/stubtest_allowlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ six.get_method_self
six.viewitems
six.viewkeys
six.viewvalues
# Should be `operator.itemgetter[int]`. But a bug in mypy prevents using TypeVar in itemgetter__call__
six.byte2int

# Utils
six.Module_six_moves_urllib
Expand Down
6 changes: 2 additions & 4 deletions stubs/six/six/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import builtins
import operator
import types
import unittest
from _typeshed import IdentityFunction, SupportsGetItem, Unused
from _typeshed import IdentityFunction, Unused
from builtins import next as next
from collections.abc import Callable, ItemsView, Iterable, Iterator as _Iterator, KeysView, Mapping, ValuesView
from functools import wraps as wraps
Expand Down Expand Up @@ -61,9 +61,7 @@ unichr = chr

def int2byte(i: int) -> bytes: ...

# Should be `byte2int: operator.itemgetter[int]`. But a bug in mypy prevents using TypeVar in itemgetter.__call__
def byte2int(obj: SupportsGetItem[int, _T]) -> _T: ...

byte2int: operator.itemgetter[int]
indexbytes = operator.getitem
iterbytes = iter

Expand Down
Loading