ACTUALIZACIÓN: esto puede ser "imposible" hacer limpiamente porque esfinge utiliza objeto de código de la función para generar la firma de la función. Pero, dado que estás usando sphinx, hay una solución hacky que funciona.
Es hacky porque desactiva efectivamente el decorador mientras sphinx se está ejecutando, pero funciona, por lo que es una solución práctica.
Al principio fui por la ruta de la construcción de un nuevo objeto types.CodeType
, para reemplazar el miembro del objeto de código func_code
del contenedor, que es lo que usa esfinge cuando genera las firmas.
pude segfault pitón por ir por el camino o tratando de cambiar en el co_varnames
, co_nlocals
, etc. miembros del objeto código de la función original, y al mismo tiempo atractiva, ya era demasiado complicado.
La siguiente solución, mientras que es un martillo pesado hacky, es también muy simple =)
El enfoque es el siguiente: cuando se ejecuta dentro de Sphinx, establece una variable de entorno de que el decorador puede comprobar. dentro del decorador, cuando se detecta sphinx, no realices ningún tipo de decoración, y en su lugar devuelve la función original.
Dentro de su esfinge conf.py:
import os
os.environ['SPHINX_BUILD'] = '1'
Y entonces aquí es un módulo de ejemplo con un caso de prueba que muestra lo que podría ser:
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()
La documentación en el stdlib y en Sphinx ambos parecen dar a entender que estás haciendo todo bien. :( –
¿Has probado usar el [paquete decorador] (http://pypi.python.org/pypi/decorator) y poner '@ decorator' en' checkStale'? Tuve un problema similar al usar 'epydoc' con un decorado función. – bstpierre
@bstpierre Supongo que el paquete de decorador no es parte de una distribución normal de Python? Me pregunto si es posible usarlo donde esté disponible, y de lo contrario retroceder a lo que tengo? –