https://github.com/python/cpython/commit/cb339d3c9eaeedb6e541a36560b8a21567158e25
commit: cb339d3c9eaeedb6e541a36560b8a21567158e25
branch: main
author: Carey Metcalfe <[email protected]>
committer: JelleZijlstra <[email protected]>
date: 2026-04-15T06:24:28-07:00
summary:
gh-143886: Ensure function annotations are returned in order of definition
(#143888)
Ensure function annotations are returned in order of definition
Previously, when getting type annotations of a function, normal
arguments were returned before positional-only ones in the dictionary.
Since `functools.singledispatch` relies on this ordering being correct
to dispatch based on the type of the first argument, this issue was
causing incorrect registrations for functions with positional-only
arguments.
This commit updates how annotations are generated so that
positional-only arguments are generated and added to the dictionary
before normal arguments.
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2026-01-15-13-37-21.gh-issue-143886.2gk5QC.rst
M Lib/test/test_functools.py
M Lib/test/test_typing.py
M Python/codegen.c
diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py
index efa85b564f7cdf..a8ee7d119e4bc6 100644
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -3358,6 +3358,21 @@ def t(self, *args, **kwargs):
with self.assertRaisesRegex(TypeError, msg):
A().t(a=1)
+ def test_positional_only_argument(self):
+ @functools.singledispatch
+ def f(arg, /, extra):
+ return "base"
+ @f.register
+ def f_int(arg: int, /, extra: str):
+ return "int"
+ @f.register
+ def f_str(arg: str, /, extra: int):
+ return "str"
+
+ self.assertEqual(f(None, "extra"), "base")
+ self.assertEqual(f(1, "extra"), "int")
+ self.assertEqual(f("s", "extra"), "str")
+
def test_union(self):
@functools.singledispatch
def f(arg):
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index c6f08ff8a052ab..9c0172f6ba7f23 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -7269,6 +7269,13 @@ class TD[UniqueT](TypedDict):
self.assertEqual(TD.__annotations__, {'a':
EqualToForwardRef('UniqueT', owner=TD, module=TD.__module__)})
self.assertEqual(get_type_hints(TD), {'a': TD.__type_params__[0]})
+ def test_get_type_hints_order(self):
+ """Ensure that the order of function annotations matches the order
they're defined"""
+ def f(positional: int, /, normal: str, *args: bytes, kwarg: list,
**kwargs: bool) -> tuple:
+ pass
+
+ self.assertEqual(list(gth(f)), ["positional", "normal", "args",
"kwarg", "kwargs", "return"])
+
class GetUtilitiesTestCase(TestCase):
def test_get_origin(self):
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2026-01-15-13-37-21.gh-issue-143886.2gk5QC.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-15-13-37-21.gh-issue-143886.2gk5QC.rst
new file mode 100644
index 00000000000000..fe4835ec28cfd5
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-15-13-37-21.gh-issue-143886.2gk5QC.rst
@@ -0,0 +1,3 @@
+Reorder function annotations so positional-only arguments are returned
+before other arguments. This fixes how :func:`functools.singledispatch`
+registers functions with positional-only arguments.
diff --git a/Python/codegen.c b/Python/codegen.c
index aca590d055f466..a0e5d9f659415e 100644
--- a/Python/codegen.c
+++ b/Python/codegen.c
@@ -1130,10 +1130,10 @@ codegen_annotations_in_scope(compiler *c, location loc,
Py_ssize_t *annotations_len)
{
RETURN_IF_ERROR(
- codegen_argannotations(c, args->args, annotations_len, loc));
+ codegen_argannotations(c, args->posonlyargs, annotations_len, loc));
RETURN_IF_ERROR(
- codegen_argannotations(c, args->posonlyargs, annotations_len, loc));
+ codegen_argannotations(c, args->args, annotations_len, loc));
if (args->vararg && args->vararg->annotation) {
RETURN_IF_ERROR(
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]