Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
d3d6e64
Resolve first positional param, required to be annotated
johnslavik Jan 6, 2026
6ea2a4a
Special-case strings for forward refs similarly to typing
johnslavik Jan 6, 2026
0a39278
Rename `ref_or_type` to `ref_or_typeform`
johnslavik Jan 6, 2026
096fc3b
Add comment
johnslavik Jan 6, 2026
c8a5cdc
Shorten error message string line
johnslavik Jan 6, 2026
e1cde59
Adjust formatting to functools style
johnslavik Jan 6, 2026
6bc698b
Normalize `None` to a type, strip annotations
johnslavik Jan 6, 2026
4ef7c7c
Rename `ref_or_typeform` to `fwdref_or_typeform`
johnslavik Jan 6, 2026
004c852
Rename `skip_first` to `skip_first_param`
johnslavik Jan 6, 2026
9bc1436
Add news entry
johnslavik Jan 6, 2026
3115fd7
Add GH-130827 test
johnslavik Jan 3, 2026
f6c102f
Fix test
johnslavik Jan 6, 2026
7968570
Remove the `get_annotations` dance for now
johnslavik Jan 6, 2026
a808a1e
Fix incorrect `regster()` calls in `TestSingleDispatch.test_method_si…
johnslavik Jan 7, 2026
69b9978
Fix string signatures accordingly
johnslavik Jan 7, 2026
ebdb68d
Raise exception if positional argument not found
johnslavik Jan 7, 2026
8d86f9e
Break the exception chain
johnslavik Jan 7, 2026
82616f9
Support all callables
johnslavik Jan 7, 2026
c9a1f1a
Clarify comment
johnslavik Jan 7, 2026
e878207
Fiat lux, inline validation
johnslavik Jan 7, 2026
d3240a3
Add more test cases (mainly wrappers)
johnslavik Jan 7, 2026
9240b0d
More comments!
johnslavik Jan 7, 2026
f6ccb97
Less history pollution
johnslavik Jan 7, 2026
6642321
Document `_get_positional_param`
johnslavik Jan 7, 2026
0eaaa5b
Better comments!
johnslavik Jan 7, 2026
345b7e9
Shorten a comment
johnslavik Jan 7, 2026
8a46f3f
Rephrase the fallback path comment
johnslavik Jan 7, 2026
16f83ee
Improve the error message when missing an annotation
johnslavik Jan 7, 2026
552daaf
Correct the docstring
johnslavik Jan 7, 2026
113cc29
Rephrase the documentation again
johnslavik Jan 7, 2026
7c1bcea
Rename the function to `_get_dispatch_param`
johnslavik Jan 7, 2026
444425c
Rewrite the news entry using precise language
johnslavik Jan 7, 2026
9b26fb1
Add a test for positional-only parameter
johnslavik Jan 7, 2026
17dfb36
Add a mixed parameter types test case
johnslavik Jan 7, 2026
fbce76d
Do not break exception chain unnecessarily
johnslavik Jan 7, 2026
44b8bba
Improve the docstring
johnslavik Jan 7, 2026
ec01821
Add precedent case for GH-84644
johnslavik Jan 7, 2026
57faa34
Fix GH-84644 test
johnslavik Jan 7, 2026
e4fb514
Reword the documentation of `_get_dispatch_param`
johnslavik Jan 7, 2026
57965a9
Merge GH-130827 test into `test_method_type_ann_register`
johnslavik Jan 7, 2026
eadc38f
Add case this PR broke -- registering bound methods
johnslavik Jan 7, 2026
682c41e
Add bound methods to slow path
johnslavik Jan 7, 2026
c406755
Optimize instance checks in the fast path
johnslavik Jan 7, 2026
e238e6a
Use a match statement instead of a for loop
johnslavik Jan 7, 2026
1e61429
Rewrite to a try-except
johnslavik Jan 7, 2026
19458fc
Improve comment
johnslavik Jan 7, 2026
6390a82
Add more bound method tests
johnslavik Jan 7, 2026
3e33040
Reuse one instance of test class
johnslavik Jan 7, 2026
c50d344
Test instance validity in bound method tests
johnslavik Jan 7, 2026
32910f3
Tests and fixes for staticmethod
johnslavik Jan 7, 2026
4283fba
Add more tests for classmethod
johnslavik Jan 7, 2026
17b5088
Disambiguate a comment
johnslavik Jan 8, 2026
cdb7cca
Always respect descriptors, fallback to assumptions on function-like …
johnslavik Jan 8, 2026
62088c7
Add more comments
johnslavik Jan 8, 2026
c497857
Specialcase bound methods in singledispatchmethods
johnslavik Jan 8, 2026
0f75d98
Finalize the logic
johnslavik Jan 8, 2026
8350e71
Crystalize the decision tree
johnslavik Jan 8, 2026
50c0e64
Fix comment
johnslavik Jan 8, 2026
052c2fd
Better comments
johnslavik Jan 8, 2026
30994eb
Fiat lux
johnslavik Jan 8, 2026
3edad44
Rename function to `_get_singledispatch_annotated_param`
johnslavik Jan 8, 2026
fbc205e
Disambiguate comment
johnslavik Jan 8, 2026
b691969
More comments
johnslavik Jan 8, 2026
0859bc0
Add more missing tests
johnslavik Jan 8, 2026
7ac8275
Cast the `idx` to an integer explicitly
johnslavik Jan 8, 2026
fbe00f8
Check param kinds by name (code review)
johnslavik Jan 8, 2026
7ada2b0
Minime the try-except
johnslavik Jan 8, 2026
ac2f5a2
Remove all new tests
johnslavik Jan 8, 2026
ba46e43
Add previously failing tests only
johnslavik Jan 8, 2026
3995e79
Merge branch 'main' into fix-singledispatch-annotation-parsing
johnslavik Jan 12, 2026
48d1bde
Use imperative form in the docstring
johnslavik Jan 19, 2026
fc5df46
Raise in `_get_singledispatch_annotated_param`
johnslavik Jan 20, 2026
7fcf4d5
Rename the ugly `_inside_dispatchmethod` to `__role__`
johnslavik Jan 20, 2026
f7ec61d
Hide private params from `.register` using `__text_signature__`
johnslavik Jan 20, 2026
946ccb8
Remove comments that are sorta obvious
johnslavik Jan 20, 2026
2d1180a
Make the private helper tighter
johnslavik Jan 20, 2026
cd10e91
Condense fast path comment
johnslavik Jan 20, 2026
0a622fb
Bring back comments but make them actually helpful
johnslavik Jan 20, 2026
9b2d20d
Another comment improvement
johnslavik Jan 20, 2026
9191a22
Fix incorrect comment
johnslavik Jan 20, 2026
ef74667
Restore old tests
johnslavik Jan 20, 2026
b55de76
Rework registration tests
johnslavik Jan 20, 2026
9977251
Use stars for referring to param names
johnslavik Jan 20, 2026
d282c9a
Use `__role__` name only in the `register()` signature
johnslavik Jan 20, 2026
4c43a9c
Denote the issues covered
johnslavik Jan 20, 2026
3b5a410
Add regrtest from @pR0Ps
pR0Ps Jan 20, 2026
f6ce233
Somewhat better comment
johnslavik Jan 20, 2026
97a8515
Merge branch 'main' into fix-singledispatch-annotation-parsing
johnslavik Jan 22, 2026
62ea333
Apply suggestion from @johnslavik
johnslavik Jan 22, 2026
f0097c4
Final optional refinements
johnslavik Feb 8, 2026
4e6a003
Bring back `role` and `__role__`
johnslavik Feb 11, 2026
2d70e22
Fix code line length in `_get_singledispatch_annotated_param`
johnslavik Mar 1, 2026
c716b57
Rename `__role__` to `_role` and make it kw-only
johnslavik Mar 1, 2026
a28e9af
Fix missing param default in `__text_signature__`
johnslavik Mar 1, 2026
cfb9194
Change wording in news entry patch attribution
johnslavik Mar 1, 2026
b03891f
Rephrase the entire news entry
johnslavik Mar 1, 2026
4c8e818
Finish up news entry corrections
johnslavik Mar 1, 2026
7fa9315
Apply more consistent formatting
johnslavik Mar 1, 2026
cee055a
...Actually readable formatting
johnslavik Mar 1, 2026
4db216f
Merge main to avoid conflict in the "What's New" entry
johnslavik Mar 1, 2026
cc2bcc7
Rephrase the news entry again
johnslavik Mar 1, 2026
371bff5
Add a what's new entry
johnslavik Mar 1, 2026
a5a79ad
Deprecate singledispatch registration by return annotation
johnslavik Jul 20, 2026
3dadb56
Merge branch 'main' into fix-singledispatch-annotation-parsing
johnslavik Jul 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Doc/deprecations/pending-removal-in-3.18.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,12 @@ Pending removal in Python 3.18
* ``import`` lines in :file:`{name}.pth` files are silently ignored.

(Contributed by Barry Warsaw in :gh:`148641`.)

* :mod:`functools`:

* Using a return annotation to infer the dispatch type for
:func:`functools.singledispatch` or
:class:`functools.singledispatchmethod` is deprecated since Python 3.16
and will raise :exc:`TypeError` in Python 3.18. Annotate the dispatch
parameter or pass the dispatch type explicitly instead.
(Contributed by Bartosz Sławecki in :gh:`143465`.)
31 changes: 26 additions & 5 deletions Doc/library/functools.rst
Original file line number Diff line number Diff line change
Expand Up @@ -506,9 +506,9 @@ The :mod:`!functools` module defines the following functions:
:no-typesetting:

To add overloaded implementations to the function, use the :func:`!register`
attribute of the generic function, which can be used as a decorator. For
functions annotated with types, the decorator will infer the type of the
first argument automatically::
attribute of the generic function, which can be used as a decorator. For
functions annotated with types, the decorator can infer the dispatch type
automatically::

>>> @fun.register
... def _(arg: int, verbose=False):
Expand Down Expand Up @@ -540,8 +540,9 @@ The :mod:`!functools` module defines the following functions:
... print(i, elem)
...

For code which doesn't use type annotations, the appropriate type
argument can be passed explicitly to the decorator itself::
For code which doesn't use type annotations, or where the first resolved
annotation does not belong to the dispatch argument, the appropriate type
can be passed explicitly to the decorator itself::

>>> @fun.register(complex)
... def _(arg, verbose=False):
Expand Down Expand Up @@ -662,6 +663,12 @@ The :mod:`!functools` module defines the following functions:
The :func:`~singledispatch.register` attribute now supports
:class:`typing.Union` as a type annotation.

.. deprecated:: 3.16
Using a return annotation to infer the dispatch type is deprecated and
will raise :exc:`TypeError` in Python 3.18. Annotate the dispatch
parameter or pass the dispatch type explicitly to :func:`!register`
instead.


.. class:: singledispatchmethod(func)

Expand All @@ -686,6 +693,20 @@ The :mod:`!functools` module defines the following functions:
def _(self, arg: bool):
return not arg

When *self* or *cls* is annotated, pass the dispatch type explicitly rather
than relying on annotation inference::

from typing import Self

class Negator:
@singledispatchmethod
def neg(self, arg):
raise NotImplementedError("Cannot negate a")

@neg.register(int)
def _(self: Self, arg: int):
return -arg

``@singledispatchmethod`` supports nesting with other decorators such as
:deco:`classmethod`. Note that to allow for
``dispatcher.register``, ``singledispatchmethod`` must be the *outer most*
Expand Down
11 changes: 11 additions & 0 deletions Lib/functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,17 @@ def register(cls, func=None):
f"Invalid annotation for {argname!r}. "
f"{cls!r} is not a class."
)
if argname == 'return':
import os
import warnings
Comment thread
johnslavik marked this conversation as resolved.
warnings.warn(
"Using the return annotation to infer the dispatch type "
"is deprecated and will raise TypeError in Python 3.18. "
"Annotate the dispatch parameter or pass the dispatch "
"type explicitly to register().",
DeprecationWarning,
skip_file_prefixes=(os.path.dirname(__file__),),
)

if isinstance(cls, UnionType):
for arg in cls.__args__:
Expand Down
32 changes: 32 additions & 0 deletions Lib/test/test_functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -3337,6 +3337,38 @@ def _(arg: typing.Union[int, typing.Iterable[str]]):
'int | typing.Iterable[str] not all arguments are classes.'
)

def test_return_annotation_deprecated(self):
msg = "Using the return annotation to infer the dispatch type"
register = compile(
"dispatcher.register(impl)", "<singledispatch caller>", "exec"
)

@functools.singledispatch
def func(arg):
return "base"

def impl(arg) -> int:
return "int"

with self.assertWarnsRegex(DeprecationWarning, msg) as cm:
exec(register, {'dispatcher': func, 'impl': impl})
self.assertEqual(cm.filename, "<singledispatch caller>")
self.assertIs(func.registry[int], impl)

class C:
@functools.singledispatchmethod
def method(self, arg):
return "base"

def method_impl(self) -> str:
return "str"

with self.assertWarnsRegex(DeprecationWarning, msg) as cm:
exec(register, {'dispatcher': C.method, 'impl': method_impl})
self.assertEqual(cm.filename, "<singledispatch caller>")
dispatcher = C.__dict__['method'].dispatcher
self.assertIs(dispatcher.registry[str], method_impl)

def test_invalid_positional_argument(self):
@functools.singledispatch
def f(*args, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Deprecate using a return annotation to infer the dispatch type for
:func:`functools.singledispatch` and
:class:`functools.singledispatchmethod`. It will raise :exc:`TypeError` in
Python 3.18. Annotate the dispatch parameter or pass the dispatch type
explicitly instead. Patch by Bartosz Sławecki.
Loading