mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Fix documentation of inner classes in autodoc. Messy!
This commit is contained in:
parent
409cc1fe39
commit
dafb55a467
4
CHANGES
4
CHANGES
@ -35,7 +35,9 @@ New features added
|
||||
|
||||
- Italian by Sandro Dentella.
|
||||
|
||||
* Extension API:
|
||||
* Extensions and API:
|
||||
|
||||
- Autodoc now handles inner classes and their methods.
|
||||
|
||||
- There is now a ``Sphinx.add_lexer()`` method to be able to use
|
||||
custom Pygments lexers easily.
|
||||
|
@ -23,7 +23,6 @@ from docutils.parsers.rst import directives
|
||||
from docutils.statemachine import ViewList
|
||||
|
||||
from sphinx.util import rpartition, nested_parse_with_titles
|
||||
from sphinx.directives.desc import py_sig_re
|
||||
|
||||
try:
|
||||
base_exception = BaseException
|
||||
@ -33,6 +32,14 @@ except NameError:
|
||||
_charset_re = re.compile(r'coding[:=]\s*([-\w.]+)')
|
||||
_module_charsets = {}
|
||||
|
||||
py_ext_sig_re = re.compile(
|
||||
r'''^ ([\w.]+::)? # explicit module name
|
||||
([\w.]+\.)? # module and/or class name(s)
|
||||
(\w+) \s* # thing name
|
||||
(?: \((.*)\) # optional arguments
|
||||
(\s* -> \s* .*)? )? $ # optional return annotation
|
||||
''', re.VERBOSE)
|
||||
|
||||
|
||||
class Options(object):
|
||||
pass
|
||||
@ -282,24 +289,31 @@ class RstGenerator(object):
|
||||
# first, parse the definition -- auto directives for classes and functions
|
||||
# can contain a signature which is then used instead of an autogenerated one
|
||||
try:
|
||||
path, base, args, retann = py_sig_re.match(name).groups()
|
||||
mod, path, base, args, retann = py_ext_sig_re.match(name).groups()
|
||||
except:
|
||||
self.warn('invalid signature for auto%s (%r)' % (what, name))
|
||||
return
|
||||
# fullname is the fully qualified name, base the name after the last dot
|
||||
fullname = (path or '') + base
|
||||
return None, [], None, None
|
||||
|
||||
# support explicit module and class name separation via ::
|
||||
if mod is not None:
|
||||
mod = mod[:-2]
|
||||
parents = path and path.rstrip('.').split('.') or []
|
||||
else:
|
||||
parents = []
|
||||
|
||||
if what == 'module':
|
||||
if mod is not None:
|
||||
self.warn('"::" in automodule name doesn\'t make sense')
|
||||
if args or retann:
|
||||
self.warn('ignoring signature arguments and return annotation '
|
||||
'for automodule %s' % fullname)
|
||||
return fullname, fullname, [], None, None
|
||||
'for automodule %s' % name)
|
||||
return (path or '') + base, [], None, None
|
||||
|
||||
elif what in ('class', 'exception', 'function'):
|
||||
elif what in ('exception', 'function', 'class'):
|
||||
if mod is None:
|
||||
if path:
|
||||
mod = path.rstrip('.')
|
||||
else:
|
||||
mod = None
|
||||
# if documenting a toplevel object without explicit module, it can
|
||||
# be contained in another auto directive ...
|
||||
if hasattr(self.env, 'autodoc_current_module'):
|
||||
@ -307,9 +321,10 @@ class RstGenerator(object):
|
||||
# ... or in the scope of a module directive
|
||||
if not mod:
|
||||
mod = self.env.currmodule
|
||||
return fullname, mod, [base], args, retann
|
||||
return mod, parents + [base], args, retann
|
||||
|
||||
else:
|
||||
if mod is None:
|
||||
if path:
|
||||
mod_cls = path.rstrip('.')
|
||||
else:
|
||||
@ -323,14 +338,15 @@ class RstGenerator(object):
|
||||
mod_cls = self.env.currclass
|
||||
# ... if still None, there's no way to know
|
||||
if mod_cls is None:
|
||||
return fullname, None, [], args, retann
|
||||
return None, [], None, None
|
||||
mod, cls = rpartition(mod_cls, '.')
|
||||
parents = [cls]
|
||||
# if the module name is still missing, get it like above
|
||||
if not mod and hasattr(self.env, 'autodoc_current_module'):
|
||||
mod = self.env.autodoc_current_module
|
||||
if not mod:
|
||||
mod = self.env.currmodule
|
||||
return fullname, mod, [cls, base], args, retann
|
||||
return mod, parents + [base], args, retann
|
||||
|
||||
def format_signature(self, what, name, obj, args, retann):
|
||||
"""
|
||||
@ -386,13 +402,15 @@ class RstGenerator(object):
|
||||
"""
|
||||
Generate reST for the object in self.result.
|
||||
"""
|
||||
fullname, mod, objpath, args, retann = self.resolve_name(what, name)
|
||||
mod, objpath, args, retann = self.resolve_name(what, name)
|
||||
if not mod:
|
||||
# need a module to import
|
||||
self.warn('don\'t know which module to import for autodocumenting %r '
|
||||
'(try placing a "module" or "currentmodule" directive in the '
|
||||
'document, or giving an explicit module name)' % fullname)
|
||||
'document, or giving an explicit module name)' % name)
|
||||
return
|
||||
# fully-qualified name
|
||||
fullname = mod + (objpath and '.' + '.'.join(objpath) or '')
|
||||
|
||||
# the name to put into the generated directive -- doesn't contain the module
|
||||
name_in_directive = '.'.join(objpath) or mod
|
||||
@ -423,7 +441,7 @@ class RstGenerator(object):
|
||||
|
||||
# format the object's signature, if any
|
||||
try:
|
||||
sig = self.format_signature(what, name, todoc, args, retann)
|
||||
sig = self.format_signature(what, fullname, todoc, args, retann)
|
||||
except Exception, err:
|
||||
self.warn('error while formatting signature for %s: %s' %
|
||||
(fullname, err))
|
||||
@ -548,8 +566,7 @@ class RstGenerator(object):
|
||||
if isinstance(member, (types.FunctionType,
|
||||
types.BuiltinFunctionType)):
|
||||
memberwhat = 'function'
|
||||
elif isinstance(member, types.ClassType) or \
|
||||
isinstance(member, type):
|
||||
elif isinstance(member, (types.ClassType, type)):
|
||||
if issubclass(member, base_exception):
|
||||
memberwhat = 'exception'
|
||||
else:
|
||||
@ -558,14 +575,18 @@ class RstGenerator(object):
|
||||
# XXX: todo -- attribute docs
|
||||
continue
|
||||
else:
|
||||
if callable(member):
|
||||
if isinstance(member, (types.ClassType, type)):
|
||||
memberwhat = 'class'
|
||||
elif callable(member):
|
||||
memberwhat = 'method'
|
||||
elif isdescriptor(member):
|
||||
memberwhat = 'attribute'
|
||||
else:
|
||||
# XXX: todo -- attribute docs
|
||||
continue
|
||||
full_membername = fullname + '.' + membername
|
||||
# give explicitly separated module name, so that members of inner classes
|
||||
# can be documented
|
||||
full_membername = mod + '::' + '.'.join(objpath + [membername])
|
||||
self.generate(memberwhat, full_membername, ['__all__'], None, indent,
|
||||
check_module=members_check_module)
|
||||
|
||||
|
@ -82,41 +82,41 @@ def skip_member(app, what, name, obj, skip, options):
|
||||
def test_resolve_name():
|
||||
# for modules
|
||||
assert gen.resolve_name('module', 'test_autodoc') == \
|
||||
('test_autodoc', 'test_autodoc', [], None, None)
|
||||
('test_autodoc', [], None, None)
|
||||
assert gen.resolve_name('module', 'test.test_autodoc') == \
|
||||
('test.test_autodoc', 'test.test_autodoc', [], None, None)
|
||||
('test.test_autodoc', [], None, None)
|
||||
|
||||
assert gen.resolve_name('module', 'test(arg)') == \
|
||||
('test', 'test', [], None, None)
|
||||
('test', [], None, None)
|
||||
assert 'ignoring signature arguments' in gen.warnings[0]
|
||||
del gen.warnings[:]
|
||||
|
||||
# for functions/classes
|
||||
assert gen.resolve_name('function', 'util.raises') == \
|
||||
('util.raises', 'util', ['raises'], None, None)
|
||||
('util', ['raises'], None, None)
|
||||
assert gen.resolve_name('function', 'util.raises(exc) -> None') == \
|
||||
('util.raises', 'util', ['raises'], 'exc', ' -> None')
|
||||
('util', ['raises'], 'exc', ' -> None')
|
||||
gen.env.autodoc_current_module = 'util'
|
||||
assert gen.resolve_name('function', 'raises') == \
|
||||
('raises', 'util', ['raises'], None, None)
|
||||
('util', ['raises'], None, None)
|
||||
gen.env.autodoc_current_module = None
|
||||
gen.env.currmodule = 'util'
|
||||
assert gen.resolve_name('function', 'raises') == \
|
||||
('raises', 'util', ['raises'], None, None)
|
||||
('util', ['raises'], None, None)
|
||||
assert gen.resolve_name('class', 'TestApp') == \
|
||||
('TestApp', 'util', ['TestApp'], None, None)
|
||||
('util', ['TestApp'], None, None)
|
||||
|
||||
# for members
|
||||
gen.env.currmodule = 'foo'
|
||||
assert gen.resolve_name('method', 'util.TestApp.cleanup') == \
|
||||
('util.TestApp.cleanup', 'util', ['TestApp', 'cleanup'], None, None)
|
||||
('util', ['TestApp', 'cleanup'], None, None)
|
||||
gen.env.currmodule = 'util'
|
||||
gen.env.currclass = 'Foo'
|
||||
gen.env.autodoc_current_class = 'TestApp'
|
||||
assert gen.resolve_name('method', 'cleanup') == \
|
||||
('cleanup', 'util', ['TestApp', 'cleanup'], None, None)
|
||||
('util', ['TestApp', 'cleanup'], None, None)
|
||||
assert gen.resolve_name('method', 'TestApp.cleanup') == \
|
||||
('TestApp.cleanup', 'util', ['TestApp', 'cleanup'], None, None)
|
||||
('util', ['TestApp', 'cleanup'], None, None)
|
||||
|
||||
# and clean up
|
||||
gen.env.currmodule = None
|
||||
@ -321,17 +321,17 @@ def test_generate():
|
||||
assert_works('exception', 'test_autodoc.CustomEx', [], None)
|
||||
|
||||
# test diverse inclusion settings for members
|
||||
should = [('class', 'Class')]
|
||||
should = [('class', 'test_autodoc.Class')]
|
||||
assert_processes(should, 'class', 'Class', [], None)
|
||||
should.extend([('method', 'Class.meth')])
|
||||
should.extend([('method', 'test_autodoc.Class.meth')])
|
||||
assert_processes(should, 'class', 'Class', ['meth'], None)
|
||||
should.extend([('attribute', 'Class.prop')])
|
||||
should.extend([('attribute', 'test_autodoc.Class.prop')])
|
||||
assert_processes(should, 'class', 'Class', ['__all__'], None)
|
||||
options.undoc_members = True
|
||||
should.append(('method', 'Class.undocmeth'))
|
||||
should.append(('method', 'test_autodoc.Class.undocmeth'))
|
||||
assert_processes(should, 'class', 'Class', ['__all__'], None)
|
||||
options.inherited_members = True
|
||||
should.append(('method', 'Class.inheritedmeth'))
|
||||
should.append(('method', 'test_autodoc.Class.inheritedmeth'))
|
||||
assert_processes(should, 'class', 'Class', ['__all__'], None)
|
||||
|
||||
# test module flags
|
||||
@ -363,6 +363,12 @@ def test_generate():
|
||||
assert_result_contains('.. class:: CustomDict', 'class', 'CustomDict',
|
||||
['__all__'], None)
|
||||
|
||||
# test inner class handling
|
||||
assert_processes([('class', 'test_autodoc.Outer'),
|
||||
('class', 'test_autodoc.Outer.Inner'),
|
||||
('method', 'test_autodoc.Outer.Inner.meth')],
|
||||
'class', 'Outer', ['__all__'], None)
|
||||
|
||||
|
||||
# --- generate fodder ------------
|
||||
|
||||
@ -404,3 +410,13 @@ def function(foo, *args, **kwds):
|
||||
Return spam.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Outer(object):
|
||||
"""Foo"""
|
||||
|
||||
class Inner(object):
|
||||
"""Foo"""
|
||||
|
||||
def meth(self):
|
||||
"""Foo"""
|
||||
|
Loading…
Reference in New Issue
Block a user