pdoc icon indicating copy to clipboard operation
pdoc copied to clipboard

PEP 695 syntax breaks parsing of forward type reference

Open ncanceill opened this issue 1 year ago • 2 comments

Problem Description

Python 3.12 introduced PEP 695 Type Parameter Syntax. This seems to break type parsing for a forward type reference to a generic class in a method of another generic class:

class Foo[T]:
    def foo(self) -> "Bar[T]":  # this breaks!
        ...

class Bar[T]:
    def bar(self, _: T) -> None:
        ...

That is a valid Python 3.12 module, but running pdoc on it will result in: Error parsing type annotation Bar[T] for package.module.Foo.foo. Import of T failed: name 'T' is not defined

The generated HTML therefore does not have a link:

<span class="return-annotation">) -&gt; <span class="s1">'Bar[T]'</span>:</span>

Steps to reproduce the behavior:

Run pdoc on a package containing the example module above.

Note that using the old syntax does not break parsing, probably because it requires a TypeVar to be defined:

from typing import Generic, TypeVar

T = TypeVar("T")

class Foo(Generic[T]):
    def foo(self) -> "Bar[T]":  # this is fine
        ...

class Bar(Generic[T]):
    def bar(self, _: T) -> None:
        ...

Note also that the problem only occurs with a forward reference, insofar as the following example does not break parsing either:

class Foo[T]:
    def foo(self, _: T) -> None:
        ...

class Bar[T]:
    def bar(self) -> Foo[T]:  # this is fine
        ...

System Information

pdoc: 15.0.0
Python: 3.12.7
Platform: Linux-6.1.0-21-amd64-x86_64-with-glibc2.36

ncanceill avatar Nov 24 '24 09:11 ncanceill

This already breaks in stdlib:

class Foo[T]:
    def foo(self) -> "Bar[T]":
        ...

class Bar[T]:
    def bar(self, _: T) -> None:
        ...

if __name__ == "__main__":
    import typing
    typing.get_type_hints(Foo.foo)  # NameError: name 'T' is not defined

Contributions are welcome, I won't have capacity to look into this anytime soon. :)

mhils avatar Nov 25 '24 13:11 mhils

Thanks for checking!

Indeed, we get the same exception even with a specific type:

>>> get_type_hints(Foo[int].foo)
Traceback (most recent call last):
  ...
NameError: name 'T' is not defined

This seems part of a broader issue between PEP563 and PEP695: https://github.com/python/cpython/issues/124089

That should be fixed by the implementation of PEP649 in Python 3.14, which would bring new attributes to help with TypeVar and PEP695. See section Deferred evaluation of PEP 695 and 696 objects of PEP749 for more details.

This issue can be closed since Python 3.12 or 3.13 do not seem to support this typing style.

ncanceill avatar Dec 03 '24 15:12 ncanceill