sphinx-doc / sphinx

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

Add a filename argument to the math directive #13028

Open electric-coder opened 1 week ago

electric-coder commented 1 week ago

Is your feature request related to a problem? Please describe.

The only easy way to place the math directive exactly where I want using autodoc is to include it in the docstring, I can't put the math in the .rst without it being misplaced relative to the rendered docstring info field lists. This makes the docstrings/modules too long and, also, using an IDE it's hard to get syntax highlight/bracket matching/code completion and other niceties if editing the MathJax code in the directive body inside a .py docstring or .rst file.

Describe the solution you'd like

I'd like the math directive to have a filename argument like the literalinclude directive does.

Something that would allow (here the .tex containing only "math mode" MathJax code):

.. math:: my_mathjax_file.tex
    ..

or

class A:
    """Description line.

    .. math:: my_mathjax_file.tex
        ..
    """

Describe alternatives you've considered

Currently I place the math directive in a separate .rst file and include it using the reST include directive, e.g.

class A:
    """Description line.

    .. include:: graphs/my_mathjax_file.rst
        ..
    """

with graphs/my_mathjax_file.rst having the math directive at the top:

.. math::
    % your MathJax code here

I also tried using .. include:: inside .. math:: to only include math mode Latex but that didn't work:

class A:
    """Description line.

    .. math::

        .. include:: graphs/my_mathjax_file.tex
            ..
    """

The current state of things has a number of drawbacks:

  1. Triggers #9707 .
  2. The file being included has to be an .rst since .. math:: must be declared in the file being included. (Just including tex source directly in the math directive doesn't render it.)
  3. The above point makes it a lot harder to use IDE Latex specific utilities, since now the file is an .rst instead of a .tex (placing it in a .py has the same problem).
  4. You necessarily mix languages, having the filename argument would make it much easier to separate py, rst and tex.

I considered a number of extensions like sphinx-math-dollar but so far none seem to provide this exact functionality. (Keeping within sphinx.ext is also generally desirable.)

timhoffm commented 1 week ago

What you are not explicitly writing, but reads between the lines is that you have very long equations. Otherwise, inlining should be sufficient.

I'm wondering whether there's a real need here. Math up to a certain length should be ok to include in the docstring directly. If it's significantly longer, why is it ok to show this long math in the rendered docstring but not in the source? Wouldn't it be better to link to a separate page describing the math? Usually such explanations also profit form interspersing explanatory text and formulas. Do you have an example what you want to achieve?

If we're going to take equations from tex files, I believe we'd want a separate .. mathinclude:: directive like we have .. literalinclude::. I'm not aware of directives that can optionally take their content from a :filename: parameter.

electric-coder commented 1 week ago

I'm not aware of directives that can optionally take their content from a :filename: parameter.

Let me try and list them to get an an overview, general directive syntax:

.. directive:: argument
   :option:
   content
  1. From the reST spec

Also as an option, not an argument :path: is listed in Common Option Value Types.


  1. From Sphinx

So from the overview most times the file or filename are directive arguments but they can also be an option. It would also make sense to take advantage of the Common Option Value.

why is it ok to show this long math in the rendered docstring but not in the source?

An equation taking up 1 single line in the rendered docs can easily take 50-100 lines in the source, depending on its complexity and how you prettify the Latex source. The 80 char PEP 8 line length limit makes it even harder because you'll likely require line breaks within a complex mixed language block.

Wouldn't it be better to link to a separate page describing the math?

Having equations in the exact documentation scope where a Python object implements it is optimal.

Do you have an example what you want to achieve?

Here's an MRE taster (it's not very noticeable here on GitHub but there are visual differences in the rst and tex source fences). It's unlikely that a latex-in-rst language injection feature be supported by an IDE, but for me PyCharm paints all the embedded latex in a single color:

