更新:这可能是“不可能”干净地完成的,因为 sphinx 使用函数的代码对象来生成其函数签名。但是,由于您使用的是 sphinx,因此有一个可行的解决方法。
这很 hacky,因为它在 sphinx 运行时有效地禁用了装饰器,但它确实有效,所以它是一个实用的解决方案。
起初我沿着构建新types.CodeType
对象的路线走,以替换包装器的func_code
代码对象成员,这是 sphinx 在生成签名时使用的。
co_varnames
我能够通过沿着路线或尝试从原始函数中交换代码对象的,co_nlocals
等成员来对python 进行段错误,虽然很吸引人,但它太复杂了。
下面的解决方案,虽然是一个笨重的重锤,但也很简单=)
方法如下:在 sphinx 内部运行时,设置一个装饰器可以检查的环境变量。在装饰器内部,当检测到 sphinx 时,根本不做任何装饰,而是返回原始函数。
在你的 sphinx conf.py 中:
import os
os.environ['SPHINX_BUILD'] = '1'
然后这是一个带有测试用例的示例模块,它显示了它的外观:
import functools
import os
import types
import unittest
SPHINX_BUILD = bool(os.environ.get('SPHINX_BUILD', ''))
class StaleError(StandardError):
"""Custom exception for staleness"""
pass
def check_stale(f):
"""Raise StaleError when the object has gone stale"""
if SPHINX_BUILD:
# sphinx hack: use the original function when sphinx is running so that the
# documentation ends up with the correct function signatures.
# See 'SPHINX_BUILD' in conf.py.
return f
@functools.wraps(f)
def wrapper(self, *args, **kwargs):
if self.stale:
raise StaleError('stale')
return f(self, *args, **kwargs)
return wrapper
class Example(object):
def __init__(self):
self.stale = False
self.value = 0
@check_stale
def get(self):
"""docstring"""
return self.value
@check_stale
def calculate(self, a, b, c):
"""docstring"""
return self.value + a + b + c
class TestCase(unittest.TestCase):
def test_example(self):
example = Example()
self.assertEqual(example.get(), 0)
example.value = 1
example.stale = True
self.assertRaises(StaleError, example.get)
example.stale = False
self.assertEqual(example.calculate(1, 1, 1), 4)
if __name__ == '__main__':
unittest.main()