Dynamic function docstring

前端 未结 3 1731
闹比i
闹比i 2020-12-20 13:53

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

相关标签:
3条回答
  • 2020-12-20 14:22

    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)
    
    0 讨论(0)
  • 2020-12-20 14:27

    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.

    0 讨论(0)
  • 2020-12-20 14:31

    (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.

    0 讨论(0)
提交回复
热议问题