[Python-checkins] r64289 - doctools/trunk/sphinx/ext/autodoc.py
georg.brandl
python-checkins at python.org
Sun Jun 15 10:29:59 CEST 2008
Author: georg.brandl
Date: Sun Jun 15 10:29:58 2008
New Revision: 64289
Log:
Some refactoring and more documentation in autodoc.
Modified:
doctools/trunk/sphinx/ext/autodoc.py
Modified: doctools/trunk/sphinx/ext/autodoc.py
==============================================================================
--- doctools/trunk/sphinx/ext/autodoc.py (original)
+++ doctools/trunk/sphinx/ext/autodoc.py Sun Jun 15 10:29:58 2008
@@ -62,7 +62,7 @@
def get_module_charset(module):
- """Return the charset of the given module."""
+ """Return the charset of the given module (cached in _module_charsets)."""
if module in _module_charsets:
return _module_charsets[module]
filename = __import__(module, None, None, ['']).__file__
@@ -79,66 +79,137 @@
return charset
+def get_doc(what, obj, env):
+ """Format and yield lines of the docstring(s) for the object."""
+ docstrings = []
+ if getattr(obj, '__doc__', None):
+ docstrings.append(obj.__doc__)
+ # skip some lines in module docstrings if configured
+ if what == 'module' and env.config.automodule_skip_lines and docstrings:
+ docstrings[0] = '\n'.join(docstring.splitlines()
+ [env.config.automodule_skip_lines:])
+ # for classes, what the "docstring" is can be controlled via an option
+ if what in ('class', 'exception'):
+ content = env.config.autoclass_content
+ if content in ('both', 'init'):
+ initdocstring = getattr(obj, '__init__', None).__doc__
+ # for new-style classes, no __init__ means default __init__
+ if initdocstring == object.__init__.__doc__:
+ initdocstring = None
+ if initdocstring:
+ if content == 'init':
+ docstrings = [initdocstring]
+ else:
+ docstrings.append('\n\n' + initdocstring)
+ # the default is only the class docstring
+
+ # decode the docstrings using the module's source encoding
+ charset = None
+ module = getattr(obj, '__module__', None)
+ if module is not None:
+ charset = get_module_charset(module)
+
+ for docstring in docstrings:
+ if isinstance(docstring, str) and charset:
+ docstring = docstring.decode(charset)
+ for line in prepare_docstring(docstring):
+ yield line
+
+
+def format_signature(what, obj):
+ """Return the signature of the object, formatted for display."""
+ if what not in ('class', 'method', 'function'):
+ return ''
+ remove_self = what in ('class', 'method')
+ if what == 'class':
+ # for classes, the relevant signature is the __init__ method's
+ obj = getattr(obj, '__init__', None)
+ # classes without __init__ method?
+ if obj is None or obj is object.__init__:
+ return ''
+ argspec = inspect.getargspec(obj)
+ if remove_self and argspec[0][0:1] == ['self']:
+ del argspec[0][0]
+ return inspect.formatargspec(*argspec)
+
+
def generate_rst(what, name, members, inherited, undoc, add_content, document,
lineno, indent='', filename_set=None, check_module=False):
env = document.settings.env
- # parse the definition
+ # first, parse the definition -- auto directives for classes and functions
+ # can contain a signature which is then used instead of an autogenerated one
try:
- mod, obj, signature = py_sig_re.match(name).groups()
+ path, base, signature = py_sig_re.match(name).groups()
except:
warning = document.reporter.warning(
'invalid signature for auto%s (%r)' % (what, name), line=lineno)
return [warning], ViewList()
- basename = (mod or '') + obj
- if mod:
- mod = mod.rstrip('.')
+ # fullname is the fully qualified name, base the name after the last dot
+ fullname = (path or '') + base
+ # path is the name up to the last dot
+ path = path and path.rstrip('.')
warnings = []
- # find out what to import
+ # determine what module to import -- mod is the module name, objpath the
+ # path of names to get via getattr
+ mod = None
if what == 'module':
- mod = basename
+ mod = fullname
if signature:
warnings.append(document.reporter.warning(
'ignoring arguments for automodule %s' % mod, line=lineno))
objpath = []
elif what in ('class', 'exception', 'function'):
- if not mod and hasattr(env, 'autodoc_current_module'):
- mod = env.autodoc_current_module
- if not mod:
- mod = env.currmodule
- objpath = [obj]
+ if path:
+ mod = path
+ else:
+ # if documenting a toplevel object without explicit module, it can
+ # be contained in another auto directive ...
+ if hasattr(env, 'autodoc_current_module'):
+ mod = env.autodoc_current_module
+ # ... or in the scope of a module directive
+ if not mod:
+ mod = env.currmodule
+ objpath = [base]
else:
- mod_cls = mod
- if not mod_cls and hasattr(env, 'autodoc_current_class'):
- mod_cls = env.autodoc_current_class
- if not mod_cls:
- mod_cls = env.currclass
+ if path:
+ mod_cls = path
+ else:
+ # if documenting a class-level object without path, there must be a
+ # current class, either from a parent auto directive ...
+ if hasattr(env, 'autodoc_current_class'):
+ mod_cls = env.autodoc_current_class
+ # ... or from a class directive
+ if not mod_cls:
+ mod_cls = env.currclass
mod, cls = rpartition(mod_cls, '.')
+ # if the module name is still missing, get it like above
if not mod and hasattr(env, 'autodoc_current_module'):
mod = env.autodoc_current_module
if not mod:
mod = env.currmodule
- objpath = [cls, obj]
-
- qualname = '.'.join(objpath) or mod
- result = ViewList()
- docstrings = []
-
- # make sure that the view list starts with an empty line. This is
- # necessary for some situations where another directive preprocesses
- # rst and no starting newline is present
- result.append('', '')
+ objpath = [cls, base]
+ # by this time, a module *must* be determined
if mod is None:
warnings.append(document.reporter.warning(
'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)' % basename, line=lineno))
+ 'or giving an explicit module name)' % fullname, line=lineno))
return warnings, result
- # import module and get docstring of object to document
+ # the name to put into the generated directive -- doesn't contain the module
+ name_in_directive = '.'.join(objpath) or mod
+
+ # make sure that the view list starts with an empty line. This is
+ # necessary for some situations where another directive preprocesses
+ # reST and no starting newline is present
+ result = ViewList()
+ result.append('', '')
+
+ # now, import the module and get docstring(s) of object to document
try:
todoc = module = __import__(mod, None, None, ['foo'])
if filename_set is not None and hasattr(module, '__file__') and module.__file__:
@@ -148,42 +219,34 @@
filename_set.add(modfile)
for part in objpath:
todoc = getattr(todoc, part)
- if check_module:
- # only checking __module__ for members not given explicitly
- if hasattr(todoc, '__module__'):
- if todoc.__module__ != mod:
- return warnings, result
- if getattr(todoc, '__doc__', None):
- docstrings.append(todoc.__doc__)
except (ImportError, AttributeError):
warnings.append(document.reporter.warning(
'autodoc can\'t import/find %s %r, check your spelling '
- 'and sys.path' % (what, str(basename)), line=lineno))
+ 'and sys.path' % (what, str(fullname)), line=lineno))
return warnings, result
- # add directive header
+ # check __module__ of object if wanted (for members not given explicitly)
+ if check_module:
+ if hasattr(todoc, '__module__'):
+ if todoc.__module__ != mod:
+ return warnings, result
+
+ # format the object's signature, if any
if signature is not None:
+ # signature given explicitly -- the parentheses were stripped by the regex
args = '(%s)' % signature
else:
try:
- if what == 'class':
- args = inspect.formatargspec(*inspect.getargspec(todoc.__init__))
- if args[1:7] == 'self, ':
- args = '(' + args[7:]
- elif args == '(self)':
- args = '()'
- elif what in ('function', 'method'):
- args = inspect.formatargspec(*inspect.getargspec(todoc))
- if what == 'method':
- if args[1:7] == 'self, ':
- args = '(' + args[7:]
- elif args == '(self)':
- args = '()'
- else:
- args = ''
- except Exception:
+ args = format_signature(what, todoc)
+ except Exception, err:
+ warnings.append(document.reporter.warning(
+ 'error while formatting signature for %s: %s' %
+ (fullname, err), line=lineno))
args = ''
- result.append(indent + '.. %s:: %s%s' % (what, qualname, args), '<autodoc>')
+
+ # now, create the directive header
+ result.append(indent + '.. %s:: %s%s' % (what, name_in_directive, args),
+ '<autodoc>')
if what != 'module':
# Be explicit about the module, this is necessary since .. class:: doesn't
# support a prepended module name
@@ -194,43 +257,16 @@
if what != 'module':
indent += ' '
- # skip some lines in module docstrings if configured
- if what == 'module' and env.config.automodule_skip_lines and docstrings[0]:
- docstrings[0] = '\n'.join(docstring.splitlines()
- [env.config.automodule_skip_lines:])
-
- # for classes, what the "docstring" is can be controlled via an option
- if what in ('class', 'exception'):
- content = env.config.autoclass_content
- if content in ('both', 'init'):
- initdocstring = getattr(todoc, '__init__', None).__doc__
- # for new-style classes, no __init__ means default __init__
- if initdocstring == object.__init__.__doc__:
- initdocstring = None
- if initdocstring:
- if content == 'init':
- docstrings = [initdocstring]
- else:
- docstrings.append('\n\n' + initdocstring)
- # the default is only the class docstring
-
- # get the encoding of the docstring
- module = getattr(todoc, '__module__', None)
- if module is not None:
- charset = get_module_charset(module)
- docstrings = [isinstance(d, str) and d.decode(charset) or d for d in docstrings]
-
- # add docstring content
- for docstring in docstrings:
- docstring = prepare_docstring(docstring)
- for i, line in enumerate(docstring):
- result.append(indent + line, '<docstring of %s>' % basename, i)
+ # add content from docstrings
+ for i, line in enumerate(get_doc(what, todoc, env)):
+ result.append(indent + line, '<docstring of %s>' % fullname, i)
# add source content, if present
if add_content:
for line, src in zip(add_content.data, add_content.items):
result.append(indent + line, src[0], src[1])
+ # document members?
if not members or what in ('function', 'method', 'attribute'):
return warnings, result
@@ -243,6 +279,7 @@
_all = members == ['__all__']
members_check_module = False
if _all:
+ # unqualified :members: given
if what == 'module':
# for implicit module members, check __module__ to avoid documenting
# imported objects
@@ -258,8 +295,10 @@
else:
all_members = [(mname, getattr(todoc, mname)) for mname in members]
for (membername, member) in all_members:
+ # ignore members whose name starts with _ by default
if _all and membername.startswith('_'):
continue
+ # ignore undocumented members if :undoc-members: is not given
doc = getattr(member, '__doc__', None)
if not undoc and not doc:
continue
@@ -283,7 +322,7 @@
else:
# XXX: todo -- attribute docs
continue
- full_membername = basename + '.' + membername
+ full_membername = fullname + '.' + membername
subwarn, subres = generate_rst(memberwhat, full_membername, ['__all__'],
inherited, undoc, None, document, lineno,
indent, check_module=members_check_module)
@@ -296,10 +335,9 @@
return warnings, result
-
def _auto_directive(dirname, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
- what = dirname[4:]
+ what = dirname[4:] # strip "auto"
name = arguments[0]
members = options.get('members', [])
inherited = 'inherited-members' in options
More information about the Python-checkins
mailing list