[英]Python decorator show signature and docstring from both decorator and decorated function
I'm trying to write a decorator whose wrapper function takes an additional input argument, based on which some processing is done within the wrapper.我正在尝试编写一个装饰器,其包装器 function 采用额外的输入参数,基于该参数在包装器内完成一些处理。 However, I'm having a hard time a) figuring out how to show this additional parameter in the signature, especially with parameter previews/helper (tested on ipynb and PyCharm) b) Figuring out how to show the info from both docstrings.但是,我很难 a) 弄清楚如何在签名中显示这个附加参数,特别是使用参数预览/帮助器(在 ipynb 和 PyCharm 上测试)b) 弄清楚如何显示来自两个文档字符串的信息。
The setup would be something like this设置将是这样的
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(self, *args, fancy_processing=False, **kwargs):
"""Some nice docstring
:param fancy_processing: If set, does fancy processing before executing the function
"""
output = func(self, *args, **kwargs)
if fancy_processing:
output += 1 # some dummy action
return output
return wrapper
def foo(important_param, other_important_param=4):
"""This is a well-written docstring.
:param important_param: Super important parameter
:param other_important_param: Other super important parameter
:returns: Something incredibly useful
"""
print(important_param) # dummy action
return (other_important_param +1) # some other dummy action
Now depending on whether I use wraps
or not I get the signature and docstring of my wrapper
function or my foo
function, but not both.现在取决于我是否使用wraps
,我得到我的wrapper
function 或我的foo
function 的签名和文档字符串,但不是两者。 For the docstring, I could do something like对于文档字符串,我可以做类似的事情
...
# @wraps(func) # don't use the `wraps` functionality
...
return output
wrapper.__doc__ += f"\n {func.__doc__}"
return wrapper
but this doesn't seem very well-formated and also doesn't solve the issue with the signature not reflecting parameters from both functions.但这似乎不是很好的格式,也没有解决签名不反映两个函数的参数的问题。 How would I go about solving this?我将如何解决这个问题?
If you look at what functools.wraps is eant to do, andhow it is implemented , you can notice it is pretty simple: it just replaces some attributes of the wrapper by some of the wrapped, so that the wrapper looks like the wrapped.如果你看一下functools.wraps想要做什么,以及它是如何实现的,你会发现它非常简单:它只是用一些被包装的东西替换了包装器的一些属性,因此包装器看起来像被包装器。
If you want to have a fancy merge between two docstrings, either write it yourself, or if you want to use this decorator for many functions, create a function to do it automatically.如果您想在两个文档字符串之间进行精美的合并,请自己编写,或者如果您想将此装饰器用于许多功能,请创建一个 function 来自动完成。 Here is a proof of concept:这是一个概念证明:
import docstring_parser # installed with PIP
def merge_docstrings(decorated_func, wrapper_func) -> str:
reST_style = docstring_parser.Style.REST
decorated_doc = docstring_parser.parse(decorated_func.__doc__, style=reST_style)
wrapper_doc = docstring_parser.parse(wrapper_func.__doc__, style=reST_style)
# adding the wrapper doc into the decorated doc
decorated_doc.short_description += "\n" + wrapper_doc.short_description
return_index = next((index for index, meta_elem in enumerate(decorated_doc.meta)
if isinstance(meta_elem, docstring_parser.DocstringReturns)
), 0)
for offset, wrapper_doc_param in enumerate(wrapper_doc.params):
decorated_doc.meta.insert(return_index + offset, wrapper_doc_param)
return docstring_parser.compose(decorated_doc, style=reST_style)
def my_decorator(func):
def wrapper(self, *args, fancy_processing=False, **kwargs):
"""Some nice docstring
:param fancy_processing: If set, does fancy processing before executing the function
"""
output = func(self, *args, **kwargs)
if fancy_processing:
output += 1 # some dummy action
return output
wrapper.__doc__ = merge_docstrings(func, wrapper)
return wrapper
def foo(important_param, other_important_param=4):
"""This is a well-written docstring.
:param important_param: Super important parameter
:param other_important_param: Other super important parameter
:returns: Something incredibly useful
"""
print(important_param) # dummy action
return (other_important_param +1) # some other dummy action
decorated_foo = my_decorator(foo) # the alternative way to decorate a function, and allows to keep the original too
assert foo.__doc__ == ('This is a well-written docstring.\n '
':param important_param: Super important parameter\n '
':param other_important_param: Other super important parameter\n '
':returns: Something incredibly useful\n ')
assert decorated_foo.__doc__ == \
('This is a well-written docstring.\n'
'Some nice docstring\n'
':param important_param: Super important parameter\n'
':param other_important_param: Other super important parameter\n'
':param fancy_processing: If set, does fancy processing before executing the function\n'
# ^^^ this one has been added
':returns: Something incredibly useful')
help(decorated_foo)
# wrapper(self, *args, fancy_processing=False, **kwargs)
# This is a well-written docstring.
# Some nice docstring
# :param important_param: Super important parameter
# :param other_important_param: Other super important parameter
# :param fancy_processing: If set, does fancy processing before executing the function
# :returns: Something incredibly useful
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.