sphinx-immaterial icon indicating copy to clipboard operation
sphinx-immaterial copied to clipboard

Support for `numpy.typing`

Open mhostetter opened this issue 2 years ago • 7 comments

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 avatar Jul 13 '23 19:07 mhostetter

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

mhostetter avatar Jul 13 '23 20:07 mhostetter

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?

jbms avatar Jul 13 '23 20:07 jbms

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

  • With no from __future__ import annotations and nothing special in conf.py I get the expanded Union.
  • Adding from __future__ import annotations doesn't help.
  • Adding just autodoc_type_aliases = {"npt.DTypeLike": "~numpy.typing.DTypeLike"} displays the hint as DTypeLike, but it doesn't hyperlink.
  • Adding just python_type_aliases = {"npt.DTypeLike": "~numpy.typing.DTypeLike"} displays the expanded Union.
  • Adding both autodoc_type_aliases = {"npt.DTypeLike": "~numpy.typing.DTypeLike"} and python_type_aliases = {"npt.DTypeLike": "~numpy.typing.DTypeLike"} displays the hint as DTypeLike, but it doesn't hyperlink.

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

mhostetter avatar Jul 13 '23 20:07 mhostetter

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'>"]

mhostetter avatar Jul 13 '23 23:07 mhostetter

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 avatar Jul 13 '23 23:07 jbms

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.

jbms avatar Jul 13 '23 23:07 jbms

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!

mhostetter avatar Jan 28 '24 20:01 mhostetter