93

I have a module, errors.py in which several global constants are defined (note: I understand that Python doesn't have constants, but I've defined them by convention using UPPERCASE).

"""Indicates some unknown error."""
API_ERROR = 1

"""Indicates that the request was bad in some way."""
BAD_REQUEST = 2

"""Indicates that the request is missing required parameters."""
MISSING_PARAMS = 3

Using reStructuredText how can I document these constants? As you can see I've listed a docstring above them, but I haven't found any documentation that indicates to do that, I've just done it as a guess.

skyler
  • 8,010
  • 15
  • 46
  • 69
  • 1
    What do you mean by "how can I document these constants?". Adding comments (or strings in your case) already is a form of documentation. Are you asking how you can get sphinx-autodoc to recognize your internal documentation and display it in sphinx's output? – mgilson Nov 26 '13 at 20:26
  • See also [numpydoc: documenting constants](https://numpydoc.readthedocs.io/en/latest/format.html#documenting-constants) – gerrit Dec 17 '18 at 15:44

8 Answers8

80

Unfortunately, variables (and constants) do not have docstrings. After all, the variable is just a name for an integer, and you wouldn't want to attach a docstring to the number 1 the way you would to a function or class object.

If you look at almost any module in the stdlib, like pickle, you will see that the only documentation they use is comments. And yes, that means that help(pickle) only shows this:

DATA
    APPEND = b'a'
    APPENDS = b'e'
    …

… completely ignoring the comments. If you want your docs to show up in the built-in help, you have to add them to the module's docstring, which is not exactly ideal.


But Sphinx can do more than the built-in help can. You can configure it to extract the comments on the constants, or use autodata to do it semi-automatically. For example:

#: Indicates some unknown error.
API_ERROR = 1

Multiple #: lines before any assignment statement, or a single #: comment to the right of the statement, work effectively the same as docstrings on objects picked up by autodoc. Which includes handling inline rST, and auto-generating an rST header for the variable name; there's nothing extra you have to do to make that work.


As a side note, you may want to consider using an enum instead of separate constants like this. If you're not using Python 3.4 (which you probably aren't yet…), there's a backport.enum package for 3.2+, or flufl.enum (which is not identical, but it is similar, as it was the main inspiration for the stdlib module) for 2.6+.

Enum instances (not flufl.enum, but the stdlib/backport version) can even have docstrings:

class MyErrors(enum.Enum):
    """Indicates some unknown error."""
    API_ERROR = 1

    """Indicates that the request was bad in some way."""
    BAD_REQUEST = 2

    """Indicates that the request is missing required parameters."""
    MISSING_PARAMS = 3

Although they unfortunately don't show up in help(MyErrors.MISSING_PARAMS), they are docstrings that Sphinx autodoc can pick up.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • 9
    "After all, the variable is just a name for an integer" -- and a function name is a variable that is just a name for a function, a class name is a variable that is just a name for a class... And functions and classes internally are just some big integers... – Alexey May 12 '18 at 17:19
  • 2
    @Alexey I think you missed the point of that sentence. Function names _don’t_ have docstrings—function _values_ do. And you wouldn’t want to give the number 1 a docstring saying `API_ERROR`. – abarnert May 12 '18 at 21:09
  • 2
    Well, but the OP's question was about documenting "constants", whose values can be functions, or classes, or anything. It is a bit of an artificial restriction to allow documenting values of one type but forbid documenting values of another type, but it is not completely unreasonable. However, Python does not seem to have a convenient mechanism to document any values, including functions and classes, if they are defined using `VAL =` syntax. Consider `MyClass = namedtuple("MyClass", ("foo", "bar"))`. – Alexey May 12 '18 at 22:27
  • @Alexey I think you're still missing the key distinction between variables and values. Python _does_ have a convenient mechanism to document values. In your example, you can just write `MyClass.__doc__ = "MyClass does my things"`. In a more typical example, you just put the docstring in the definition. But either way, you're documenting the _value_, not the _variable_. If you write `MyOtherClass = MyClass`, it will have the same docstring (still referring to `MyClass`), because it's still the same value. – abarnert May 12 '18 at 22:39
  • @Alexey And that's exactly the issue here: it usually makes sense to document a class value, function value, or module value. It doesn't make sense to document an int value like `1`. That class object has some specific meaning, no matter what name you give it; that number just means the number 1, and the additional meanings you give it by giving it more names aren't part of being the number 1. – abarnert May 12 '18 at 22:40
  • 1
    I did not count `MyClass.__doc__ =` as a "convenient" mechanism because it requires mentioning the documented object and manipulating its `__doc__` attribute explicitly, and feels quite different from putting documentation in comments, but maybe i am picky... – Alexey May 13 '18 at 08:16
  • @Alexey If you really want a namedtuple with anything more than the basics, the usual answer is inheritance: `class MyClass(namedtuple('MyClass, ("foo", "bar")):`. (In 3.7+, `dataclass` may or may not be a better answer…) You use `MyClass = namedtuple(…)` when you want something trivial. If you want to use it and add on to `MyClass` anyway, you can, and Python makes that possible, but it doesn't go out of its way to make uncommon things easy. But you're still missing the main point: either way, `MyClass`—the class object as a value—is what has documentation, not the variable you assign it to. – abarnert May 13 '18 at 08:20
  • 1
    `namedtuple` was just one example. There is nothing unreasonable in principle in constructing functions and classes as return values of functions, and in wanting to document them. – Alexey May 13 '18 at 08:25
  • @Alexey Sure, but the simple way to do that is to have a `class` statement in the function that creates the type. Sure, sometimes you want to instead, e.g., call `type` or another metaclass directly, and you can do that, but it's not surprising, or a problem, that a few things are less convenient if you do. And meanwhile, I don't see how any of this has anything to do with this question or answer. If you're just looking to argue with someone about the design of Python, there are better places to do it. – abarnert May 13 '18 at 08:39
  • The enum sample is wrong. Docstrings follow the constants, not precede them. – 0xF Feb 14 '20 at 09:03
