Add general docstring processing support with a new event in autodoc.

This commit is contained in:
Georg Brandl 2008-06-22 21:02:50 +00:00
parent abddd96093
commit c1bedfc105
7 changed files with 117 additions and 19 deletions

View File

@ -67,6 +67,10 @@ New features added
are now supported.
* Extensions:
- The autodoc extension now offers a much more flexible way to
manipulate docstrings before including them into the output, via
the new `autodoc-process-docstring` event.
- The `autodoc` extension accepts signatures for functions, methods
and classes now that override the signature got via introspection

View File

@ -131,8 +131,6 @@ latex_logo = '_static/sphinx.png'
# Documents to append as an appendix to all manuals.
#latex_appendices = []
automodule_skip_lines = 4
# Extension interface
# -------------------
@ -180,6 +178,8 @@ def parse_event(env, sig, signode):
def setup(app):
from sphinx.ext.autodoc import cut_lines
app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
app.add_description_unit('directive', 'dir', 'pair: %s; directive', parse_directive)
app.add_description_unit('role', 'role', 'pair: %s; role', parse_role)
app.add_description_unit('confval', 'confval', 'pair: %s; configuration value')

View File

@ -162,24 +162,24 @@ package.
Sphinx core events
------------------
These events are known to the core. The arguments showed are given to the
These events are known to the core. The arguments shown are given to the
registered event handlers.
.. event:: builder-inited ()
.. event:: builder-inited (app)
Emitted the builder object has been created.
.. event:: doctree-read (doctree)
.. event:: doctree-read (app, doctree)
Emitted when a doctree has been parsed and read by the environment, and is
about to be pickled.
.. event:: doctree-resolved (doctree, docname)
.. event:: doctree-resolved (app, doctree, docname)
Emitted when a doctree has been "resolved" by the environment, that is, all
references and TOCs have been inserted.
.. event:: page-context (pagename, templatename, context, doctree)
.. event:: page-context (app, pagename, templatename, context, doctree)
Emitted when the HTML builder has created a context dictionary to render a
template with -- this can be used to add custom elements to the context.

View File

@ -148,6 +148,10 @@ There are also new config values that you can set:
fields with version control tags, that you don't want to put in the generated
documentation.
.. deprecated:: 0.4
Use the more versatile docstring processing provided by
:event:`autodoc-process-docstring`.
.. confval:: autoclass_content
This value selects what content will be inserted into the main body of an
@ -164,3 +168,36 @@ There are also new config values that you can set:
Only the ``__init__`` method's docstring is inserted.
.. versionadded:: 0.3
Docstring preprocessing
-----------------------
.. versionadded:: 0.4
autodoc provides the following additional event:
.. event:: autodoc-process-docstring (app, what, name, obj, options, lines)
Emitted when autodoc has read and processed a docstring. *lines* is a list
of strings -- the lines of the processed docstring -- that the event handler
can modify **in place** to change what Sphinx puts into the output.
:param app: the Sphinx application object
:param what: the type of the object which the docstring belongs to (one of
``"module"``, ``"class"``, ``"exception"``, ``"function"``, ``"method"``,
``"attribute"``)
:param name: the fully qualified name of the object
:param obj: the object itself
:param options: the options given to the directive: an object with attributes
``inherited_members``, ``undoc_members``, ``show_inheritance`` and
``noindex`` that are true if the flag option of same name was given to the
auto directive
:param lines: the lines of the docstring, see above
The :mod:`sphinx.ext.autodoc` module provides factory functions for commonly
needed docstring processing:
.. autofunction:: cut_lines
.. autofunction:: between

View File

@ -209,6 +209,9 @@ class BuildEnvironment:
self.srcdir = srcdir
self.config = config
# the application object; only set while update() runs
self.app = None
# the docutils settings for building
self.settings = default_settings.copy()
self.settings['env'] = self
@ -420,6 +423,7 @@ class BuildEnvironment:
yield msg
self.config = config
self.app = app
# clear all files no longer present
for docname in removed:
@ -434,6 +438,8 @@ class BuildEnvironment:
self.warn(None, 'master file %s not found' %
self.doc2path(config.master_doc))
self.app = None
# remove all non-existing images from inventory
for imgsrc in self.images.keys():
if not os.access(path.join(self.srcdir, imgsrc), os.R_OK):

View File

