Skip to content

Conversation

@johnslavik
Copy link
Member

@johnslavik johnslavik commented Jan 8, 2026

Follow-up to gh-130309.
See https://github.com/python/cpython/pull/130309/changes#r2663516538.

Details

These functions don't annotate parameters for values used in single-dispatching (item), they annotate parameters one later (arg).

This being legal relies on a bug -- GH-84644, which is that singledispatch doesn't verify the annotation is on the "first" parameter.

In practice, it doesn't matter how you annotate func here, because it's a default callback. But what matters is that any function passed to register() annotates the target parameter (always first positional in singledispatch and always second positional in singledispatchmethod (unless staticmethod, then the first) etc.) for the value to single-dispatch on; not some other, unrelated parameter (or return type, as presented in GH-84644).

Let's have a class with the same registree signature as these from the test:

class A:
    @functools.singledispatchmethod
    def func(self, item, arg: int) -> str:
        return 'argint'
    @func.register
    def _(self, item, arg: bytes) -> str:
        return 'argbytes'

a = A()
print(a.func(b'', 1))

For this code, the output would be argbytes, even though arg is an integer 1.

… the right params

See https://github.com/python/cpython/pull/130309/changes#r2663516538.

These functions don't annotate parameters for values used in single-dispatching (`item`), they annotate parameters one later (`arg`).

This being legal relies on a bug -- pythonGH-84644, which is that `singledispatch` doesn't verify the annotation is on the "first" parameter.
I think these functions should look like

```diff
            @functools.singledispatchmethod
-           def func(self, item, arg: int) -> str:
+           def func(self, item: int, arg) -> str:
                return str(item)
            @func.register
-           def _(self, item, arg: bytes) -> str:
+           def _(self, item: bytes, arg) -> str:
                return str(item)
```

(and signature tests updated accordingly)

In practice, it doesn't matter how you annotate `func` here, because it's a default callback. But what matters is that any function passed to `register()` annotates the target parameter (always first positional in `singledispatch` and always second positional in `singledispatchmethod` (unless staticmethod, then the first) etc.) for the value to single-dispatch on; not some other, unrelated parameter (or return type, as presented in pythonGH-84644).

Let's have a class with the same registree signature as these from the test:

```py
class A:
    @functools.singledispatchmethod
    def func(self, item, arg: int) -> str:
        return 'argint'
    @func.register
    def _(self, item, arg: bytes) -> str:
        return 'argbytes'

a = A()
print(a.func(b'', 1))
```

For this code, the output would be `argbytes`, even though `arg` is an integer `1`.
Comment on lines +3451 to 3455
def m(self, item: int, arg) -> str:
return str(item)
@classmethod
def cm(cls, item, arg: int) -> str:
def cm(cls, item: int, arg) -> str:
return str(item)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove these methods if they are not used.

return str(item)
@functools.singledispatchmethod
def func(self, item, arg: int) -> str:
def func(self, item: int, arg) -> str:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leave item not annotated here. This is the generic variant. But keep annotation for arg, so it will be seen in the result.

return str(item)
@func.register
def _(self, item, arg: bytes) -> str:
def _(self, item: bytes, arg) -> str:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep annotation for arg.

Suggested change
def _(self, item: bytes, arg) -> str:
def _(self, item: int, arg: bytes) -> str:

See test_signatures above. These examples should be similar.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting review needs backport to 3.13 bugs and security fixes needs backport to 3.14 bugs and security fixes skip news tests Tests in the Lib/test dir

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants