mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Add general docstring processing support with a new event in autodoc.
This commit is contained in:
parent
abddd96093
commit
c1bedfc105
4
CHANGES
4
CHANGES
@ -67,6 +67,10 @@ New features added
|
|||||||
are now supported.
|
are now supported.
|
||||||
|
|
||||||
* Extensions:
|
* 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
|
- The `autodoc` extension accepts signatures for functions, methods
|
||||||
and classes now that override the signature got via introspection
|
and classes now that override the signature got via introspection
|
||||||
|
@ -131,8 +131,6 @@ latex_logo = '_static/sphinx.png'
|
|||||||
# Documents to append as an appendix to all manuals.
|
# Documents to append as an appendix to all manuals.
|
||||||
#latex_appendices = []
|
#latex_appendices = []
|
||||||
|
|
||||||
automodule_skip_lines = 4
|
|
||||||
|
|
||||||
|
|
||||||
# Extension interface
|
# Extension interface
|
||||||
# -------------------
|
# -------------------
|
||||||
@ -180,6 +178,8 @@ def parse_event(env, sig, signode):
|
|||||||
|
|
||||||
|
|
||||||
def setup(app):
|
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('directive', 'dir', 'pair: %s; directive', parse_directive)
|
||||||
app.add_description_unit('role', 'role', 'pair: %s; role', parse_role)
|
app.add_description_unit('role', 'role', 'pair: %s; role', parse_role)
|
||||||
app.add_description_unit('confval', 'confval', 'pair: %s; configuration value')
|
app.add_description_unit('confval', 'confval', 'pair: %s; configuration value')
|
||||||
|
@ -162,24 +162,24 @@ package.
|
|||||||
Sphinx core events
|
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.
|
registered event handlers.
|
||||||
|
|
||||||
.. event:: builder-inited ()
|
.. event:: builder-inited (app)
|
||||||
|
|
||||||
Emitted the builder object has been created.
|
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
|
Emitted when a doctree has been parsed and read by the environment, and is
|
||||||
about to be pickled.
|
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
|
Emitted when a doctree has been "resolved" by the environment, that is, all
|
||||||
references and TOCs have been inserted.
|
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
|
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.
|
template with -- this can be used to add custom elements to the context.
|
||||||
|
@ -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
|
fields with version control tags, that you don't want to put in the generated
|
||||||
documentation.
|
documentation.
|
||||||
|
|
||||||
|
.. deprecated:: 0.4
|
||||||
|
Use the more versatile docstring processing provided by
|
||||||
|
:event:`autodoc-process-docstring`.
|
||||||
|
|
||||||
.. confval:: autoclass_content
|
.. confval:: autoclass_content
|
||||||
|
|
||||||
This value selects what content will be inserted into the main body of an
|
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.
|
Only the ``__init__`` method's docstring is inserted.
|
||||||
|
|
||||||
.. versionadded:: 0.3
|
.. 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
|
||||||
|
@ -209,6 +209,9 @@ class BuildEnvironment:
|
|||||||
self.srcdir = srcdir
|
self.srcdir = srcdir
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
|
# the application object; only set while update() runs
|
||||||
|
self.app = None
|
||||||
|
|
||||||
# the docutils settings for building
|
# the docutils settings for building
|
||||||
self.settings = default_settings.copy()
|
self.settings = default_settings.copy()
|
||||||
self.settings['env'] = self
|
self.settings['env'] = self
|
||||||
@ -420,6 +423,7 @@ class BuildEnvironment:
|
|||||||
yield msg
|
yield msg
|
||||||
|
|
||||||
self.config = config
|
self.config = config
|
||||||
|
self.app = app
|
||||||
|
|
||||||
# clear all files no longer present
|
# clear all files no longer present
|
||||||
for docname in removed:
|
for docname in removed:
|
||||||
@ -434,6 +438,8 @@ class BuildEnvironment:
|
|||||||
self.warn(None, 'master file %s not found' %
|
self.warn(None, 'master file %s not found' %
|
||||||
self.doc2path(config.master_doc))
|
self.doc2path(config.master_doc))
|
||||||
|
|
||||||
|
self.app = None
|
||||||
|
|
||||||
# remove all non-existing images from inventory
|
# remove all non-existing images from inventory
|
||||||
for imgsrc in self.images.keys():
|
for imgsrc in self.images.keys():
|
||||||
if not os.access(path.join(self.srcdir, imgsrc), os.R_OK):
|
if not os.access(path.join(self.srcdir, imgsrc), os.R_OK):
|
||||||
|
@ -78,6 +78,51 @@ class AutodocReporter(object):
|
|||||||
return self.system_message(4, *args, **kwargs)
|
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):
|
def isdescriptor(x):
|
||||||
"""Check if the object is some kind of descriptor."""
|
"""Check if the object is some kind of descriptor."""
|
||||||
for item in '__get__', '__set__', '__delete__':
|
for item in '__get__', '__set__', '__delete__':
|
||||||
@ -127,7 +172,7 @@ def get_module_charset(module):
|
|||||||
return charset
|
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."""
|
"""Format and yield lines of the docstring(s) for the object."""
|
||||||
docstrings = []
|
docstrings = []
|
||||||
if getattr(obj, '__doc__', None):
|
if getattr(obj, '__doc__', None):
|
||||||
@ -168,7 +213,12 @@ def get_doc(what, obj, env):
|
|||||||
except UnicodeError:
|
except UnicodeError:
|
||||||
# last resort -- can't fail
|
# last resort -- can't fail
|
||||||
docstring = docstring.decode('latin1')
|
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
|
yield line
|
||||||
|
|
||||||
|
|
||||||
@ -336,7 +386,7 @@ def generate_rst(what, name, members, options, add_content, document, lineno,
|
|||||||
sourcename = 'docstring of %s' % fullname
|
sourcename = 'docstring of %s' % fullname
|
||||||
|
|
||||||
# add content from docstrings
|
# 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)
|
result.append(indent + line, sourcename, i)
|
||||||
|
|
||||||
# add source content, if present
|
# add source content, if present
|
||||||
@ -364,7 +414,7 @@ def generate_rst(what, name, members, options, add_content, document, lineno,
|
|||||||
members_check_module = True
|
members_check_module = True
|
||||||
all_members = inspect.getmembers(todoc)
|
all_members = inspect.getmembers(todoc)
|
||||||
else:
|
else:
|
||||||
if options.inherited:
|
if options.inherited_members:
|
||||||
# getmembers() uses dir() which pulls in members from all base classes
|
# getmembers() uses dir() which pulls in members from all base classes
|
||||||
all_members = inspect.getmembers(todoc)
|
all_members = inspect.getmembers(todoc)
|
||||||
else:
|
else:
|
||||||
@ -378,7 +428,7 @@ def generate_rst(what, name, members, options, add_content, document, lineno,
|
|||||||
continue
|
continue
|
||||||
# ignore undocumented members if :undoc-members: is not given
|
# ignore undocumented members if :undoc-members: is not given
|
||||||
doc = getattr(member, '__doc__', None)
|
doc = getattr(member, '__doc__', None)
|
||||||
if not options.undoc and not doc:
|
if not options.undoc_members and not doc:
|
||||||
continue
|
continue
|
||||||
if what == 'module':
|
if what == 'module':
|
||||||
if isinstance(member, types.FunctionType):
|
if isinstance(member, types.FunctionType):
|
||||||
@ -420,11 +470,11 @@ def _auto_directive(dirname, arguments, options, content, lineno,
|
|||||||
name = arguments[0]
|
name = arguments[0]
|
||||||
genopt = Options()
|
genopt = Options()
|
||||||
members = options.get('members', [])
|
members = options.get('members', [])
|
||||||
genopt.inherited = 'inherited-members' in options
|
genopt.inherited_members = 'inherited-members' in options
|
||||||
if genopt.inherited and not members:
|
if genopt.inherited_members and not members:
|
||||||
# :inherited-members: implies :members:
|
# :inherited-members: implies :members:
|
||||||
members = ['__all__']
|
members = ['__all__']
|
||||||
genopt.undoc = 'undoc-members' in options
|
genopt.undoc_members = 'undoc-members' in options
|
||||||
genopt.show_inheritance = 'show-inheritance' in options
|
genopt.show_inheritance = 'show-inheritance' in options
|
||||||
genopt.noindex = 'noindex' in options
|
genopt.noindex = 'noindex' in options
|
||||||
|
|
||||||
@ -465,16 +515,16 @@ def auto_directive_withmembers(*args, **kwds):
|
|||||||
return _auto_directive(*args, **kwds)
|
return _auto_directive(*args, **kwds)
|
||||||
|
|
||||||
|
|
||||||
def members_directive(arg):
|
def members_option(arg):
|
||||||
if arg is None:
|
if arg is None:
|
||||||
return ['__all__']
|
return ['__all__']
|
||||||
return [x.strip() for x in arg.split(',')]
|
return [x.strip() for x in arg.split(',')]
|
||||||
|
|
||||||
|
|
||||||
def setup(app):
|
def setup(app):
|
||||||
mod_options = {'members': members_directive, 'undoc-members': directives.flag,
|
mod_options = {'members': members_option, 'undoc-members': directives.flag,
|
||||||
'noindex': 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,
|
'noindex': directives.flag, 'inherited-members': directives.flag,
|
||||||
'show-inheritance': directives.flag}
|
'show-inheritance': directives.flag}
|
||||||
app.add_directive('automodule', auto_directive_withmembers,
|
app.add_directive('automodule', auto_directive_withmembers,
|
||||||
@ -489,5 +539,7 @@ def setup(app):
|
|||||||
noindex=directives.flag)
|
noindex=directives.flag)
|
||||||
app.add_directive('autoattribute', auto_directive, 1, (1, 0, 1),
|
app.add_directive('autoattribute', auto_directive, 1, (1, 0, 1),
|
||||||
noindex=directives.flag)
|
noindex=directives.flag)
|
||||||
|
# deprecated: remove in some future version.
|
||||||
app.add_config_value('automodule_skip_lines', 0, True)
|
app.add_config_value('automodule_skip_lines', 0, True)
|
||||||
app.add_config_value('autoclass_content', 'class', True)
|
app.add_config_value('autoclass_content', 'class', True)
|
||||||
|
app.add_event('autodoc-process-docstring')
|
||||||
|
@ -239,4 +239,3 @@ def setup(app):
|
|||||||
app.add_config_value('coverage_c_path', [], False)
|
app.add_config_value('coverage_c_path', [], False)
|
||||||
app.add_config_value('coverage_c_regexes', {}, False)
|
app.add_config_value('coverage_c_regexes', {}, False)
|
||||||
app.add_config_value('coverage_ignore_c_items', {}, False)
|
app.add_config_value('coverage_ignore_c_items', {}, False)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user