How to put a variable into Python docstring

26,532

Solution 1

Triple-quoted strings are one big string. Nothing is evaluated inside them. The % part is all part of the string. You'd need to have it operating on the actual string.

def func(animalType):
    """
    This is a sample function.

    @param animalType: "It takes one of these animal types %(ANIMAL_TYPES)s"
    """ % {'ANIMAL_TYPES': ANIMAL_TYPES}

I'm not certain this will work properly, though; docstrings are a bit magic. This will not work; the docstring is evaluated at compile time (as the first statement in the function, given it is a string literal—once it's got the % in it it's not just a string literal), string formatting takes place at runtime, so __doc__ will be empty:

>>> def a(): 'docstring works'
... 
>>> a.__doc__
'docstring works'
>>> def b(): "formatted docstring doesn't work %s" % ':-('
... 
>>> b.__doc__
>>> 

If you wanted to work this way, you'd need to do func.__doc__ %= {'ANIMAL_TYPES': ANIMAL_TYPES} after the function is defined. Be aware that this would then break on python -OO if you didn't check that __doc__ was defined, as -OO strips docstrings.

>>> def c(): "formatted docstring works %s"
... 
>>> c.__doc__
"formatted docstring works %s"
>>> c.__doc__ %= 'after'
>>> c.__doc__
"formatted docstring works after"

This is not the standard technique anyway; the standard technique is to reference the appropriate constant: "Takes one of the animal types in ANIMAL_TYPES", or similar.

Solution 2

One way to do this would be to use a decorator. I'm not sure how I feel about this; I actually searched for commentary on this method and found this answer, which rightly notes that it could mask a design problem. But your use case seems sound to me at first glance.

In any case, here's a fairly elegant way to achieve the result you're looking for:

>>> def docstring_parameter(*sub):
...     def dec(obj):
...         obj.__doc__ = obj.__doc__.format(*sub)
...         return obj
...     return dec
... 
>>> @docstring_parameter('Ocean')
... def foo():
...     '''My Docstring Lies Over The {0}'''
...     pass
... 
>>> @docstring_parameter('Sea')
... def bar():
...     '''My Docstring Lies Over The {0}'''
...     pass
... 
>>> @docstring_parameter('Docstring', 'Me')
... def baz():
...     '''Oh Bring Back My {0} To {1}'''
...     pass
... 
>>> foo.__doc__
'My Docstring Lies Over The Ocean'
>>> bar.__doc__
'My Docstring Lies Over The Sea'
>>> foo.__doc__
'My Docstring Lies Over The Ocean'
>>> baz.__doc__
'Oh Bring Back My Docstring To Me'

Solution 3

You can also define a docstring using .__doc__

For example:

>>> def f():
      pass
>>> x = 1
>>> y = "docstring"

>>> f.__doc__ = "%s string %s" % (x, y)
>>> print(f.__doc__)
1 string docstring

Solution 4

You could simply use cross-references in your docstring to refer to the variable.

So:

:param animalType: It takes one of these :data:`animal types<ANIMAL_TYPES>`

And in the second:

:param choice: can be one of :attr:`MY_CONST`
Share:
26,532
Jack Z
Author by

Jack Z

Updated on July 05, 2022

Comments

  • Jack Z
    Jack Z almost 2 years

    So I'm trying to create a "dynamic" docstring which is something like this:

    ANIMAL_TYPES = ["mammals", "reptiles", "other"]
    
    def func(animalType):
    """ This is a sample function.
    
        @param animalType: "It takes one of these animal types %s" % ANIMAL_TYPES
    """
    

    to basically let the docstring for @param animalType show whatever ANIMAL_TYPES has; so that when this variable is updated, the docstring will be updated automatically.

    Unfortunately, it doesn't seem to work. Does anyone know if there is a way of achieving this?

  • Jack Z
    Jack Z about 12 years
    Thanks, Chris. Can I ask why __doc__ will be empty though in the code you crossed out? Thanks!
  • Karl Knechtel
    Karl Knechtel about 12 years
    The reason it's empty is because the expression 'foo %s' % 'bar' isn't a string literal, it's an expression that would evaluate to a string. The compiler expects a real string literal, as the first thing inside the block, to qualify as a docstring.
  • Jack Z
    Jack Z about 12 years
    I eventually went with the suggestion in the last code snippet. I tried to do "Takes one of the animal types in ANIMAL_TYPES", but it didn't work well because I'm writing the docstring for an API which will be generated by SPHINX and it wouldn't print out the content of the constant in the docs, no matter what I tried... :( So now it's like (Please see the original post. Code doesn't format very well here... )
  • taz
    taz over 9 years
    Can someone provide a SSCE clarifying how to write this into a python script? See also: sphinx-doc.org/domains.html#cross-referencing-python-objects and sphinx-doc.org/domains.html#python-roles
  • nik_m
    nik_m over 6 years
    A slight amendment to this answer, if I may: def docstring_parameter(*args, **kwargs): and then obj.__doc__ = obj.__doc__.format(*args, **kwargs). Now, you can do this: @docstring_parameter('Docstring', 'Me', yeah='yeah') and '''Oh Bring Back My {} To {}. {yeah}!'''. Finally, print(baz.__doc__) prints 'Oh Bring Back My Docstring To Me. yeah!'. Thank you!
  • endolith
    endolith over 3 years
    Can you explain in more detail how to use this?
  • HitLuca
    HitLuca almost 3 years
    Nice addition @nik_m. I would like to further improve on your solution by noting the fact that a check for obj.__doc__ is not None would fix an issue where no docstring is set in the function!