Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic function docstring

Tags:

python

I'd like to write a python function that has a dynamically created docstring. In essence for a function func() I want func.__doc__ to be a descriptor that calls a custom __get__ function create the docstring on request. Then help(func) should return the dynamically generated docstring.

The context here is to write a python package wrapping a large number of command line tools in an existing analysis package. Each tool becomes a similarly named module function (created via function factory and inserted into the module namespace), with the function documentation and interface arguments dynamically generated via the analysis package.

like image 281
Tom Aldcroft Avatar asked Apr 22 '10 19:04

Tom Aldcroft


People also ask

What is a function docstring?

A docstring is simply a multi-line string, that is not assigned to anything. It is specified in source code that is used to document a specific segment of code. Unlike conventional source code comments, the docstring should describe what the function does, not how.

Should every function have a docstring?

Every function you create ought to have a docstring. They're in triple-quoted strings and allow for multi-line text.

How do you write a docstring for a function?

Docstrings must be defined with three double-quotes. No blank lines should be left before or after the docstring. The text starts in the next line after the opening quotes. The closing quotes have their own line (meaning that they are not at the end of the last sentence).

Why should you use a docstring to document a function?

A docstring is a string literal that occurs as the first statement in a module, function, class, or method definition. Docstrings are for people who are going to be using your code without needing or wanting to know how it works. Docstrings can be turned into actual documentation.


1 Answers

You can't do what you're looking to do, in the way you want to do it.

From your description it seems like you could do something like this:

for tool in find_tools():
    def __tool(*arg):
        validate_args(tool, args)
        return execute_tool(tool, args)
    __tool.__name__ = tool.name
    __tool.__doc__ = compile_docstring(tool)
    setattr(module, tool.name, __tool)

i.e. create the documentation string dynamically up-front when you create the function. Is the a reason why the docstring has to be dynamic from one call to __doc__ to the next?

Assuming there is, you'll have to wrap your function up in a class, using __call__ to trigger the action.

But even then you've got a problem. When help() is called to find the docstring, it is called on the class, not the instance, so this kind of thing:

class ToolWrapper(object):
    def __init__(self, tool):
        self.tool = tool 
        self.__name__ = tool.name
    def _get_doc(self):
        return compile_docstring(self.tool)
    __doc__ = property(_get_doc)
    def __call__(self, *args):
        validate_args(args)
        return execute_tool(tool, args)

won't work, because properties are instance, not class attributes. You can make the doc property work by having it on a metaclass, rather than the class itself

for tool in find_tools():
    # Build a custom meta-class to provide __doc__.
    class _ToolMetaclass(type):
        def _get_doc(self):
            return create_docstring(tool)
        __doc__ = property(_get_doc)

    # Build a callable class to wrap the tool.
    class _ToolWrapper(object):
        __metaclass__ = _ToolMetaclass
        def _get_doc(self):
            return create_docstring(tool)
        __doc__ = property(_get_doc)
        def __call__(self, *args):
            validate_args(tool, args)
            execute_tool(tool, args)

    # Add the tool to the module.
    setattr(module, tool.name, _ToolWrapper())

Now you can do

help(my_tool_name)

and get the custom docstring, or

my_tool_name.__doc__

for the same thing. The __doc__ property is in the _ToolWrapper class is needed to trap the latter case.

like image 122
Ian Avatar answered Oct 14 '22 00:10

Ian