ОБНОВЛЕНИЕ: это может быть «невозможно» сделать чисто, потому что sphinx использует объект кода функции для генерации сигнатуры функции.Но, так как вы используете sphinx, есть хакерский обходной путь, который работает.
Он хакерский, потому что эффективно отключает декоратор во время работы sphinx, но работает, поэтому это практическое решение.
Сначала я пошел по пути создания нового объекта types.CodeType
, чтобы заменить элемент объекта кода func_code
оболочки, который используется sphinx при генерации подписей.
Я смоготключить Python, идя по маршруту или пытаясь поменять местами co_varnames
, co_nlocals
и т. д. члены объекта кода из исходной функции, и при обращении это было слишком сложно.
Следующее решение, хотя и является хакерским тяжелым молотом, также очень просто =)
Подход заключается в следующем: при работе внутри 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()