2011-08-29 13 views
5

Estoy usando la función autodoc de Sphinx para documentar mi API.sphinx.ext.autodoc: Mantener los nombres de las constantes en la firma

Ejemplo:

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

La documentación generada ahora muestra la siguiente firma:

do_something(msg, option='default') 

¿Cómo puedo saber Sphinx mantener el nombre del valor constante, es decir

do_something(msg, option=DEFAULT_OPTION) 

?

¿Existe alguna opción que haya pasado por alto? Si es posible, me gustaría NO escribir nuevamente toda la firma a mano.

Respuesta

1

Probablemente tenga que override the signature a mano en el archivo reST.

Es difícil encontrar una mejor respuesta. Autodoc importa los módulos que documenta, por lo que se ejecuta todo el código de nivel de módulo (incluidos los argumentos de función predeterminados).

Consulte también estas preguntas similares: here y here.


Actualización:

me he dado cuenta de que hay otra opción. Puede anular la firma incluyéndola como la primera línea de la docstring. Consulte la documentación de la variable de configuración autodoc_docstring_signature y this answer.

+0

Gracias por el consejo. Sin embargo, esperaba evitar esto por completo. – basti

+1

Para cualquiera que esté interesado, el informe de error correspondiente está aquí https://bitbucket.org/birkenfeld/sphinx/issue/759/ keeping-original-signatures-for-functions – basti

+0

El informe de error ahora está en GitHub: https: // github .com/sphinx-doc/sphinx/issues/759. – mzjn

1

Puede obtener la firma con los nombres constantes de un AST y "desaprender" de nuevo al código de Python.

poner esto en su archivo 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) 

Y esto en algún archivo unparser.py, importable de conf.py:

Nota: Este "unparser" probablemente tiene muchos errores.

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

Esto es para Python 3, ¿verdad? – mzjn

+0

@mzjn Sí, pero no debería ser difícil portarlo a Python 2. –

Cuestiones relacionadas