sphinx.ext.autodoc: Mantener los nombres de las constantes en la firma

Estoy usando la función autodoc de Sphinx para documentar mi API.

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 decirle a Sphinx que mantenga el nombre del valor constante?

 do_something(msg, option=DEFAULT_OPTION) 

?

¿Hay alguna opción que haya pasado por alto? Si es posible, no quiero volver a escribir todas las firmas a mano.

Probablemente tenga que anular la firma 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).

Vea también estas preguntas similares: aquí y aquí .


Actualizar:

Me acabo de dar cuenta de que hay otra opción. Puede anular la firma incluyéndola como la primera línea de la cadena de documentos. Consulte la documentación de la variable de configuración autodoc_docstring_signature y esta respuesta .

Puede obtener la firma con nombres constantes de un AST y “separar” de nuevo a código Python.

Pon esto en tu 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 un archivo unparser.py , importable desde conf.py :

Nota: Este “no analizador” 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