Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

typing.get_type_hints() works for Callable[[], None] from the typing module, but not collections.abc #91621

Closed
Zac-HD opened this issue Apr 16, 2022 · 5 comments
Labels
3.11 expert-typing stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@Zac-HD
Copy link
Contributor

Zac-HD commented Apr 16, 2022

Bug report

typing.get_type_hints() works for Callable[[], None] from the typing module, but not collections.abc. This is the standard way to annotate a callable which takes no arguments, and the generic types from typing should behave identically to those in collections.abc.

import collections.abc
import typing

def f1(x: typing.Callable[[], None]):
    pass

def f2(x: collections.abc.Callable[[], None]):
    pass

assert "x" in typing.get_type_hints(f1)
typing.get_type_hints(f2)  # Raises TypeError!

Your environment

CPython 3.11.0a7 (and I believe back to a3), found via CI for Hypothesis, and reproduced in Ubuntu/WSL2 locally.
I'm skipping that test for now, and will re-enable when this is fixed.

@Zac-HD Zac-HD added the type-bug An unexpected behavior, bug, or error label Apr 16, 2022
@AlexWaygood AlexWaygood added stdlib Python modules in the Lib dir 3.11 expert-typing labels Apr 16, 2022
@AlexWaygood
Copy link
Member

AlexWaygood commented Apr 16, 2022

Reproduced, and I agree that this looks like a regression in 3.11. On 3.10:

C:\Users\alexw\coding>python
Python 3.10.4 (tags/v3.10.4:9d38120, Mar 23 2022, 23:13:41) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import typing, collections.abc
>>> def f(x: collections.abc.Callable[[], None]): ...
...
>>> typing.get_type_hints(f)
{'x': collections.abc.Callable[[], None]}

On 3.11:

C:\Users\alexw\coding>python
Python 3.10.4 (tags/v3.10.4:9d38120, Mar 23 2022, 23:13:41) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import typing, collections.abc
>>> def f(x: collections.abc.Callable[[], None]): ...
...
>>> typing.get_type_hints(f)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\alexw\coding\cpython\Lib\typing.py", line 2277, in get_type_hints
    hints[name] = _eval_type(value, globalns, localns)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\alexw\coding\cpython\Lib\typing.py", line 353, in _eval_type
    t = t.__origin__[args]
        ~~~~~~~~~~~~^^^^^^
  File "<frozen _collections_abc>", line 430, in __new__
TypeError: Callable must be used as Callable[[arg, ...], result].

@hauntsaninja
Copy link
Contributor

hauntsaninja commented Apr 17, 2022

Looks like this is a result of #30900 cc @NiklasRosenstein @JelleZijlstra

@hauntsaninja
Copy link
Contributor

hauntsaninja commented Apr 17, 2022

The complexity here traces back to some of the decisions made in https://bugs.python.org/issue42195

We do this kind of thing a couple times in typing.py, I can open a PR:

diff --git a/Lib/typing.py b/Lib/typing.py
index 3e0fbdb989..af424c2109 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -350,7 +350,11 @@ def _eval_type(t, globalns, localns, recursive_guard=frozenset()):
                 ForwardRef(arg) if isinstance(arg, str) else arg
                 for arg in t.__args__
             )
-            t = t.__origin__[args]
+            if (t.__origin__ is collections.abc.Callable
+                and not (len(args) == 2 and _is_param_expr(args[0]))):
+                t = t.__origin__[(args[:-1], args[-1])]
+            else:
+                t = t.__origin__[args]
         ev_args = tuple(_eval_type(a, globalns, localns, recursive_guard) for a in t.__args__)
         if ev_args == t.__args__:
             return t

@Fidget-Spinner
Copy link
Member

Fidget-Spinner commented Apr 17, 2022

The complexity here traces back to some of the decisions made in https://bugs.python.org/issue42195

We do this kind of thing a couple times in typing.py, I can open a PR:

🤦 . Seeing this again, I'm sad that the special case has to exist. Unfortunately, this seems like an easy way to trip up new contributors. Please open a PR.

hauntsaninja added a commit to hauntsaninja/cpython that referenced this issue Apr 18, 2022
This mirrors logic in typing.get_args. The trickiness comes from how we
flatten args in collections.abc.Callable, see
https://bugs.python.org/issue42195
JelleZijlstra pushed a commit that referenced this issue May 2, 2022
)

This mirrors logic in typing.get_args. The trickiness comes from how we
flatten args in collections.abc.Callable, see
https://bugs.python.org/issue42195
@JelleZijlstra
Copy link
Member

JelleZijlstra commented May 2, 2022

Thanks @Zac-HD for the pre-beta testing and @hauntsaninja for the patch!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.11 expert-typing stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

5 participants