@ -78,6 +78,51 @@ class AutodocReporter(object):
return self.system_message(4, *args, **kwargs)
# Some useful event listener factories for autodoc-process-docstring.
def cut_lines(pre, post=0, what=None):
"""
Return a listener that removes the first *pre* and last *post*
lines of every docstring. If *what* is a sequence of strings,
only docstrings of a type in *what* will be processed.
Use like this (e.g. in the ``setup()`` function of :file:`conf.py`)::
from sphinx.ext.autodoc import cut_lines
app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
This can (and should) be used in place of :confval:`automodule_skip_lines`.
"""
def process(app, what_, name, obj, options, lines):
if what and what_ not in what:
return
del lines[:pre]
if post:
del lines[-post:]
return process
def between(marker, what=None):
"""
Return a listener that only keeps lines between the first two lines that
match the *marker* regular expression. If *what* is a sequence of strings,
only docstrings of a type in *what* will be processed.
"""
marker_re = re.compile(marker)
def process(app, what_, name, obj, options, lines):
if what and what_ not in what:
return
seen = 0
for i, line in enumerate(lines[:]):
if marker_re.match(line):
if not seen:
del lines[:i+1]
seen = i+1
else:
del lines[i-seen:]
break
return process
def isdescriptor(x):
"""Check if the object is some kind of descriptor."""
for item in '__get__', '__set__', '__delete__':
@ -127,7 +172,7 @@ def get_module_charset(module):
return charset
def get_doc(what, obj, env):
def get_doc(what, name, obj, options, env):
"""Format and yield lines of the docstring(s) for the object."""
docstrings = []
if getattr(obj, '__doc__', None):
@ -168,7 +213,12 @@ def get_doc(what, obj, env):
except UnicodeError:
# last resort -- can't fail
docstring = docstring.decode('latin1')
for line in prepare_docstring(docstring):
docstringlines = prepare_docstring(docstring)
if env.app:
# let extensions preprocess docstrings
env.app.emit('autodoc-process-docstring',
what, name, obj, options, docstringlines)
for line in docstringlines:
yield line
@ -336,7 +386,7 @@ def generate_rst(what, name, members, options, add_content, document, lineno,
sourcename = 'docstring of %s' % fullname
# add content from docstrings
for i, line in enumerate(get_doc(what, todoc, env)):
for i, line in enumerate(get_doc(what, fullname, todoc, options, env)):
result.append(indent + line, sourcename, i)
# add source content, if present
@ -364,7 +414,7 @@ def generate_rst(what, name, members, options, add_content, document, lineno,
members_check_module = True
all_members = inspect.getmembers(todoc)
else:
if options.inherited:
if options.inherited_members:
# getmembers() uses dir() which pulls in members from all base classes
all_members = inspect.getmembers(todoc)
else:
@ -378,7 +428,7 @@ def generate_rst(what, name, members, options, add_content, document, lineno,
continue
# ignore undocumented members if :undoc-members: is not given
doc = getattr(member, '__doc__', None)
if not options.undoc and not doc:
if not options.undoc_members and not doc:
continue
if what == 'module':
if isinstance(member, types.FunctionType):
@ -420,11 +470,11 @@ def _auto_directive(dirname, arguments, options, content, lineno,
name = arguments[0]
genopt = Options()
members = options.get('members', [])
genopt.inherited = 'inherited-members' in options
if genopt.inherited and not members:
genopt.inherited_members = 'inherited-members' in options
if genopt.inherited_members and not members:
# :inherited-members: implies :members:
members = ['__all__']
genopt.undoc = 'undoc-members' in options
genopt.undoc_members = 'undoc-members' in options
genopt.show_inheritance = 'show-inheritance' in options
genopt.noindex = 'noindex' in options
@ -465,16 +515,16 @@ def auto_directive_withmembers(*args, **kwds):
return _auto_directive(*args, **kwds)
def members_directive(arg):
def members_option(arg):
if arg is None:
return ['__all__']
return [x.strip() for x in arg.split(',')]
def setup(app):
mod_options = {'members': members_directive, 'undoc-members': directives.flag,
mod_options = {'members': members_option, 'undoc-members': directives.flag,
'noindex': directives.flag}
cls_options = {'members': members_directive, 'undoc-members': directives.flag,
cls_options = {'members': members_option, 'undoc-members': directives.flag,
'noindex': directives.flag, 'inherited-members': directives.flag,
'show-inheritance': directives.flag}
app.add_directive('automodule', auto_directive_withmembers,
@ -489,5 +539,7 @@ def setup(app):
noindex=directives.flag)
app.add_directive('autoattribute', auto_directive, 1, (1, 0, 1),
noindex=directives.flag)
# deprecated: remove in some future version.
app.add_config_value('automodule_skip_lines', 0, True)
app.add_config_value('autoclass_content', 'class', True)
app.add_event('autodoc-process-docstring')

View File

@ -239,4 +239,3 @@ def setup(app):
app.add_config_value('coverage_c_path', [], False)
app.add_config_value('coverage_c_regexes', {}, False)
app.add_config_value('coverage_ignore_c_items', {}, False)