Автодок Python Sphinx и украшенные участники - PullRequest
26 голосов
/ 10 сентября 2010

Я пытаюсь использовать Sphinx для документирования своего класса Python.Я делаю это, используя autodoc:

.. autoclass:: Bus
   :members:

Хотя он корректно выбирает строки документации для моих методов, те, которые оформлены:

    @checkStale
    def open(self):
        """
        Some docs.
        """
        # Code

, где @checkStale равно

def checkStale(f):
    @wraps(f)
    def newf(self, *args, **kwargs):
        if self._stale:
            raise Exception
        return f(self, *args, **kwargs)
    return newf

имеет неправильный прототип, такой как open(*args, **kwargs).

Как я могу это исправить?У меня сложилось впечатление, что использование @wraps исправит подобные вещи.

Ответы [ 5 ]

15 голосов
/ 12 сентября 2010

Чтобы расширить мой комментарий:

Вы пытались использовать пакет decorator и поместить @decorator на checkStale?У меня была похожая проблема с использованием epydoc с декорированной функцией.

Как вы спросили в своем комментарии, пакет decorator не является частью стандартной библиотеки.

Вы можете использоватькод что-то вроде следующего (не проверено):

try:
    from decorator import decorator
except ImportError:
    # No decorator package available. Create a no-op "decorator".
    def decorator(f):
        return f
14 голосов
/ 29 марта 2013

У меня была такая же проблема с декоратором сельдерея @task.

Вы также можете исправить это в вашем случае, добавив правильную сигнатуру функции в ваш первый файл, например:

.. autoclass:: Bus
    :members:

    .. automethod:: open(self)
    .. automethod:: some_other_method(self, param1, param2)

Он по-прежнему будет автоматически документировать элементы без декоратора.

Это упоминается в документации sphinx на http://www.sphinx -doc.org / ru / master / ext / autodoc.html # directive-automodule - поиск "Это полезно, если подпись метод скрыт декоратором. "

В моем случае мне пришлось использовать автофункцию, чтобы указать сигнатуру моих задач с сельдереем в модуле tasks.py приложения django:

.. automodule:: django_app.tasks
    :members:
    :undoc-members:
    :show-inheritance:

    .. autofunction:: funct1(user_id)
    .. autofunction:: func2(iterations)
3 голосов
/ 07 ноября 2012

Добавлено в версии 1.1. Теперь вы можете переопределить сигнатуру метода, указав пользовательское значение в первой строке строки документации.

http://sphinx -doc.org / ext / autodoc.html #confval-autodoc_docstring_signature

@checkStale
def open(self):
    """
    open()
    Some docs.
    """
    # Code
0 голосов
/ 14 марта 2013

Если вы особенно непреклонны в том, чтобы не добавлять еще одну зависимость, вот фрагмент кода, который работает с обычным инспектором путем внедрения в строку документации.Это довольно хакерский и не очень рекомендуется, если нет веских причин не добавлять другой модуль, но здесь это так.

# inject the wrapped functions signature at the top of a docstring
args, varargs, varkw, defaults = inspect.getargspec(method)
defaults = () if defaults is None else defaults
defaults = ["\"{}\"".format(a) if type(a) == str else a for a in defaults]
l = ["{}={}".format(arg, defaults[(idx+1)*-1]) if len(defaults)-1 >= idx else arg for idx, arg in enumerate(reversed(list(args)))]
if varargs: allargs.append('*' + varargs)
if varkw: allargs.append('**' + varkw)
doc = "{}({})\n{}".format(method.__name__, ', '.join(reversed(l)), method.__doc__)
wrapper.__doc__ = doc
0 голосов
/ 14 октября 2011

ОБНОВЛЕНИЕ: это может быть «невозможно» сделать чисто, потому что 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()
...