You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
functools.singledispatch does not support Union types #90172
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=<Date2021-12-10.23:29:02.779>created_at=<Date2021-12-08.13:34:12.625>labels= ['type-feature', 'library', '3.9', '3.10', '3.11']
title='functools.singledispatch does not support Union types'updated_at=<Date2022-04-03.08:22:50.956>user='https://github.com/evansd'
It's not currently possible to use singledispatch with a function annotated with a Union type, although the desired behaviour is clear.
Example:
from functools import singledispatch
from typing import Union
@singledispatch
def test(arg):
raise ValueError(arg)
@test.register
def _(arg: Union[int, float]):
print(f"{arg} is a number")
test(1)
This dies with:
TypeError: Invalid annotation for 'arg'. typing.Union[int, float] is not a class.
I've implemented a workaround which digs the types out of the union and registers them one-by-one:
from functools import singledispatch
from typing import Union, get_type_hints
def register_for_union_type(target_fn):
def decorator(fn):
arg_type = list(get_type_hints(fn).values())[0]
assert arg_type.__origin__ is Union
for type_ in arg_type.__args__:
fn = target_fn.register(type_)(fn)
return fn
return decorator
@singledispatch
def test(arg):
raise ValueError(arg)
@register_for_union_type(test)
def _(arg: Union[int, float]):
print(f"{arg} is a number")
test(1)
Looking at the source for singledispatch is doesn't look like it would too difficult to add support for something like this, though of course there may be subtleties I've missed. Would you be open to a patch?
This is awesome! Should a note be added to the functools documentation mentioning the new feature? (Genuine question — I'm not sure whether it's necessary myself.)
wrobell commented on Dec 30, 2021
wrobellmannequin
Mannequin
More actions
Will it support Optional as well?
According to PEP-604, these two shall be equivalent (using Python 3.10 below)?
Also, IMHO, the documentation of singledispatch should be improved. It needs to state that only Python types are supported with few exceptions (union type now), but other annotation types are not supported, i.e. generics like list[int].
>>> from typing import Optional, get_origin
>>> get_origin(Optional[int])
typing.Union
^Because of that, it will work with typing.Optional as well as typing.Union and types.UnionType, yes.
It does indeed work (continuing from the OP's example):
>>> @test.register
... def_(arg: str|None):
... print(f"{arg} may be a string")
...
>>> test(None)
None may be a string
>>> test("x")
x may be a string
But it's not obvious that it should, because None isn't a type. It apparently works because None gets turned into type(None) somewhere along the way in typing.py, but this seems worth testing explicitly in test_functools.
So anyone up for adding a test case that uses an Optional type?
Activity
evansd commentedon Dec 8, 2021
It's not currently possible to use
singledispatchwith a function annotated with a Union type, although the desired behaviour is clear.Example:
This dies with:
I've implemented a workaround which digs the types out of the union and registers them one-by-one:
Looking at the source for
singledispatchis doesn't look like it would too difficult to add support for something like this, though of course there may be subtleties I've missed. Would you be open to a patch?The only other mentions I've found of this online are:
https://mail.python.org/archives/list/python-ideas@python.org/thread/HF5HDXQ2SXZHO3TWODIRQATTE4TCAWPL/
https://stackoverflow.com/questions/61721761/type-hints-and-singledispatch-how-do-i-include-union-in-an-extensible-w
3 remaining items
ambv commentedon Dec 10, 2021
New changeset 3cb357a by Yurii Karabas in branch 'main':
bpo-46014: Add ability to use typing.Union with singledispatch (GH-30017)
3cb357a
ambv commentedon Dec 10, 2021
Support added as an enhancement in Python 3.11. Thanks, Yurii! ✨ 🍰 ✨
AlexWaygood commentedon Dec 10, 2021
This is awesome! Should a note be added to the functools documentation mentioning the new feature? (Genuine question — I'm not sure whether it's necessary myself.)
wrobell commentedon Dec 30, 2021
Will it support Optional as well?
According to PEP-604, these two shall be equivalent (using Python 3.10 below)?
Also, IMHO, the documentation of singledispatch should be improved. It needs to state that only Python types are supported with few exceptions (union type now), but other annotation types are not supported, i.e. generics like
list[int].AlexWaygood commentedon Dec 30, 2021
^Because of that, it will work with typing.Optional as well as typing.Union and types.UnionType, yes.
I am planning on submitting a docs PR at some point in the next few days (probably linked to a new BPO issue).
iritkatriel commentedon Apr 11, 2022
Reopening as per #32282 (comment)
JelleZijlstra commentedon Apr 16, 2022
It does indeed work (continuing from the OP's example):
But it's not obvious that it should, because
Noneisn't a type. It apparently works becauseNonegets turned intotype(None)somewhere along the way in typing.py, but this seems worth testing explicitly intest_functools.So anyone up for adding a test case that uses an Optional type?
gh-90172: add test for functools.singledispatch on Union types with N…
JelleZijlstra commentedon May 3, 2022
Thanks @Thaddeus1499 for contributing more tests.