2011-08-29 5 views
5

Używam funkcji autodoc Sphinx do udokumentowania mojego API.sphinx.ext.autodoc: Zachowywanie nazw stałych w podpisie

Przykład:

DEFAULT_OPTION = 'default' 
def do_something(msg, option=DEFAULT_OPTION): 
    print msg 

Wytworzona dokumentacja pokazuje teraz następujący podpis:

do_something(msg, option='default') 

Jak mogę powiedzieć Sphinx zachować nazwę stałej wartości tj

do_something(msg, option=DEFAULT_OPTION) 

?

Czy istnieje opcja, którą przeoczyłem? Jeśli to w ogóle możliwe, chciałbym NIE napisać jeszcze raz podpisu ręcznie.

Odpowiedz

1

Najprawdopodobniej musisz ręcznie uzyskać override the signature w pliku REST.

Trudno wymyślić lepszą odpowiedź. Autodoc importuje moduły, które dokumentuje, więc wykonywany jest cały kod na poziomie modułu (w tym domyślne argumenty funkcji).

Zobacz także podobne pytania: here i here.


Aktualizacja:

Właśnie uświadomiłem sobie, że nie ma innej opcji. Możesz nadpisać podpis, włączając go jako pierwszy wiersz instrukcji. Zobacz dokumentację zmiennej konfiguracyjnej autodoc_docstring_signature i this answer.

+0

Dzięki za cynk. Miałem jednak nadzieję, że całkowicie tego uniknę. – basti

+1

Dla każdego zainteresowanego raport o błędzie znajduje się tutaj https://bitbucket.org/birkenfeld/sphinx/issue/759/keeping-original-signatures-for-functions – basti

+0

Raport o błędzie znajduje się teraz na GitHub: https: // github .com/sphinx-doc/sphinx/issues/759. – mzjn

1

Możesz otrzymać podpis ze stałymi nazwami od AST i "nieodparcie" go z powrotem do kodu Pythona.

umieścić to w pliku conf.py:

import ast 
import inspect 

from unparser import Unparser 


unparse = Unparser() 

def get_signature_from_ast(app, what, name, obj, options, signature, 
          return_annotation): 
    if what in ('class', 'exception', 'function', 'method'): 
     remove_args = 0 
     if what == 'method': 
      remove_args += 1 # Remove self from instance methods. 
     while True: 
      if inspect.isclass(obj): 
       obj = obj.__init__ 
      elif inspect.ismethod(obj): 
       remove_args += 1 # Remove self from instance methods. 
       obj = obj.__func__ 
      elif hasattr(obj, '__wrapped__'): 
       obj = obj.__wrapped__ 
      else: 
       break 
     filename = sys.modules[obj.__module__].__file__ 
     with open(filename) as file: 
      node = ast.parse(file.read(), filename) 
     lineno = obj.__code__.co_firstlineno 
     for n in ast.walk(node): 
      if isinstance(n, ast.FunctionDef) and n.lineno == lineno: 
       signature = '(' + unparse.argspec(n.args, remove_args) + ')' 
       if n.returns: 
        return_annotation = unparse.expr(n.returns) 
       break 
    return signature, return_annotation 

def setup(app): 
    app.connect('autodoc-process-signature', get_signature_from_ast) 

a to z pewnym unparser.py pliku, importable z conf.py:

Uwaga: Ta "unparser" prawdopodobnie ma wiele błędów.

import ast 
from itertools import zip_longest 


class _Ast(object): 
    """Type that returns a dummy type on failed attribute access. 
    Used for backwards compatibility when accessing new types in the :mod:`ast` 
    module. 
    """ 
    def __getattribute__(self, attr): 
     """Return a type from :mod:`ast` or a dummy type when the attribute 
     does not exist in the module. 
     """ 
     return getattr(ast, attr, type(self)) 

_ast = _Ast() 


