sphinx-doc / sphinx

The Sphinx documentation generator
https://www.sphinx-doc.org/
Other
6.61k stars 2.13k forks source link

`automodule` does not work with `TYPE_CHECKING` guarded code #13137

Open LecrisUT opened 5 days ago

LecrisUT commented 5 days ago

Describe the bug

I want to document TypeAlias variables which are typically guarded by if TYPE_CHECKING or they might be defined in a stub file. The issue is that these members are not automatically added and indexed by automodule with members. If I move the section to outside the TYPE_CHECKING if-guard, then the member is being documented.

How to Reproduce

from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from typing import TypeAlias

    ComplexType: TypeAlias = int | str
    """This has some documentation"""

def some_func(input: ComplexType) -> None:
    """Some function that uses the type alias"""

Environment Information

Platform:              linux; (Linux-6.11.7-300.fc41.x86_64-x86_64-with-glibc2.40)
Python version:        3.13.0 (main, Oct  8 2024, 00:00:00) [GCC 14.2.1 20240912 (Red Hat 14.2.1-3)])
Python implementation: CPython
Sphinx version:        8.1.3
Docutils version:      0.21.2
Jinja2 version:        3.1.4
Pygments version:      2.18.0

Sphinx extensions

extensions = [
    "myst_parser",
    "sphinx.ext.intersphinx",
    "sphinx_tippy",
    "sphinx.ext.autodoc",
    "sphinx_autodoc_typehints",
]

Additional context

No response

timhoffm commented 5 days ago

This is to be expected: autodoc introspects the module by importing them. This is done in a regular python run, in which case typing.TYPE_CHECKING is False. Therefore, your TypeAlias definition is not executed and thus does not exist. Consequently autodoc cannot see it.

I've not tested it, but you could try to adding

import typing
typing.TYPE_CHECKING = True

to your Sphinx conf.py. That should make your alias visible, but be aware that it cancels the effect of the TYPE_CHECKING variable and the guards it has put in place not only for your code but also for all the downstream dependencies you import.

LecrisUT commented 5 days ago

I've tested it out a bit and it breaks on sphinx eources even, so it seems a more sophisticated solution is needed. Is it possible to interogate typing for what members it sees in __dict__, etc. and try to union the results?

timhoffm commented 5 days ago

typing is just a module. It doesn’t see anything. autodoc uses ordinary runtime introspection. It has no means to get information from a block that’s excluded from running through an effective if False statement. The only way to get that info would be statically parsing the AST.

You could try to unhide your specific location only by something like

if TYPE_CHECKING or “sphinx.ext.autodoc” in sys.modules:
    …

(Note: This is an idea, not tested because I don’t have a system to test at hand right now)

LecrisUT commented 5 days ago

The or approach indeed worked. I guess one approach would be to patch in a context TYPE_CHECKING. Maybe this still fails when there are circular dependencies though :thinking:.

I was thinking of typing.reveal_type or typing.get_type_hints, but I've tried those and that didn't help. I've also tried PEP695, but that does not have any support atm it seems. Static parsing would be nice and there are sphinx-autoapi and sphinx-autodoc2 that do the static documentation building, but these would not support well more complex objects like attrs that can dynamically alter the docstring. It would be great if these methods could be combined, having 2 passes complementing each other's documentations

AA-Turner commented 4 days ago

@LecrisUT does setting SPHINX_AUTODOC_RELOAD_MODULES=1 work? It's a new environment variable in Sphinx 7.2.

A

LecrisUT commented 4 days ago

@AA-Turner I supose you mean in reference to setting typing.TYPE_CHECKING=True. That didn't seem to work:

$ SPHINX_AUTODOC_RELOAD_MODULES=1 sphinx-build docs/ docs/_build
Running Sphinx v8.1.3
loading translations [en]... done

Extension error:
Could not import extension sphinx.builders.changes (exception: No module named 'typing_extensions')

Installing typing-extensions did not help, it simply delayed the error a bit further.

AA-Turner commented 4 days ago

I imagine this is as you use typing_extensions in TYPE_CHECKING blocks. What was the second error?

A

LecrisUT commented 4 days ago

Second attempt is trickier:

$ SPHINX_AUTODOC_RELOAD_MODULES=1 sphinx-build docs/ docs/_build
Running Sphinx v8.1.3
loading translations [en]... done

Extension error:
Could not import extension sphinx.builders.epub3 (exception: cannot import name '_ReadableStream' from 'sphinx.util.typing' (/home/lecris/PycharmProjects/fmf-jinja/venv/lib/python3.13/site-packages/sphinx/util/typing.py))
AA-Turner commented 4 days ago

Can you provide a full reproducer? It shouldn't be reloading internal modules (such as sphinx.builders.epub3), I think.

A

LecrisUT commented 4 days ago

The project I'm working on is at: https://github.com/LecrisUT/fmf-jinja/tree/fix/use-upstream-fmf

That branch lacks the typing.TYPE_CHECKING change in conf.py, but it should be straight forward to add for a reproduction. The test item is in `fmf_jinja.utils

timhoffm commented 4 days ago

@AA-Turner I've seen the changelog entry for SPHINX_AUTODOC_RELOAD_MODULES. However, from that I've not been able to understand when it should be used and what it does exactly. There's no mentioning of it in the regular docs.

I also feel this is a bit inflexible because it's a global variable. Does it only affect the inspected module or may it propagate to modules that are imported in there as well? Even if it only affects the imports for autodoc, it affects all the documented modules. Extending the if TYPE_CHECKING: guard to if TYPE_CHECKING or [sphinx autodoc]: seems like the more flexible approach. how exactly or [sphinx autodoc] is spelled out can be discussed or “sphinx.ext.autodoc” in sys.modules was a quick guess.

LecrisUT commented 4 days ago

Extending the if TYPE_CHECKING: guard to if TYPE_CHECKING or [sphinx autodoc]: seems like the more flexible approach

Preferably there would be a different approach. This method:

electric-coder commented 3 days ago

@timhoffm

Extending the if TYPE_CHECKING: guard to if TYPE_CHECKING or [sphinx autodoc]: seems like the more flexible approach.

This doesn't make any sense, you are not supposed to do invasive changes to the py source for autodoc to work. (Many previous contributors have addressed this -there are workarounds available that get the job done, with some digging in the knowledge base they can be found- and none of them saw the need to change the TYPE_CHECKING clause itself.)

TypeAlias itself isn't currently supported as far as I can tell (wait a minute! I checked one of my projects and I got it working since at least Sphinx 7.2, I published a description of the workaround that several others built on, it's somewhere in an open issue here in the Sphinx repo) what's more there are about 10 outstanding type alias bugs not counting the specific relation between TYPE_CHECKING and how the imports are done, see #9813 - that's to say that currently the prevalent workaround is to use strings instead of the bare type when using the alias.

By all accounts this bug report is a duplicate.