How do I reference a documented Python function parameter using Sphinx markup?

Question:

I’d like to reference a previously-documented function parameter elsewhere in a Python docstring. Consider the following (admittedly completely artificial) example:

def foo(bar):
    """Perform foo action
    :param bar: The bar parameter
    """

    def nested():
        """Some nested function that depends on enclosing scope's bar parameter.
        I'd like to reference function foo's bar parameter here
        with a link, is that possible?"""
        return bar * bar

    # ...
    return nested()

Is there a simple way to embed a parameter reference using Sphinx markup, or will this happen automagically?

(I’m a complete Sphinx newbie. I’ve been scanning the Sphinx docs and haven’t found an answer to this question, or an example demonstrating proper markup.)

Asked By: Inactivist

||

Answers:

If you are looking for a way to link directly to the bar definition of foo then your documentation is too lengthy or you are asking your reader to ignore the forest for one tree or some combination of the two.

Taking an example from defaultdict Examples:

Setting the :attr:`default_factory` to :class:`int` makes the
:class:`defaultdict` useful for counting (like a bag or multiset in other
languages):

if I can’t be bothered to read five sentences into collections.defaultdict to find the meaning of default_factory I probably don’t deserve to be lead there.

Note that the attribute reference syntax is the same as in the section above:

The first argument provides the initial value for the :attr:`default_factory`
attribute; it defaults to ``None``.

but it looks like Sphinx doesn’t reach out of the current section scope and so renders the later reference as styled text rather than as an anchor. It would not surprise me if this was intentional.

Answered By: msw

There is no simple way to get a direct reference to a parameter of a function with sphinx and I don’t know an extension for this problem.

The documentation of the python domain explains which objects can be cross referenced.

A possible way to give the user a reference to parameter bar of function foo would be

See parameter ``bar`` in :func:`foo`.

Maybe a direct reference would be possible by writing an extension.

Answered By: bmu

I’ve just built an extension to accomplish this task. So far it seems to be working with standalone HTML build and additionally with readthedocs (after some more tweaks).

the extension is available at: https://pypi.python.org/pypi/sphinx-paramlinks/.

I’m rolling it out right now for the Alembic and SQLAlchemy projects. (sample).

I take disagreement with the suggestion that linking to params means the docs are too lengthy. The Python standard library is a poor example here as stdlib functions are necessarily granular and simple. Software that is accomplishing a more coarse-grained task, where a single function rides on top of a complex problem to be solved, will often have parameters that require a lot more explanation; this explanation is often quite valuable as the solution to a particular problem elsewhere, and therefore being able to link to it is very important.

Answered By: zzzeek

For those, that want to use sphinx-paramlinks with sphinx.ext.napoleon here is a patch. Simple find the right fragment in the sphinx-paramlinks source code (sphinx_paramlinkssphinx_paramlinks.py, somewhere around line 50) and replace it with this:

def cvt(m):
    directive, modifier, objname, paramname = (
        m.group(1), m.group(2) or '', name, m.group(3))
    if directive == 'param':
        refname = _refname_from_paramname(paramname, strip_markup=True)
        item = ('single', '%s (%s parameter)' % (refname, objname),
                '%s.params.%s' % (objname, refname), '')
        if LooseVersion(__version__) >= LooseVersion('1.4.0'):
            item += (None,)
        doc_idx.append(item)
    return ":%s %s_sphinx_paramlinks_%s.%s:" % (
        directive, modifier, objname, paramname)
return re.sub(r'^:(param|type) ([^:]+? )?([^:]+?):', cvt, line)

Note: remember about the right indent.

I’m not a Sphinx specialist, but this seems to get the job done.

Answered By: Tomasz Kurgan

Not to say that sphinx-paramlinks is not a fine solution, but I’m a bit
stubborn about adding more extensions to my project.

You won’t get the benefit of the visual highlight, or an anchor icon, which is a bummer, but a reStructuredText hyperlink target before that section at least gets you close:

    def from_existing_id(cls, jobid, **kwargs):
        """Instantiates a new :class:`Job` object from an existing job ID.

        :param jobid: the ID of the previous job
        :param kwargs: keyword arguments supported by :meth:`deserialize`,
            *e.g.*, :ref:`ignore_missing <deserialize_params>`.
        """
        ⋮

    # elsewhere
    def deserialize(self, filename, copy_inputs=False, ignore_missing=False):
        """Reads a disk file into the current :class:`Job` object's config.
    
        .. _deserialize_params:

        :param filename: the filename to read from to disk
        :param copy_inputs: copy input files to output directory
        :param ignore_missing: don't bail if input files are missing
        """
        ⋮

The longer your parameter list gets, the less useful this becomes, though.

Answered By: TheDudeAbides
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.