Support for autodocumenting partial functions.

This commit is contained in:
Łukasz Langa 2011-01-08 18:06:24 +01:00
parent a79758baed
commit 48d64cc4cb
2 changed files with 67 additions and 8 deletions

View File

@ -902,15 +902,15 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter):
# cannot introspect arguments of a C function or method
pass
try:
argspec = inspect.getargspec(self.object)
argspec = getargspec(self.object)
except TypeError:
# if a class should be documented as function (yay duck
# typing) we try to use the constructor signature as function
# signature without the first argument.
try:
argspec = inspect.getargspec(self.object.__new__)
argspec = getargspec(self.object.__new__)
except TypeError:
argspec = inspect.getargspec(self.object.__init__)
argspec = getargspec(self.object.__init__)
if argspec[0]:
del argspec[0][0]
args = inspect.formatargspec(*argspec)
@ -960,7 +960,7 @@ class ClassDocumenter(ModuleLevelDocumenter):
(inspect.ismethod(initmeth) or inspect.isfunction(initmeth)):
return None
try:
argspec = inspect.getargspec(initmeth)
argspec = getargspec(initmeth)
except TypeError:
# still not possible: happens e.g. for old-style classes
# with __init__ in C
@ -1117,7 +1117,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter):
inspect.ismethoddescriptor(self.object):
# can never get arguments of a C function or method
return None
argspec = inspect.getargspec(self.object)
argspec = getargspec(self.object)
if argspec[0] and argspec[0][0] in ('cls', 'self'):
del argspec[0][0]
return inspect.formatargspec(*argspec)
@ -1284,6 +1284,36 @@ def add_documenter(cls):
AutoDirective._registry[cls.objtype] = cls
if sys.version_info >= (2, 5):
from functools import partial
def getargspec(func):
"""Like inspect.getargspec but supports functools.partial as well."""
if inspect.ismethod(func):
func = func.im_func
parts = 0, ()
if type(func) is partial:
parts = len(func.args), func.keywords.keys()
func = func.func
if not inspect.isfunction(func):
raise TypeError('{!r} is not a Python function'.format(func))
args, varargs, varkw = inspect.getargs(func.func_code)
func_defaults = func.func_defaults
if func_defaults:
func_defaults = list(func_defaults)
if parts[0]:
args = args[parts[0]:]
if parts[1]:
for arg in parts[1]:
i = args.index(arg) - len(args)
del args[i]
try:
del func_defaults[i]
except IndexError:
pass
return inspect.ArgSpec(args, varargs, varkw, func_defaults)
else:
getargspec = inspect.getargspec
def setup(app):
app.add_autodocumenter(ModuleDocumenter)
app.add_autodocumenter(ClassDocumenter)

View File

@ -10,6 +10,9 @@
:license: BSD, see LICENSE for details.
"""
import sys
from StringIO import StringIO
from util import *
from docutils.statemachine import ViewList
@ -17,7 +20,6 @@ from docutils.statemachine import ViewList
from sphinx.ext.autodoc import AutoDirective, add_documenter, \
ModuleLevelDocumenter, FunctionDocumenter, cut_lines, between, ALL
from StringIO import StringIO
def setup_module():
global app, lid, options, directive
@ -422,12 +424,14 @@ def test_generate():
('attribute', 'test_autodoc.Class.udocattr'),
('attribute', 'test_autodoc.Class.mdocattr'),
('attribute', 'test_autodoc.Class.inst_attr_comment'),
('attribute', 'test_autodoc.Class.inst_attr_string')
('attribute', 'test_autodoc.Class.inst_attr_string'),
('method', 'test_autodoc.Class.moore'),
])
options.members = ALL
assert_processes(should, 'class', 'Class')
options.undoc_members = True
should.append(('method', 'test_autodoc.Class.undocmeth'))
should.extend((('method', 'test_autodoc.Class.undocmeth'),
('method', 'test_autodoc.Class.roger')))
assert_processes(should, 'class', 'Class')
options.inherited_members = True
should.append(('method', 'test_autodoc.Class.inheritedmeth'))
@ -490,6 +494,8 @@ def test_generate():
' .. py:attribute:: Class.docattr',
' .. py:attribute:: Class.udocattr',
' .. py:attribute:: Class.mdocattr',
' .. py:classmethod:: Class.roger(a, e=5, f=6)',
' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
' .. py:attribute:: Class.inst_attr_comment',
' .. py:attribute:: Class.inst_attr_string',
' .. py:method:: Class.inheritedmeth()',
@ -509,6 +515,9 @@ def test_generate():
'test_autodoc.DocstringSig.meth')
assert_result_contains(
' rest of docstring', 'method', 'test_autodoc.DocstringSig.meth')
assert_result_contains(
'.. py:classmethod:: Class.moore(a, e, f) -> happiness', 'method',
'test_autodoc.Class.moore')
# --- generate fodder ------------
@ -534,6 +543,21 @@ class CustomDataDescriptor(object):
return self
return 42
def _funky_classmethod(name, b, c, d, docstring=None):
"""Generates a classmethod for a class from a template by filling out
some arguments."""
def template(cls, a, b, c, d=4, e=5, f=6):
return a, b, c, d, e, f
if sys.version_info >= (2, 5):
from functools import partial
function = partial(template, b=b, c=c, d=d)
else:
def function(cls, a, e=5, f=6):
return template(a, b, c, d, e, f)
function.__name__ = name
function.__doc__ = docstring
return classmethod(function)
class Base(object):
def inheritedmeth(self):
"""Inherited function."""
@ -576,6 +600,11 @@ class Class(Base):
mdocattr = StringIO()
"""should be documented as well - süß"""
roger = _funky_classmethod("roger", 2, 3, 4)
moore = _funky_classmethod("moore", 9, 8, 7,
docstring="moore(a, e, f) -> happiness")
def __init__(self, arg):
#: a documented instance attribute
self.inst_attr_comment = None