Skip to content

Changed callable to return TypeIs[Callable] rather than TypeIs[Callable[..., object]] #16051

Open
randolf-scholz wants to merge 5 commits into
python:mainfrom
randolf-scholz:callable_any_return
Open

Changed callable to return TypeIs[Callable] rather than TypeIs[Callable[..., object]] #16051
randolf-scholz wants to merge 5 commits into
python:mainfrom
randolf-scholz:callable_any_return

Conversation

@randolf-scholz

Copy link
Copy Markdown
Contributor

Fixes #16050

@github-actions

This comment has been minimized.

@AlexWaygood

Copy link
Copy Markdown
Member

you can fix the pyright issues by using TypeIs[Callable[..., Any]] rather than TypeIs[Callable] here. It means the same thing as TypeIs[Callable] but is more explicit and will satisfy pyright in strict mode.

But it's unclear to me why this change is desirable. I don't really agree that type checkers are correct in narrowing an object to Callable[..., Any] after an isinstance(x, Callable) check. ty currently narrows the type to Top[Callable[..., object]] after an isinstance(x, Callable) check, which is possibly too strict, but requiring additional narrowing checks before you permit arbitrary operations on the object returned by the callable seems reasonable to me

@github-actions

This comment has been minimized.

@randolf-scholz

Copy link
Copy Markdown
Contributor Author

Well, I would say there are 2 motivations:

  1. I want if isinstance(arg, Callable): and if callable(arg): to produce identical narrowing. Whether or not thats Callable[..., Any] or Callable[..., object] is a second order concern.

  2. I can see the motivation why one would want to infer object rather than Any/Unknown, but I do not see why one should special case that only for callable in particular. Consider:

from typing import reveal_type
from collections.abc import Iterable

def check(arg: object) -> None:
    if isinstance(arg, Iterable):
        reveal_type(arg)
        reveal_type(next(iter(arg)))

Here all the type checkers, except ty, say it's Iterable[Any] / Iterable[unknown] as well. Hence, I don't believe it makes sense to single out and special case callable in typeshed. One could consider making all the standard covariant containers default to object, but maybe one should leave that to the type checker vendors.

@randolf-scholz

randolf-scholz commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Oh, and wrt to the last point, my idea with using TypeIs[Callable] rather than TypeIs[Callable[..., Any]] was exactly that, if a type checker like ty decided to treat Callable as class Callable[**P, R=object] rather than class Callable[**P, R], then callable would inherit that behavior automatically with TypeIs[Callable], but with Typeis[Callable[..., Any]] it would force the Any inference.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@randolf-scholz
randolf-scholz marked this pull request as ready for review July 20, 2026 16:41
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Should callable narrow to TypeIs[Callable[..., object]] or TypeIs[Callable] ?

2 participants