Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # 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