This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: functools' singledispatch does not support GenericAlias
Type: behavior Stage: resolved
Components: Documentation, Library (Lib) Versions: Python 3.11, Python 3.10, Python 3.9
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: serhiy.storchaka Nosy List: AlexWaygood, kumaraditya, lukasz.langa, miss-islington, rhettinger, serhiy.storchaka, uriyyo
Priority: normal Keywords: patch

Created on 2021-12-10 09:04 by kumaraditya, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 30050 merged serhiy.storchaka, 2021-12-11 13:48
PR 30254 merged serhiy.storchaka, 2021-12-25 13:28
PR 30255 merged miss-islington, 2021-12-25 14:12
Messages (9)
msg408179 - (view) Author: Kumar Aditya (kumaraditya) * (Python triager) Date: 2021-12-10 09:04
functools' singledispatch does not support GenericAlias

```py
from functools import singledispatch

@singledispatch
def func(x):
    print("any")

@func.register
def _(x: list[str]):
    print("list[str]")


func(["a", "b"])

```
msg408181 - (view) Author: Alex Waygood (AlexWaygood) * (Python triager) Date: 2021-12-10 09:21
My opinion is that supporting `GenericAlias` here would be a bad idea. Associating an implementation of the function with the argument type `list[str]` is ambiguous. Would this implementation be called if any argument of type `list` was supplied, or would it only be called if all elements in the list were of type `str`?

The first option would be efficient, simple, and similar to the way singledispatch treats most other argument-types. However, it would be unintuitive.

The second option would be more intuitive, but could be extremely inefficient if a very long list was passed in. It would also make the code more complicated.
msg408204 - (view) Author: Alex Waygood (AlexWaygood) * (Python triager) Date: 2021-12-10 12:58
It would be well worth it to improve the error message, however:

```
>>> from functools import singledispatch
>>> @singledispatch
... def func(arg):
...     raise NotImplementedError
... 
>>> @func.register
... def _(arg: list[str]):
...     print('Got a list of strings')
... 
>>> func(1)
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/functools.py", line 830, in dispatch
    impl = dispatch_cache[cls]
  File "/usr/local/lib/python3.9/weakref.py", line 405, in __getitem__
    return self.data[ref(key)]
KeyError: <weakref at 0x7f2a0d9141d0; to 'type' at 0x7f2a0e08b200 (int)>

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/functools.py", line 833, in dispatch
    impl = registry[cls]
KeyError: <class 'int'>

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.9/functools.py", line 877, in wrapper
    return dispatch(args[0].__class__)(*args, **kw)
    return dispatch(args[0].__class__)(*args, **kw)
  File "/usr/local/lib/python3.9/functools.py", line 835, in dispatch
    impl = _find_impl(cls, registry)
  File "/usr/local/lib/python3.9/functools.py", line 782, in _find_impl
    mro = _compose_mro(cls, registry.keys())
  File "/usr/local/lib/python3.9/functools.py", line 743, in _compose_mro
    types = [n for n in types if is_related(n)]
  File "/usr/local/lib/python3.9/functools.py", line 743, in <listcomp>
    types = [n for n in types if is_related(n)]
  File "/usr/local/lib/python3.9/functools.py", line 742, in is_related
    and issubclass(cls, typ))
TypeError: issubclass() argument 2 cannot be a parameterized generic
```
msg408205 - (view) Author: Alex Waygood (AlexWaygood) * (Python triager) Date: 2021-12-10 13:05
The above traceback is because the `isinstance(list[str], type)` check at Lib/functools.py:848 evaluates to `True`. Related: #45665.
msg408306 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2021-12-11 14:13
Yes, it is related to issue45665. It is a complicated case due to coincidence of several circumstances.

1. isinstance(list[int], type) is True, while isinstance(typing.List[int], type) is False. list[int] is considered a type in this check.

2. list[int].__mro__ == list.__mro__, while typing.List[int] does not have the __mro__ attribute.  list[int] is considered a type in this check.