class Unparser(object): 
    """Unparse an AST back to Python code. 
    Supports only expressions, up to Python 3.3. 
    """ 
    #: Mapping of AST types to Python code strings. 
    ast_symbols = { 
     # Boolean binary operators. 
     _ast.And: 'and', 
     _ast.Or: 'or', 
     # Binary operators. 
     _ast.Add: '+', 
     _ast.Sub: '-', 
     _ast.Mult: '*', 
     _ast.Div: '/', 
     _ast.FloorDiv: '//', 
     _ast.Mod: '%', 
     _ast.LShift: '<<', 
     _ast.RShift: '>>', 
     _ast.BitOr: '|', 
     _ast.BitAnd: '&', 
     _ast.BitXor: '^', 
     # Comparison operators. 
     _ast.Eq: '==', 
     _ast.Gt: '>', 
     _ast.GtE: '>=', 
     _ast.In: 'in', 
     _ast.Is: 'is', 
     _ast.IsNot: 'is not', 
     _ast.Lt: '<', 
     _ast.LtE: '<=', 
     _ast.NotEq: '!=', 
     _ast.NotIn: 'not in', 
     # Unary operators. 
     _ast.Invert: '~', 
     _ast.Not: 'not', 
     _ast.UAdd: '+', 
     _ast.USub: '-' 
    } 

    def args(unparse, args, defaults, remove_args=0, override_args={}): 
     """Unparse arguments from an argspec. This can strip out positional 
     arguments and replace keyword arguments. 
     """ 
     l = [] 
     defaults = list(map(unparse.expr, defaults)) 
     args = list(zip_longest(reversed(args), reversed(defaults))) 
     args.reverse() 
     for arg, default in args[remove_args:]: 
      a = arg.arg 
      if a in override_args: 
       default = repr(override_args[a]) 
      if arg.annotation: 
       a += ': ' + unparse.expr(arg.annotation) 
      if default is not None: 
       a += '=' + default 
      l.append(a) 
     return l 

    def argspec(unparse, node, remove_args=0, override_args={}): 
     """Unparse an argspec from a function definition. This can strip out 
     positional arguments and replace keyword arguments.""" 
     s = [] 
     s.extend(unparse.args(node.args, node.defaults, 
           remove_args, override_args)) 
     if node.vararg or node.kwonlyargs: 
      vararg = '*' 
      if node.vararg: 
       vararg += node.vararg 
       if node.varargannotation: 
        vararg += ': ' + unparse.expr(node.varargannotation) 
      s.append(vararg) 
     s.extend(unparse.args(node.kwonlyargs, node.kw_defaults, 
           override_args=override_args)) 
     kwarg = node.kwarg 
     if kwarg: 
      if node.kwargannotation: 
       kwarg += ': ' + unparse.expr(node.kwargannotation) 
      s.append('**' + kwarg) 
     return ', '.join(s) 

    def comprehension(unparse, node): 
     """Unparse a comprehension.""" 
     s = ['for', unparse.expr(node.target), 'in', unparse.expr(node.iter)] 
     for cond in node.ifs: 
      s.extend(('if', cond)) 
     return ' '.join(s) 

    def slice(unparse, node): 
     """Unparse a slice.""" 
     s = '' 
     if isinstance(node, _ast.Slice): 
      s = [] 
      if node.lower: 
       s.append(unparse.expr(node.lower)) 
      else: 
       s.append('') 
      if node.upper: 
       s.append(unparse.expr(node.upper)) 
      else: 
       s.append('') 
      if node.step: 
       s.append(unparse.expr(node.step)) 
      s = ':'.join(s) 
     elif isinstance(node, _ast.ExtSlice): 
      s = ', '.join(map(unparse.slice, node.dims)) 
     elif isinstance(node, _ast.Index): 
      s = unparse.expr(node.value) 
     return s 

    def expr(unparse, node, parenthesise=False): 
     """Unparse an expression.""" 
     s = 'None' 
     if isinstance(node, _ast.BoolOp): 
      s = [] 
      for expr in node.values: 
       s.append(unparse.expr(expr, parenthesise=True)) 
      s = (' ' + unparse.ast_symbols[type(node.op)] + ' ').join(s) 
     elif isinstance(node, _ast.BinOp): 
      s = ' '.join((unparse.expr(node.left, parenthesise=True), 
          unparse.ast_symbols[type(node.op)], 
          unparse.expr(node.right, parenthesise=True))) 
     elif isinstance(node, _ast.UnaryOp): 
      s = (unparse.ast_symbols[type(node.op)] + 
       unparse.expr(node.operand, parenthesise=True)) 
     elif isinstance(node, _ast.Lambda): 
      s = ('lambda ' + unparse.argspec(node.args) + ': ' + 
       unparse.expr(node.body)) 
     elif isinstance(node, _ast.IfExp): 
      s = ' '.join((unparse.expr(node.body), 
          'if', unparse.expr(node.test), 
          'else', unparse.expr(node.orelse))) 
     elif isinstance(node, _ast.Dict): 
      s = [] 
      for key, value in zip(node.keys, node.values): 
       s.append(unparse.expr(key) + ': ' + unparse.expr(value)) 
      s = '{' + ', '.join(s) + '}' 
      parenthesise = False 
     elif isinstance(node, _ast.Set): 
      s = '{' + ', '.join(map(unparse.expr, node.elts)) + '}' 
      parenthesise = False 
     elif isinstance(node, _ast.ListComp): 
      s = [unparse.expr(node.elt)] 
      s.extend(map(unparse.comprehension, node.generators)) 
      s = '[' + ' '.join(map(unparse.expr, node.elts)) + ']' 
      parenthesise = False 
     elif isinstance(node, _ast.SetComp): 
      s = [unparse.expr(node.elt)] 
      s.extend(map(unparse.comprehension, node.generators)) 
      s = '{' + ' '.join(map(unparse.expr, node.elts)) + '}' 
      parenthesise = False 
     elif isinstance(node, _ast.DictComp): 
      s = [unparse.expr(node.key) + ': ' + unparse.expr(node.value)] 
      s.extend(map(unparse.comprehension, node.generators)) 
      s = '{' + ' '.join(map(unparse.expr, node.elts)) + '}' 
      parenthesise = False 
     elif isinstance(node, _ast.GeneratorExp): 
      s = [unparse.expr(node.elt)] 
      s.extend(map(unparse.comprehension, node.generators)) 
      s = '(' + ' '.join(map(unparse.expr, node.elts)) + ')' 
      parenthesise = False 
     elif isinstance(node, _ast.Yield): 
      s = ['yield'] 
      if node.value: 
       s.append(unparse.expr(node.value)) 
      s = ' '.join(s) 
      parenthesise = False 
     elif isinstance(node, _ast.YieldFrom): 
      s = ['yield from'] 
      if node.value: 
       s.append(unparse.expr(node.value)) 
      s = ' '.join(s) 
      parenthesise = False 
     elif isinstance(node, _ast.Compare): 
      s = [unparse.expr(node.left, parenthesise=True)] 
      for op, operand in zip(node.ops, node.comparators): 
       s.append(unparse.ast_symbols[type(op)]) 
       s.append(unparse.expr(operand, parenthesise=True)) 
      s = ' '.join(s) 
     elif isinstance(node, _ast.Call): 
      s = list(map(unparse.expr, node.args)) 
      if node.starargs: 
       s.append('*' + unparse.expr(node.starargs, parenthesise=True)) 
      for kw in node.keywords: 
       s.append(kw.arg + '=' + 
         unparse.expr(kw.value, parenthesise=True)) 
      if node.kwargs: 
       s.append('**' + unparse.expr(node.kwargs, parenthesise=True)) 
      s = (unparse.expr(node.func, parenthesise=True) + 
       '(' + ', '.join(s) + ')') 
      parenthesise = False 
     elif isinstance(node, _ast.Num): 
      s = repr(node.n) 
      parenthesise = False 
     elif isinstance(node, (_ast.Str, _ast.Bytes)): 
      s = repr(node.s) 
      parenthesise = False 
     elif isinstance(node, _ast.Ellipsis): 
      s = '...' 
      parenthesise = False 
     elif isinstance(node, _ast.Attribute): 
      s = unparse.expr(node.value) + '.' + node.attr 
      parenthesise = False 
     elif isinstance(node, _ast.Subscript): 
      s = (unparse.expr(node.value, parenthesise=True) + 
       '[' + unparse.slice(node.slice) + ']') 
      parenthesise = False 
     elif isinstance(node, _ast.Starred): 
      s = '*' + unparse.expr(node.value) 
      parenthesise = False 
     elif isinstance(node, _ast.Name): 
      s = node.id 
      parenthesise = False 
     elif isinstance(node, _ast.List): 
      s = '[' + ', '.join(map(unparse.expr, node.elts)) + ']' 
      parenthesise = False 
     elif isinstance(node, _ast.Tuple): 
      s = ', '.join(map(unparse.expr, node.elts)) 
      if len(node.elts) == 1: 
       s += ',' 
      s = '(' + s + ')' 
      parenthesise = False 
     if parenthesise: 
      s = '(' + s + ')' 
     return s 
+0

To jest dla Pythona 3, prawda? – mzjn

+0

@mzjn Tak, ale nie powinno być trudno przenieść go do Pythona 2. –