46

If you put a string after the variable, then sphinx will pick it up as the variable's documentation. I know it works because I do it all over the place. Like this:

FOO = 1
"""
Constant signifying foo.

Blah blah blah...
"""  # pylint: disable=W0105

The pylint directive tells pylint to avoid flagging the documentation as being a statement with no effect.

Louis
  • 146,715
  • 28
  • 274
  • 320
  • 1
    and you can use this new annotation=> .. autodata:: FOO :annotation: ... see http://sphinx-doc.org/ext/autodoc.html#directive-autoattribute – macm Jun 18 '14 at 21:44
  • Updated link (@macm) https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#directive-autoattribute - see example described in the docs which shows all 3 possible variants of docstrings. – E. Körner Jan 02 '21 at 16:51
28

This is an older question, but I noted that a relevant answer was missing.

Or you can just include a description of the constants in the docstring of the module via .. py:data::. That way the documentation is also made available via the interactive help. Sphinx will render this nicely.

"""
Docstring for my module.

.. data:: API_ERROR

    Indicates some unknown error.

.. data:: BAD_REQUEST

    Indicates that the request was bad in some way.

.. data:: MISSING_PARAMS

    Indicates that the request is missing required parameters.
"""
Kraigolas
  • 5,121
  • 3
  • 12
  • 37
22

You can use hash + colon to document attributes (class or module level).

#: Use this content as input for moo to do bar
MY_CONSTANT = "foo"

This will be picked up by some document generators.

An example here, could not find a better one: Sphinx document module properties

STJ
  • 1,478
  • 7
  • 25
Mikko Ohtamaa
  • 82,057
  • 50
  • 264
  • 435
  • Is the @ecatmur's answer is the example for the Sphinx document module properties link? – alper Aug 27 '21 at 14:14
6

the following worked for me with Sphinx 2.4.4:

in foo.py :

API_ERROR = 1
"""int: Indicates some unknown error."""

then to document it:

.. automodule:: foo.py 
    :members:

JDG
  • 477
  • 5
  • 6
2

I think you're out of luck here.

Python don't support directly docstrings on variables: there is no attribute that can be attached to variables and retrieved interactively like the __doc__ attribute on modules, classes and functions.

Source.

johnsyweb
  • 136,902
  • 23
  • 188
  • 247
  • 3
    But, if OP is just trying to document them for the sake of sphinx, I believe that the `#:` used by epydoc was reused in the sphinx parser. – mgilson Nov 26 '13 at 20:22
  • @mgilson: Ah, then maybe the linked documentation will help ☺︎ – johnsyweb Nov 26 '13 at 20:24
2

The Sphinx Napoleon Python documentation extension allows to document module-level variables in an Attributes section.

Per https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_numpy.html :

Attributes
----------
module_level_variable1 : int
    Module level variables may be documented in either the ``Attributes``
    section of the module docstring, or in an inline docstring immediately
    following the variable.

    Either form is acceptable, but the two should not be mixed. Choose
    one convention to document module level variables and be consistent
    with it.
myselfhimself
  • 559
  • 5
  • 12
1

Writing only because I haven't seen this option in the answers so far:

You can also define your constants as functions that simply return the desired constant value when called, so for example:

def get_const_my_const() -> str:
    """Returns 'my_const'."""
    return "my_const"

This way they'll be a bit "more constant" on one hand (less worrying about reassignment) and they'll also provide the opportunity for regular documentation, as with any other function.

Gábriel
  • 155
  • 7
  • 2
    Folks are downvoting, but it's certainly a reasonable alternative. I think it would be cleaner without the `get_const_` prefix, though... If Python also had module-level properties, that would probably be another viable alternative. – Mateen Ulhaq Aug 19 '20 at 05:50