fastai / nbdev

Create delightful software with Jupyter Notebooks
https://nbdev.fast.ai/
Apache License 2.0
4.88k stars 489 forks source link

`show_doc` errors on Python 3.10 union sub-type hints #1179

Open warner-benjamin opened 1 year ago

warner-benjamin commented 1 year ago

show_doc errors out when using Python 3.10 union sub-type hints. This occurs with typing.List List[Tensor|int] or list list[Tensor|int]

Minimal reproducible code and error:

from __future__ import annotations
from nbdev.showdoc import *

def test(l:list[float|int]):
    return l

show_doc(test)

Error:

Traceback (most recent call last):

  Cell In [3], line 1
    show_doc(test)

  File ~/python3.9/site-packages/nbdev/showdoc.py:191 in show_doc
    else:return renderer(sym or show_doc, name=name, title_level=title_level)

  File ~/python3.9/site-packages/nbdev/showdoc.py:127 in __init__
    try: self.sig = signature_ex(sym, eval_str=True)

  File ~/python3.9/site-packages/fastcore/basics.py:350 in signature_ex
    ann = type_hints(obj)

  File ~/python3.9/site-packages/fastcore/basics.py:318 in type_hints
    return {k:_eval_type(v,glb,loc) for k,v in ann.items()}

  File ~/miniconda3/envs/fastai/lib/python3.9/site-packages/fastcore/basics.py:318 in <dictcomp>
    return {k:_eval_type(v,glb,loc) for k,v in ann.items()}

  File ~/python3.9/site-packages/fastcore/basics.py:311 in _eval_type
    res = eval_type(t, glb, loc)

  File ~python3.9/site-packages/fastcore/basics.py:304 in eval_type
    if '|' in t: return Union[eval_type(tuple(t.split('|')), glb, loc)]

  File ~/python3.9/site-packages/fastcore/basics.py:306 in eval_type
    if isinstance(t,(tuple,list)): return type(t)([eval_type(c, glb, loc) for c in t])

  File ~/python3.9/site-packages/fastcore/basics.py:306 in <listcomp>
    if isinstance(t,(tuple,list)): return type(t)([eval_type(c, glb, loc) for c in t])

  File ~/python3.9/site-packages/fastcore/basics.py:305 in eval_type
    return eval(t, glb, loc)

  File <string>:1
    list[float
              ^
SyntaxError: unexpected EOF while parsing

However, using optional list[Optional[float]] or union Union list[Union[float, int]]works.

from typing import Union
from nbdev.showdoc import *

def test(
    l:list[Union[float, int]] # test comment
):
    return l

show_doc(test)

Using user-defined generic types renders the table, but displays list[T] as the type, not list[int, float] (or something similar).

from typing import TypeVar
from nbdev.showdoc import *

T = TypeVar('T', int, float)

def test(
    l:list[T] # test comment
):
    return l

show_doc(test)
seeM commented 1 year ago

Here's a minimal repro:

from fastcore.basics import eval_type
eval_type('list[float|int]', {}, {})

The issue is that fastcore's backport of py310-style annotations (e.g. float|int) doesn't support generic types, see this line: https://github.com/fastai/fastcore/blob/86337bad16a65f23c5335286ab73cd4d6425c586/fastcore/basics.py#L304

seeM commented 1 year ago

I wonder if we should be evaluating types for show_doc at all? If we don't, we would avoid https://github.com/fastai/nbdev/issues/1116 too. cc @jph00

jph00 commented 1 year ago

It would be worth trying removing eval and seeing how that changes, for instance, the fastai and nbdev docs