python / python/cpython

Calling typing.get_type_hints with a class or instance method with PEP 695 type parameters fails if PEP 563 is enabled

Open
#124,089 5 comments 1 reaction 1 assignee View on GitHub

@sobolevn is already working on this.

Since Sep 14, 2024.

3.12 3.13 3.14 stdlib topic-typing type-bug
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

Bug report

Bug description:
from __future__ import annotations

import typing

class Test[M]:
    def foo(self, arg: M) -> None:
        pass

print(typing.get_type_hints(Test.foo))
$ python3.12 pep_695_pep_563.py 
Traceback (most recent call last):
  File "/run/host/tmp/test/pep_695_pep_563.py", line 9, in <module>
    print(typing.get_type_hints(Test.foo))
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.12/typing.py", line 2310, in get_type_hints
    hints[name] = _eval_type(value, globalns, localns, type_params)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.12/typing.py", line 415, in _eval_type
    return t._evaluate(globalns, localns, type_params, recursive_guard=recursive_guard)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.12/typing.py", line 947, in _evaluate
    eval(self.__forward_code__, globalns, localns),
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<string>", line 1, in <module>
NameError: name 'M' is not defined

This seems very similar to #114053, but with class methods (or instance methods, the result is the same) instead of classes themselves.

Removing the from __future__ import annotations import works fine:

import typing

class Test[M]:
    def foo(self, arg: M) -> None:
        pass

print(typing.get_type_hints(Test.foo))
$ python3.12 pep_695_no_563.py 
{'arg': M, 'return': <class 'NoneType'>}

Using the pre PEP-695 syntax, with or without from __future__ import annotations, works fine as well:

from __future__ import annotations

import typing

M = typing.TypeVar("M")

class Test(typing.Generic[M]):
    def foo(self, arg: M) -> None:
        pass

print(typing.get_type_hints(Test.foo))
$ python3.12 no_695_pep_563.py 
{'arg': ~M, 'return': <class 'NoneType'>}
import typing

M = typing.TypeVar("M")

class Test(typing.Generic[M]):
    def foo(self, arg: M) -> None:
        pass

print(typing.get_type_hints(Test.foo))
$ python3.12 no_695_no_563.py 
{'arg': ~M, 'return': <class 'NoneType'>}

This happens on both 3.12.5 and 3.13.0rc2

CPython versions tested on:

3.12, 3.13

Operating systems tested on:

Linux

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.