7

In my code I have

X_DEFAULT = ['a', 'long', 'list', 'of', 'values', 'that', 'is', 'really', 'ugly', 'to', 'see', 'over', 'and', 'over', 'again', 'every', 'time', 'it', 'is', 'referred', 'to', 'in', 'the', 'documentation']

and later

def some_function(..., x=X_DEFAULT, ...):

so that in my Sphinx documentation, using (e.g., using .. autofunction::, etc.) I get the entire long and unwieldy value of X_DEFAULT expanded in the signature for some_function:

some_function(..., x=['a', 'long', 'list', 'of', 'values', 'that', 'is', 'really', 'ugly', 'to', 'see', 'over', 'and', 'over', 'again', 'every', 'time', 'it', 'is', 'referred', 'to', 'in', 'the', 'documentation'], ...)

Is there a way to suppress this substitution in the generated documentation, ideally with a link back to the definition of X_DEFAULT:

some_function(..., x=X_DEFAULT, ...)


I'm aware that I can manually override the signature for each function and method that I explicitly list as arguments to Sphinx documentation directives, but that's not my goal here. I'm also aware that I could use autodoc_docstring_signature and the first line of the docstring, but that would produce bad docstrings, really intended for cases where introspection fails (like C). I suspect that there's something I could do in autodoc-process-signature that might be adequate (but not perfect), though I'm unsure how to proceed.

bad_coder
  • 11,289
  • 20
  • 44
  • 72
orome
  • 45,163
  • 57
  • 202
  • 418
  • Related: [Omit (or format) the value of a variable when documenting with Sphinx](http://stackoverflow.com/q/10861463/395760) –  Jan 22 '14 at 15:50
  • @delnan: I'm not sure how to use that for what I want. I've added an answer that's a start, but will happily accept a better one, or one that fixes the shortcomings with mine. – orome Jan 22 '14 at 21:06
  • 2
    There is now (Sphinx 4.0) a configuration option to handle this. See https://stackoverflow.com/a/67482867/407651. – mzjn Nov 06 '21 at 16:52

1 Answers1

1

One approach, that will, for example, "revert" substitution of all values that have been defined at the module level as "public constants" (recognized by all-caps names with no leading underscore) for which a unique name can be found in the module in which it was defined:

def pretty_signature(app, what, name, obj, options, signature, return_annotation):
    if what not in ('function', 'method', 'class'):
        return

    if signature is None:
        return

    import inspect
    mod = inspect.getmodule(obj)

    new_sig = signature
    # Get all-caps names with no leading underscore
    global_names = [name for name in dir(mod) if name.isupper() if name[0] != '_']
    # Get only names of variables with distinct values
    names_to_replace = [name for name in global_names
                        if [mod.__dict__[n] for n in global_names].count(mod.__dict__[name]) == 1]
    # Substitute name for value in signature, including quotes in a string value
    for var_name in names_to_replace:
        var_value = mod.__dict__[var_name]
        value_string = str(var_value) if type(var_value) is not str else "'{0}'".format(var_value)
        new_sig = new_sig.replace(value_string, var_name)

    return new_sig, return_annotation

def setup(app):
    app.connect('autodoc-process-signature', pretty_signature)

An alternative is to simply take the docstring directly from the source:

import inspect
import re

def pretty_signature(app, what, name, obj, options, signature, return_annotation):
    """Prevent substitution of values for names in signatures by preserving source text."""
    if what not in ('function', 'method', 'class') or signature is None:
        return

    new_sig = signature
    if inspect.isfunction(obj) or inspect.isclass(obj) or inspect.ismethod(obj):
        sig_obj = obj if not inspect.isclass(obj) else obj.__init__
        sig_re = '\((self|cls)?,?\s*(.*?)\)\:'
        new_sig = ' '.join(re.search(sig_re, inspect.getsource(sig_obj), re.S).group(2).replace('\n', '').split())
        new_sig = '(' + new_sig + ')'

    return new_sig, return_annotation
orome
  • 45,163
  • 57
  • 202
  • 418
  • There are several problems with the first version that I think the second addresses: (1) it's messy and probably misusing things like `__dict__`, it (2) I'm not sure I'm really checking correctly and reliably for the in which the signature is defined (and for which it is being documented), (3) it doesn't handle substitution across modules (e.g. of a default for a named parameter that is defined in a module different from the one the signature is defined in), it (4) doesn't implement a Sphinx link to the definition of the constant, and (5) it doesn't handle class-level "constants". – orome Jan 28 '14 at 22:12
  • The second probably has (a) issues with RegEx, probably stumbles over source that includes comments or odd characters, and (c) may miss some things that I'm not aware of. – orome Jan 28 '14 at 22:20
  • I would accept as an answer anything that resolves these issues with either approach. – orome Jan 28 '14 at 22:20