mkdocstrings / pytkdocs

Load Python objects documentation.
https://mkdocstrings.github.io/pytkdocs
ISC License
50 stars 32 forks source link

[BUG] type unions with pipes not supported #140

Open analog-cbarber opened 2 years ago

analog-cbarber commented 2 years ago

I have code that subclasses a class in the bokeh library

from bokeh.application.handlers import Handler
from bokeh.server.server import Server

class ShutdownOnSessionDestroyed(Handler):
    """A bokeh application handler that will shutdown server when last session is closed.

    This is not normally what you want, but can be useful if you just want
    to debug one session or run a bokeh application from a single command
    line invocation.
    """
    def __init__(self, server:Server):
        self.server = server
        super().__init__()

    def modify_document(self, doc):
        pass

    async def on_session_destroyed(self, session_context):
        if len(self.server.get_sessions()) == 0:
            self.server.stop()

    def on_server_unloaded(self, server_context):
        sys.exit(0)

when I try to generate documentation for this using mkdocstrings 0.17 I get the following stack dump:

            Traceback (most recent call last):
              File "/Users/cbarber/miniconda3/envs/garpy.panel-dev/lib/python3.7/site-packages/pytkdocs/cli.py", line 205, in main
                output = json.dumps(process_json(line))
              File "/Users/cbarber/miniconda3/envs/garpy.panel-dev/lib/python3.7/site-packages/pytkdocs/cli.py", line 114, in process_json
                return process_config(json.loads(json_input))
              File "/Users/cbarber/miniconda3/envs/garpy.panel-dev/lib/python3.7/site-packages/pytkdocs/cli.py", line 91, in process_config
                obj = loader.get_object_documentation(path, members)
              File "/Users/cbarber/miniconda3/envs/garpy.panel-dev/lib/python3.7/site-packages/pytkdocs/loader.py", line 358, in get_object_documentation
                root_object = self.get_module_documentation(leaf, members)
              File "/Users/cbarber/miniconda3/envs/garpy.panel-dev/lib/python3.7/site-packages/pytkdocs/loader.py", line 426, in get_module_documentation
                root_object.add_child(self.get_class_documentation(child_node))
              File "/Users/cbarber/miniconda3/envs/garpy.panel-dev/lib/python3.7/site-packages/pytkdocs/loader.py", line 483, in get_class_documentation
                merge(attributes_data, get_class_attributes(parent_class))
              File "/Users/cbarber/miniconda3/envs/garpy.panel-dev/lib/python3.7/site-packages/pytkdocs/parsers/attributes.py", line 115, in get_class_attributes
                type_hints = get_type_hints(cls)
              File "/Users/cbarber/miniconda3/envs/garpy.panel-dev/lib/python3.7/typing.py", line 981, in get_type_hints
                value = _eval_type(value, base_globals, localns)
              File "/Users/cbarber/miniconda3/envs/garpy.panel-dev/lib/python3.7/typing.py", line 263, in _eval_type
                return t._evaluate(globalns, localns)
              File "/Users/cbarber/miniconda3/envs/garpy.panel-dev/lib/python3.7/typing.py", line 467, in _evaluate
                eval(self.__forward_code__, globalns, localns),
              File "<string>", line 1, in <module>
            TypeError: unsupported operand type(s) for |: 'type' and 'NoneType'

One issue here is that the output does not indicate what code caused the problem, so it took a little experimentation to figure out this came from this particular class.

The actual error is caused by bokeh's use of the new PEP 640 pipe syntax for union types even though their code does not require python 3.10:

class Handler:
    ''' Provide a mechanism for Bokeh applications to build up new Bokeh
    Documents.

    .. autoclasstoc::

    '''

    _failed: bool
    _error: str | None
    _error_detail: str | None
    _static: str | None

   ...

So strictly speaking bokeh should not be using those declarations but they have decided to do so anyway, so it would be nice to have a way to work around this without having to resort to building docs in a python 3.10 environment.

Perhaps code that calls get_type_hints should be prepared to catch and reasonably handle TypeErrors resulting from this kind of thing.

pawamoy commented 2 years ago

Similar to #139! I'll try to bring some kind of support for future annotations in pytkdocs as soon as I can :slightly_smiling_face:

analog-cbarber commented 2 years ago

Could you catch and ignore this as a temporary fix?