Python docstrings templated
Question: Why doesn't dynamically formatting docstrings work? Is there an acceptable
workaround for doing this _at function definition time_?

    
    
    >>> DEFAULT_BAR = "moe's tavern"
    >>> def foo(bar=DEFAULT_BAR):
    ...     """
    ...     hello this is the docstring
    ...     
    ...     Args:
    ...       bar (str)    the bar argument (default: {})
    ...     """.format(DEFAULT_BAR)
    ... 
    >>> foo.__doc__
    >>> foo.__doc__ is None
    True
    

I tried with old-skool style %s formatting and that didn't work either.


Answer: Your string requires the function to be called, but function bodies are not
executed when a function is created.

A proper docstring is not _executed_ , it is simply taken from the parsed
sourcecode and attached to the function object, no code is ever executed for
this. Python stores the docstring as the first constant value in a code
object:

    
    
    >>> def f():
    ...     """docstring"""
    ...     pass
    ...
    >>> f.__code__.co_consts
    ('docstring', None)
    

where the code object was passed to the function type when constructing a new
function (see the [`PyFunction_New()`
function](https://hg.python.org/cpython/file/v2.7.11/Objects/funcobject.c#l28)).

See the [_Function definitions_ reference
documentation](https://docs.python.org/2/reference/compound_stmts.html#id3):

> The function definition does not execute the function body; this gets
> executed only when the function is called. [3]
>
> _[...]_
>
> [3] A string literal appearing as the first statement in the function body
> is transformed into the function’s `__doc__` attribute and therefore the
> function’s docstring.

Your definition is otherwise valid; there is just no stand-alone string
literal at the top of the function body. Your string literal is instead part
of the function itself, and is only executed when the function is called (and
the result discarded as you don't store that).

Note that the `__doc__` attribute on a function object is writable; you can
always apply variables _after_ creating the function:

    
    
    >>> DEFAULT_BAR = "moe's tavern"
    >>> def foo(bar=DEFAULT_BAR):
    ...     """
    ...     hello this is the docstring
    ...
    ...     Args:
    ...       bar (str)    the bar argument (default: {})
    ...     """
    ...
    >>> foo.__doc__ = foo.__doc__.format(DEFAULT_BAR)
    >>> print(foo.__doc__)
    
        hello this is the docstring
    
        Args:
          bar (str)    the bar argument (default: moe's tavern)
    

You could do that in a decorator with the help of `functionobject.__globals__`
and `inspect.getargspec()` perhaps, but then do use named slots in the
template so you can apply everything as a dictionary and have the docstring
choose what to interpolate:

    
    
    from inspect import getargspec
    
    def docstringtemplate(f):
        """Treat the docstring as a template, with access to globals and defaults"""
        spec = getargspec(f)
        defaults = {} if not spec.defaults else dict(zip(spec.args[-len(spec.defaults):], spec.defaults))
        f.__doc__ = f.__doc__ and f.__doc__.format(**dict(f.__globals__, **defaults))
        return f
    

Demo:

    
    
    >>> @docstringtemplate
    ... def foo(bar=DEFAULT_BAR):
    ...     """
    ...     hello this is the docstring
    ...
    ...     Args:
    ...       bar (str)    the bar argument (default: {bar!r}, or {DEFAULT_BAR!r})
    ...
    ...     """
    ...
    >>> print(foo.__doc__)
    
        hello this is the docstring
    
        Args:
          bar (str)    the bar argument (default: "moe's tavern", or "moe's tavern")
    

Function keyword arguments override globals, as they would in the function.