3. issubclass(cls, list[int]) raises a TypeError (the same for typing.List[int]). list[int] cannot be used as a type here.

4. 2-argument registry() does not check the type of its first argument. f.registry(42, ...) is silently passed.

In 2-argument registry() typing.List[int] is passed due to (4) and ignored in dispatch() due to (2). list[int] is passed due to (4), but caused error due to (3).

In other uses of registry() (1-argument decorator factory and decorator with annotations) typing.List[int] is not passed due to 1. list[int] is passed due to (1) and caused error due to (3).

The proposed PR makes list[int] be treated the same way as typing.List[int]. It also makes 2-argument registry() rejecting invalid first argument, so all three forms of registry() accept and reject now the same types.
msg408348 - (view) Author: Alex Waygood (AlexWaygood) * (Python triager) Date: 2021-12-11 23:08
The PR looks good to me. I think it's also important that we document that these types aren't supported, as it's not mentioned anywhere at the moment. Related: Issue34498.
msg409169 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2021-12-25 12:16
New changeset 078abb676cf759b1e960f78390b6e80f256f0255 by Serhiy Storchaka in branch 'main':
bpo-46032: Check types in singledispatch's register() at declaration time (GH-30050)
https://github.com/python/cpython/commit/078abb676cf759b1e960f78390b6e80f256f0255
msg409170 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2021-12-25 14:12
New changeset 03c7449fbc7c57f5e0365f234a0b65c1dde763f2 by Serhiy Storchaka in branch '3.10':
[3.10] bpo-46032: Check types in singledispatch's register() at declaration time (GH-30050) (GH-30254)
https://github.com/python/cpython/commit/03c7449fbc7c57f5e0365f234a0b65c1dde763f2
msg409197 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2021-12-26 12:23
New changeset 25a12aac4de819745dfc64664ba183a5784b5a81 by Miss Islington (bot) in branch '3.9':
[3.9] bpo-46032: Check types in singledispatch's register() at declaration time (GH-30050) (GH-30254) (GH-30255)
https://github.com/python/cpython/commit/25a12aac4de819745dfc64664ba183a5784b5a81
History
Date User Action Args
2022-04-11 14:59:53adminsetgithub: 90190
2021-12-26 12:23:57serhiy.storchakasetstatus: open -> closed
resolution: fixed
stage: patch review -> resolved
2021-12-26 12:23:31serhiy.storchakasetmessages: + msg409197
2021-12-25 14:12:57serhiy.storchakasetmessages: + msg409170
2021-12-25 14:12:41miss-islingtonsetnosy: + miss-islington
pull_requests: + pull_request28476
2021-12-25 13:28:39serhiy.storchakasetpull_requests: + pull_request28475
2021-12-25 12:16:19serhiy.storchakasetmessages: + msg409169
2021-12-11 23:26:47AlexWaygoodsettype: enhancement -> behavior
components: + Documentation
2021-12-11 23:08:05AlexWaygoodsetnosy: + uriyyo
messages: + msg408348
2021-12-11 14:14:36serhiy.storchakalinkissue45665 dependencies
2021-12-11 14:13:32serhiy.storchakasetmessages: + msg408306
versions: + Python 3.9, Python 3.10
2021-12-11 13:48:47serhiy.storchakasetkeywords: + patch
stage: patch review
pull_requests: + pull_request28275
2021-12-10 15:19:58serhiy.storchakasetassignee: serhiy.storchaka

nosy: + serhiy.storchaka
2021-12-10 14:50:20serhiy.storchakaunlinkissue45665 dependencies
2021-12-10 14:47:26serhiy.storchakalinkissue45665 dependencies
2021-12-10 13:05:13AlexWaygoodsetmessages: + msg408205
2021-12-10 12:58:17AlexWaygoodsetmessages: + msg408204
2021-12-10 09:21:40AlexWaygoodsetnosy: + lukasz.langa, AlexWaygood
messages: + msg408181
2021-12-10 09:04:56kumaradityacreate