jbms / sphinx-immaterial

Adaptation of the popular mkdocs-material material design theme to the sphinx documentation system
https://jbms.github.io/sphinx-immaterial/
Other
177 stars 28 forks source link

Support for `numpy.typing` #270

Open mhostetter opened 11 months ago

mhostetter commented 11 months ago

Hey guys. Hope you're well.

I was adding a type annotation to a function that is npt.DTypeLike, see here. However, when displayed with sphinx-immaterial the underlying Union is unpacked. Is there a way to only display npt.DTypeLike in our API docs?

from typing import Optional
import numpy as np
import numpy.typing as npt

def pack(x: np.ndarray, bpe: int, dtype: Optional[npt.DTypeLike] = None) -> np.ndarray:
    return x

image

mhostetter commented 11 months ago

I found this https://stackoverflow.com/a/67483317/11694321. It says to add from __future__ import annotations to the top of the module to add this to conf.py.

autodoc_type_aliases = {
    "npt.DTypeLike": "~numpy.typing.DTypeLike",
}

This now give me the following. It is almost perfect. It is succinct, but the pink DTypeLike doesn't hyperlink (even though the yellow one below does, using :obj:numpy.typing.DTypeLike).

image

jbms commented 11 months ago

Do you get any warnings?

I would expect this to work. This theme also has its own type alias mechanism that applies to the Python domain generally (not just to autodoc-generated things):

https://jbms.github.io/sphinx-immaterial/apidoc/python/index.html#confval-python_type_aliases

You could try using that instead. I believe the autodoc_type_aliases only apply in certain cases (and not, for example, when using text signatures as obtained from things defined by C extensions), but I would expect them to apply in this case.

If you don't specify the autodoc_type_aliases, do you get the same thing except it shows npt.DTypeLike?

mhostetter commented 11 months ago

I got no warnings. I'm including a reproducible example. foo-270.zip

I'm using Sphinx v5.3.0 and Sphinx Immaterial v0.11.5 in Python 3.8.10.

mhostetter commented 11 months ago

This may be another clue... Using the autodoc_type_aliases trick above, the following give these outputs.

Acceptable

def unpack(x: np.ndarray, bpe: int, dtype: npt.DTypeLike) -> np.ndarray:

image

Something's wrong

def unpack(x: np.ndarray, bpe: int, dtype: npt.DTypeLike = np.uint8) -> np.ndarray:

image

And I now get these warnings.

/mnt/c/Users/matth/repos/sdr/<python_apigen_rst_epilog>:2: WARNING: Parameter name 'x' does not match any of the parameters defined in the signature: ['x: ~numpy.ndarray', 'bpe: int', "dtype: ~numpy.typing.DTypeLike = <class 'numpy.uint8'>"]
/mnt/c/Users/matth/repos/sdr/<python_apigen_rst_epilog>:2: WARNING: Parameter name 'bpe' does not match any of the parameters defined in the signature: ['x: ~numpy.ndarray', 'bpe: int', "dtype: ~numpy.typing.DTypeLike = <class 'numpy.uint8'>"]
/mnt/c/Users/matth/repos/sdr/<python_apigen_rst_epilog>:2: WARNING: Parameter name 'dtype' does not match any of the parameters defined in the signature: ['x: ~numpy.ndarray', 'bpe: int', "dtype: ~numpy.typing.DTypeLike = <class 'numpy.uint8'>"]
jbms commented 11 months ago

The default value issue is the same as https://github.com/jbms/sphinx-immaterial/issues/140. That makes the signature unparsable as an ast, which causes sphinx to fall back to a more heuristic parsing which prevents a lot of things from working.

jbms commented 11 months ago

I looked into this --- the issue is that numpy.typing.DTypeLike is not a class object but a data object, but Sphinx has hardcoded that the type annotation xrefs use the class role:

https://github.com/sphinx-doc/sphinx/blob/d3c91f951255c6729a53e38c895ddc0af036b5b9/sphinx/domains/python.py#L100

There is room for a lot of improvement in that function.

Separately, I'd suggest always building with nitpicky = True in your config, otherwise it is too easy to miss invalid references. You can silence expected missing references with nitpick_ignore.

mhostetter commented 5 months ago

I'm running into this again. This function (no from __future__ import annotations)

def db(
    x: npt.ArrayLike,
    type: Literal["value", "power", "voltage"] = "value",
) -> npt.NDArray[np.float_]:

is rendered like this, which is borderline unreadable.

image

Any thoughts on ways to work around this? I'd really like to use native NumPy types, where possible. Thanks again for all the great work on this theme!