[英]How to preserve a docstring of a decorated class for sphinx documentation?
[英]Preserve default arguments of wrapped/decorated Python function in Sphinx documentation
如何用裝飾函數文檔中的真實簽名替換*args
和**kwargs
?
假設我有以下裝飾器和裝飾功能:
import functools
def mywrapper(func):
@functools.wraps(func)
def new_func(*args, **kwargs):
print('Wrapping Ho!')
return func(*args, **kwargs)
return new_func
@mywrapper
def myfunc(foo=42, bar=43):
"""Obscure Addition
:param foo: bar!
:param bar: bla bla
:return: foo + bar
"""
return foo + bar
因此,調用print(myfunc(3, 4))
給我們:
Wrapping Ho!
7
到現在為止還挺好。 我還希望我的庫包含使用 Sphinx 正確記錄的myfunc
。 但是,如果我通過以下方式將我的函數包含在我的 sphinx html 頁面中:
.. automodule:: mymodule
:members: myfunc
它實際上會顯示為:
模糊加成
如何擺脫標題中的通用myfunc(*args, **kwargs)
? 這應該替換為myfunc(foo=42, bar=43) 。 如何更改 sphinx 或我的裝飾器mywrapper
以便在文檔中保留默認關鍵字參數?
編輯:
正如所指出的,這個問題以前曾被問過,但答案並不是那么有幫助。
但是,我有一個想法,想知道這是否可能。 Sphinx 是否設置了一些環境變量來告訴我的模塊它實際上是由 Sphinx 導入的? 如果是這樣,我可以簡單地修補我自己的包裝。 如果我的模塊是由 Sphinx 導入的,我的包裝器將返回原始函數而不是包裝它們。 因此,簽名被保留。
我為functools.wraps
想出了一個猴子補丁。 因此,我只是將其添加到我的項目文檔的 sphinx source
文件夾中的conf.py
腳本中:
# Monkey-patch functools.wraps
import functools
def no_op_wraps(func):
"""Replaces functools.wraps in order to undo wrapping.
Can be used to preserve the decorated function's signature
in the documentation generated by Sphinx.
"""
def wrapper(decorator):
return func
return wrapper
functools.wraps = no_op_wraps
因此,當通過make html
構建 html 頁面時, functools.wraps
被替換為這個裝飾器no_op_wraps
,它除了簡單地返回原始函數之外什么都不做。
你通常不能。 那是因為在包裝函數中用作參數的變量名稱甚至不存在於包裝函數中 - 所以 Sphinx 不知道它們。
這是 Python 中一個已知的復雜問題 - 以至於最近的版本 - 不僅包括 Python 3,還包括 Python 2.7 在裝飾類上包含一個__wrapped__
屬性,可以從functools.wraps
正確使用 - 這樣,在檢查裝飾函數時可以通過查看__wrapped__
來了解實際的包裝函數。 不幸的是,Sphinxs 忽略了__wrapped__
,而是顯示包裝函數的信息。
所以,要做的一件事當然是將此作為錯誤報告給 Sphinx 項目本身 - 它應該考慮__wrapped__
。
在此期間的解決方法是更改包裝器函數以實際包含有關包裝的更多信息(例如其簽名),因此您可以編寫另一個要調用的函數來代替項目的“functools.wraps”,它就是這樣做的: 將函數簽名預先添加到其文檔字符串中,如果有的話。 不幸的是,在 Python 3.3 之前檢索函數簽名很棘手 - (對於 3.3 和更高版本,請檢查https://docs.python.org/3/library/inspect.html#inspect-signature-object ) - 但無論如何,對於一個天真的形式,你可以寫另一個版本的“包裝”:
def wraps(original_func):
wrap_decorator = functools.wraps(original_func)
def re_wrapper(func):
wrapper = wrap_decorator(func)
poorman_sig = original_func.__code__.co_varnames[
:original_func.__code__.co_argcount]
wrapper.__doc__ = "{} ({})\n\n{}".format (
original_func.__name__, ", ".join(poorman_sig),
wrapper.__doc__)
return wrapper
return re_wrapper
並使用它而不是“functools.wraps”。 它至少會添加一行帶有參數名稱的行(但不是默認值)作為文檔中的第一行。
---嗯.. __wrapped__
在正確完成此操作之前修補 Sphinx 以使用__wrapped__
會更容易。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.