Skip to content

Commit cb339d3

Browse files
authored
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.
1 parent ab45919 commit cb339d3

File tree

4 files changed

+27
-2
lines changed

4 files changed

+27
-2
lines changed

Lib/test/test_functools.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3358,6 +3358,21 @@ def t(self, *args, **kwargs):
33583358
with self.assertRaisesRegex(TypeError, msg):
33593359
A().t(a=1)
33603360

3361+
def test_positional_only_argument(self):
3362+
@functools.singledispatch
3363+
def f(arg, /, extra):
3364+
return "base"
3365+
@f.register
3366+
def f_int(arg: int, /, extra: str):
3367+
return "int"
3368+
@f.register
3369+
def f_str(arg: str, /, extra: int):
3370+
return "str"
3371+
3372+
self.assertEqual(f(None, "extra"), "base")
3373+
self.assertEqual(f(1, "extra"), "int")
3374+
self.assertEqual(f("s", "extra"), "str")
3375+
33613376
def test_union(self):
33623377
@functools.singledispatch
33633378
def f(arg):

Lib/test/test_typing.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7269,6 +7269,13 @@ class TD[UniqueT](TypedDict):
72697269
self.assertEqual(TD.__annotations__, {'a': EqualToForwardRef('UniqueT', owner=TD, module=TD.__module__)})
72707270
self.assertEqual(get_type_hints(TD), {'a': TD.__type_params__[0]})
72717271

7272+
def test_get_type_hints_order(self):
7273+
"""Ensure that the order of function annotations matches the order they're defined"""
7274+
def f(positional: int, /, normal: str, *args: bytes, kwarg: list, **kwargs: bool) -> tuple:
7275+
pass
7276+
7277+
self.assertEqual(list(gth(f)), ["positional", "normal", "args", "kwarg", "kwargs", "return"])
7278+
72727279

72737280
class GetUtilitiesTestCase(TestCase):
72747281
def test_get_origin(self):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Reorder function annotations so positional-only arguments are returned
2+
before other arguments. This fixes how :func:`functools.singledispatch`
3+
registers functions with positional-only arguments.

Python/codegen.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,10 +1130,10 @@ codegen_annotations_in_scope(compiler *c, location loc,
11301130
Py_ssize_t *annotations_len)
11311131
{
11321132
RETURN_IF_ERROR(
1133-
codegen_argannotations(c, args->args, annotations_len, loc));
1133+
codegen_argannotations(c, args->posonlyargs, annotations_len, loc));
11341134

11351135
RETURN_IF_ERROR(
1136-
codegen_argannotations(c, args->posonlyargs, annotations_len, loc));
1136+
codegen_argannotations(c, args->args, annotations_len, loc));
11371137

11381138
if (args->vararg && args->vararg->annotation) {
11391139
RETURN_IF_ERROR(

0 commit comments

Comments
 (0)