.. math::
    :label: label here

    \underbrace{
        \underbrace{
            \underbrace{
                \mbox{inner }
                    \mathrlap{
                    \overbrace{
                        \mathrlap{
                        \overbrace{
                            \phantom{
                            \mbox{- most}}
                        }^{in\_right}}
                        \phantom{
                            \mbox{- most}
                            \mbox{ + Segment2}
                            \mbox{ + Sub-Segment-3}
                            \mbox{ + Sub-Segment-4}}
                    }^{\mbox{Starting from the other side}}}
                \mbox{- most}
                \mathrlap{
                    \phantom{\mbox{ + }}
                    \overbrace{
                        \phantom{\mbox{Segment2}}
                    }^{\mbox{a tad}}
                    \phantom{\mbox{ + }}
                    \overbrace{
                        \phantom{\mbox{Sub-Segment-3}}
                    }^{\href{
                        #HONKING_LONG.attr1}
                        {\mbox{attr1}}}
                    \phantom{\mbox{ + }}
                    \overbrace{
                        \phantom{\mbox{Sub-Segment-4}}
                    }^{\href{
                        #HONKING_LONG.attr2}
                        {\mbox{attr2}}}
                }
            }_{\mathrm{innermost}}\mbox{ + Segment2}
        }_{\mathrm{Just\ a\ tad\ more}}\mbox{ + Sub-Segment-3 + Sub-Segment-4}
    }_{\mathrm{The\ friggin\ big\ picture}}

Should instead be the_mathjax.tex:

\underbrace{
    \underbrace{
        \underbrace{
            \mbox{inner }
                \mathrlap{
                \overbrace{
                    \mathrlap{
                    \overbrace{
                        \phantom{
                        \mbox{- most}}
                    }^{in\_right}}
                    \phantom{
                        \mbox{- most}
                        \mbox{ + Segment2}
                        \mbox{ + Sub-Segment-3}
                        \mbox{ + Sub-Segment-4}}
                }^{\mbox{Starting from the other side}}}
            \mbox{- most}
            \mathrlap{
                \phantom{\mbox{ + }}
                \overbrace{
                    \phantom{\mbox{Segment2}}
                }^{\mbox{a tad}}
                \phantom{\mbox{ + }}
                \overbrace{
                    \phantom{\mbox{Sub-Segment-3}}
                }^{\href{
                    #HONKING_LONG.attr1}
                    {\mbox{attr1}}}
                \phantom{\mbox{ + }}
                \overbrace{
                    \phantom{\mbox{Sub-Segment-4}}
                }^{\href{
                    #HONKING_LONG.attr2}
                    {\mbox{attr2}}}
            }
        }_{\mathrm{innermost}}\mbox{ + Segment2}
    }_{\mathrm{Just\ a\ tad\ more}}\mbox{ + Sub-Segment-3 + Sub-Segment-4}
}_{\mathrm{The\ friggin\ big\ picture}}

How it would look in the .py:

class Middle(Top):
    r"""Intro.

    Attributes need a lengthy domain description and are documented separatly in the module rst file.
    To shorten the py as much as possible.
    I only keep Args in the docstring to take advantage of autodoc/napoleon without cluttering the modules.
    These are also convenient when scrolling the source to understand signatures in place,
    and with the current Sphinx limitations its the easiest way to solve type linking bugs,
    otherwise its more flexible to write in the rst. 

    .. include::

        \underbrace{
            \underbrace{
                \underbrace{
                    \mbox{inner }
                        \mathrlap{
                        \overbrace{
                            \mathrlap{
                            \overbrace{
                                \phantom{
                                \mbox{- most}}
                            }^{in\_right}}
                            \phantom{
                                \mbox{- most}
                                \mbox{ + Segment2}
                                \mbox{ + Sub-Segment-3}
                                \mbox{ + Sub-Segment-4}}
                        }^{\mbox{Starting from the other side}}}
                    \mbox{- most}
                    \mathrlap{
                        \phantom{\mbox{ + }}
                        \overbrace{
                            \phantom{\mbox{Segment2}}
                        }^{\mbox{a tad}}
                        \phantom{\mbox{ + }}
                        \overbrace{
                            \phantom{\mbox{Sub-Segment-3}}
                        }^{\href{
                            #HONKING_LONG.attr1}
                            {\mbox{attr1}}}
                        \phantom{\mbox{ + }}
                        \overbrace{
                            \phantom{\mbox{Sub-Segment-4}}
                        }^{\href{
                            #HONKING_LONG.attr2}
                            {\mbox{attr2}}}
                    }
                }_{\mathrm{innermost}}\mbox{ + Segment2}
            }_{\mathrm{Just\ a\ tad\ more}}\mbox{ + Sub-Segment-3 + Sub-Segment-4}
        }_{\mathrm{The\ friggin\ big\ picture}}

    Args:
        arg1 (str): Describe.
        arg2 (str): Describe.
    """

with your normal class in a module:

class Middle(Top):
    r"""Intro.

    Attributes need a lengthy domain description and are documented separatly in the module rst file.
    To shorten the py as much as possible.
    I only keep Args in the docstring to take advantage of autodoc/napoleon without cluttering the modules.
    These are also convenient when scrolling the source to understand signatures in place,
    and with the current Sphinx limitations its the easiest way to solve type linking bugs,
    otherwise its more flexible to write in the rst. 

    .. include:: graphs/the_mathjax.rst

    # Ideally should be:
    # .. math:: graphs/the_mathjax.tex
    # or 
    # .. math:: 
    #     :path: graphs/the_mathjax.tex

    Args:
        arg1 (str): Describe.
        arg2 (str): Describe.
    """

Gives:

GH1

From what I understand you generally write numpy style @timhoffman ? The above is a Google style example that uses full PEP 484 typed signatures and merges the constructor with the class declaration.

I don't know what a clear separation by language is called in CS theory? But notice that in the current state the math directive doesn't make it easy to cleanly create a separation of 1 language per file, which would be optimal.

I believe we'd want a separate .. mathinclude:: directive like we have .. literalinclude::

I'm not sure why there are separate include and literalinclude directives (maybe to enhance docutils without changing it?) But adding an argument or extra option having Sphinx encapsulate the reST math could make sense? I suppose making a judgement on this requires extensive knowledge about the internals and their logic.