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 descri
Instead of messing with the function, why not write your own help
function?
my_global=42
def help(func):
print('%s: my_global=%s'%(func.func_name,my_global))
def foo():
pass
help(foo)
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.
(Python 3 solution)
You could make use of Python's duck typing to implement a dynamic string:
import time
def fn():
pass
class mydoc( str ):
def expandtabs( self, *args, **kwargs ):
return "this is a dynamic strting created on {}".format( time.asctime() ).expandtabs( *args, **kwargs )
fn.__doc__ = mydoc()
help( fn )
Caveats:
This assumes that the help
function is calling .expandtabs
to get the text from the __doc__
object, which works in Python 3.7. A more robust solution would implement the other str
methods in order to have our duck continue acting like a duck even if the help
method changes. Also note that our mydoc
class derives from str
, this is because help
, somewhat atypically, enforces strong typing by asserting isinstance(thing.__doc__, str)
. Like all solutions this is a bit hacky, but whether this is a problem largely depends on the full project requirements.