sphinx-doc / sphinx

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

LaTeX writer imposes too many constraints on desc_type_parameter_list nodes #12543

Closed jbms closed 1 month ago

jbms commented 1 month ago

Describe the bug

The LaTeX writer support added in https://github.com/sphinx-doc/sphinx/pull/11444 imposes the requirement that any desc_type_parameter_list node is immediately followed by a desc_parameterlist node.

This is not satisfied with the following:

.. py:class:: Foo[T]

   Some class.

The workaround is:

.. py:class:: Foo[T]()

   Some class.

Additionally, in the sphinx_immaterial theme, I'm adding support for type parameters to our apigen extension: https://github.com/jbms/sphinx-immaterial/pull/366

See example here: https://sphinx-immaterial--366.org.readthedocs.build/en/366/python_apigen_demo.html#type-parameter-demo

In particular, the apigen extension documents class members "out of line" on a separate page: https://sphinx-immaterial--366.org.readthedocs.build/en/366/python_apigen_generated/Map.items.html

For members of a class with type parameters, the extension inserts an additional type parameter list after the class name but before the .member suffix:

type_param_demo.Map[K, V].items() -> ItemsView[K, V]

If the method itself also has type parameters, then there will be both the type parameter list for the parent class and the type parameter list for the method itself:

https://sphinx-immaterial--366.org.readthedocs.build/en/366/python_apigen_generated/Map.get.html

type_param_demo.Map[K, V].get(key: K) → V | None
type_param_demo.Map[K, V].get(key: K, default: V) → V
type_param_demo.Map[K, V].get[T](key: K, default: T) → V | T

While this syntax is not standard Python syntax, and can only be generated via an extension to the Sphinx python domain, in my opinion this is very convenient syntax for clear documentation.

It would be nice if the LaTeX writer could be made to support this extension. Given that the C++ domain, for example, can generate incredibly complex signatures with multiple template parameter lists, template arguments at various positions, etc. all without any additional constraints imposed by the LaTeX writer, it would be nice if the LaTeX writer could somehow avoid imposing constraints on desc_type_parameter_list.

In the meantime, it seems like the easiest workaround will be to replace the desc_type_parameter_list nodes with other nodes.

@jfbu @picnixz

How to Reproduce

N/A

Environment Information

N/A

Sphinx extensions

No response

Additional context

No response

picnixz commented 1 month ago

Wait. The HTML writer works but not the LaTeX one?

jbms commented 1 month ago

Wait. The HTML writer works but not the LaTeX one?

Yes, the HTML writer handles the following correctly, but the LaTeX one does not.

.. py:class:: Foo[T]

   Some class.

The latex writer fails with the assertion here: https://github.com/sphinx-doc/sphinx/blob/41ee3be2cc180b182d15b050fa9d171baa2ddc6e/sphinx/writers/latex.py#L736

picnixz commented 1 month ago

I remember now why I had that assertion (sorry for that btw). It was to simplify my life because without (), macros would need to be called differently. I'll need to fix that part (I'll do it tomorrow when I have access to my projects).

(At least, I know why I had that assertion but my premises were wrong. I'll also need to check whether the regex is indeed correct for that since my comment tells me that the regex is incorrect otherwise...)

picnixz commented 1 month ago

Ok, so the main issue is that the doctree for

type_param_demo.Map[K, V].get(key: K) → V | None

will have multiple desc_type_parameter_list nodes but not all of them will be followed by what I expect, right?

I observed that there are missing tests and that the assertion on the LaTeX builder is incorrect. However, I don't think this kind of node should be used wherever you want since it uses a specific LaTeX macro for rendering the objects on multiple lines.

Instead, the extension should use another kind of node so that the visitor is not triggered. Currently, the visitor simply closes the name argument block and then injects the parameters to be injected. If you use a desc_type_parameter_list for [K, V], then the issue is that it would assume that what follows is the list of arguments, but this is not correct at all (maybe I misunderstood the issue).