mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Correctly report source location for docstrings included
with autodoc.
This commit is contained in:
parent
b05861454c
commit
34da6f8934
6
CHANGES
6
CHANGES
@ -42,7 +42,8 @@ New features added
|
|||||||
which makes the builder select the one that matches best.
|
which makes the builder select the one that matches best.
|
||||||
|
|
||||||
* The new config value `exclude_trees` can be used to exclude whole
|
* The new config value `exclude_trees` can be used to exclude whole
|
||||||
subtrees from the search for source files.
|
subtrees from the search for source files. Thanks to Sebastian
|
||||||
|
Wiesner.
|
||||||
|
|
||||||
* Defaults for configuration values can now be callables, which allows
|
* Defaults for configuration values can now be callables, which allows
|
||||||
dynamic defaults.
|
dynamic defaults.
|
||||||
@ -58,6 +59,9 @@ New features added
|
|||||||
Bugs fixed
|
Bugs fixed
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
* Correctly report the source location for docstrings included with
|
||||||
|
autodoc.
|
||||||
|
|
||||||
* Fix the LaTeX output of description units with multiple signatures.
|
* Fix the LaTeX output of description units with multiple signatures.
|
||||||
|
|
||||||
* Handle the figure directive in LaTeX output.
|
* Handle the figure directive in LaTeX output.
|
||||||
|
@ -33,6 +33,47 @@ _charset_re = re.compile(r'coding[:=]\s*([-\w.]+)')
|
|||||||
_module_charsets = {}
|
_module_charsets = {}
|
||||||
|
|
||||||
|
|
||||||
|
class AutodocReporter(object):
|
||||||
|
"""
|
||||||
|
A reporter replacement that assigns the correct source name
|
||||||
|
and line number to a system message, as recorded in a ViewList.
|
||||||
|
"""
|
||||||
|
def __init__(self, viewlist, reporter):
|
||||||
|
self.viewlist = viewlist
|
||||||
|
self.reporter = reporter
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return getattr(self.reporter, name)
|
||||||
|
|
||||||
|
def system_message(self, level, message, *children, **kwargs):
|
||||||
|
if 'line' in kwargs:
|
||||||
|
try:
|
||||||
|
source, line = self.viewlist.items[kwargs['line']]
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
kwargs['source'] = source
|
||||||
|
kwargs['line'] = line
|
||||||
|
return self.reporter.system_message(level, message,
|
||||||
|
*children, **kwargs)
|
||||||
|
|
||||||
|
def debug(self, *args, **kwargs):
|
||||||
|
if self.reporter.debug_flag:
|
||||||
|
return self.system_message(0, *args, **kwargs)
|
||||||
|
|
||||||
|
def info(self, *args, **kwargs):
|
||||||
|
return self.system_message(1, *args, **kwargs)
|
||||||
|
|
||||||
|
def warning(self, *args, **kwargs):
|
||||||
|
return self.system_message(2, *args, **kwargs)
|
||||||
|
|
||||||
|
def error(self, *args, **kwargs):
|
||||||
|
return self.system_message(3, *args, **kwargs)
|
||||||
|
|
||||||
|
def severe(self, *args, **kwargs):
|
||||||
|
return self.system_message(4, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
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__':
|
||||||
@ -65,8 +106,11 @@ def get_module_charset(module):
|
|||||||
"""Return the charset of the given module (cached in _module_charsets)."""
|
"""Return the charset of the given module (cached in _module_charsets)."""
|
||||||
if module in _module_charsets:
|
if module in _module_charsets:
|
||||||
return _module_charsets[module]
|
return _module_charsets[module]
|
||||||
|
try:
|
||||||
filename = __import__(module, None, None, ['']).__file__
|
filename = __import__(module, None, None, ['']).__file__
|
||||||
if filename[-4:] in ('.pyc', '.pyo'):
|
except (ImportError, AttributeError):
|
||||||
|
return None
|
||||||
|
if filename[-4:].lower() in ('.pyc', '.pyo'):
|
||||||
filename = filename[:-1]
|
filename = filename[:-1]
|
||||||
for line in [linecache.getline(filename, x) for x in (1, 2)]:
|
for line in [linecache.getline(filename, x) for x in (1, 2)]:
|
||||||
match = _charset_re.search(line)
|
match = _charset_re.search(line)
|
||||||
@ -137,6 +181,8 @@ def generate_rst(what, name, members, inherited, undoc, add_content, document,
|
|||||||
lineno, indent='', filename_set=None, check_module=False):
|
lineno, indent='', filename_set=None, check_module=False):
|
||||||
env = document.settings.env
|
env = document.settings.env
|
||||||
|
|
||||||
|
result = None
|
||||||
|
|
||||||
# first, parse the definition -- auto directives for classes and functions
|
# first, parse the definition -- auto directives for classes and functions
|
||||||
# can contain a signature which is then used instead of an autogenerated one
|
# can contain a signature which is then used instead of an autogenerated one
|
||||||
try:
|
try:
|
||||||
@ -144,7 +190,7 @@ def generate_rst(what, name, members, inherited, undoc, add_content, document,
|
|||||||
except:
|
except:
|
||||||
warning = document.reporter.warning(
|
warning = document.reporter.warning(
|
||||||
'invalid signature for auto%s (%r)' % (what, name), line=lineno)
|
'invalid signature for auto%s (%r)' % (what, name), line=lineno)
|
||||||
return [warning], ViewList()
|
return [warning], result
|
||||||
# fullname is the fully qualified name, base the name after the last dot
|
# fullname is the fully qualified name, base the name after the last dot
|
||||||
fullname = (path or '') + base
|
fullname = (path or '') + base
|
||||||
# path is the name up to the last dot
|
# path is the name up to the last dot
|
||||||
@ -203,20 +249,17 @@ def generate_rst(what, name, members, inherited, undoc, add_content, document,
|
|||||||
# the name to put into the generated directive -- doesn't contain the module
|
# the name to put into the generated directive -- doesn't contain the module
|
||||||
name_in_directive = '.'.join(objpath) or mod
|
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
|
# now, import the module and get docstring(s) of object to document
|
||||||
try:
|
try:
|
||||||
todoc = module = __import__(mod, None, None, ['foo'])
|
todoc = module = __import__(mod, None, None, ['foo'])
|
||||||
if filename_set is not None and hasattr(module, '__file__') and module.__file__:
|
if hasattr(module, '__file__') and module.__file__:
|
||||||
modfile = module.__file__
|
modfile = module.__file__
|
||||||
if modfile.lower().endswith('.pyc') or modfile.lower().endswith('.pyo'):
|
if modfile[-4:].lower() in ('.pyc', '.pyo'):
|
||||||
modfile = modfile[:-1]
|
modfile = modfile[:-1]
|
||||||
|
if filename_set is not None:
|
||||||
filename_set.add(modfile)
|
filename_set.add(modfile)
|
||||||
|
else:
|
||||||
|
modfile = None # e.g. for builtin and C modules
|
||||||
for part in objpath:
|
for part in objpath:
|
||||||
todoc = getattr(todoc, part)
|
todoc = getattr(todoc, part)
|
||||||
except (ImportError, AttributeError):
|
except (ImportError, AttributeError):
|
||||||
@ -244,6 +287,12 @@ def generate_rst(what, name, members, inherited, undoc, add_content, document,
|
|||||||
(fullname, err), line=lineno))
|
(fullname, err), line=lineno))
|
||||||
args = ''
|
args = ''
|
||||||
|
|
||||||
|
# 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, create the directive header
|
# now, create the directive header
|
||||||
result.append(indent + '.. %s:: %s%s' % (what, name_in_directive, args),
|
result.append(indent + '.. %s:: %s%s' % (what, name_in_directive, args),
|
||||||
'<autodoc>')
|
'<autodoc>')
|
||||||
@ -257,9 +306,14 @@ def generate_rst(what, name, members, inherited, undoc, add_content, document,
|
|||||||
if what != 'module':
|
if what != 'module':
|
||||||
indent += ' '
|
indent += ' '
|
||||||
|
|
||||||
|
if modfile:
|
||||||
|
sourcename = '%s:docstring of %s' % (modfile, fullname)
|
||||||
|
else:
|
||||||
|
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, todoc, env)):
|
||||||
result.append(indent + line, '<docstring of %s>' % fullname, i)
|
result.append(indent + line, sourcename, i)
|
||||||
|
|
||||||
# add source content, if present
|
# add source content, if present
|
||||||
if add_content:
|
if add_content:
|
||||||
@ -349,12 +403,17 @@ def _auto_directive(dirname, arguments, options, content, lineno,
|
|||||||
filename_set = set()
|
filename_set = set()
|
||||||
warnings, result = generate_rst(what, name, members, inherited, undoc, content,
|
warnings, result = generate_rst(what, name, members, inherited, undoc, content,
|
||||||
state.document, lineno, filename_set=filename_set)
|
state.document, lineno, filename_set=filename_set)
|
||||||
|
if result is None:
|
||||||
|
return warnings
|
||||||
|
|
||||||
# record all filenames as dependencies -- this will at least partially make
|
# record all filenames as dependencies -- this will at least partially make
|
||||||
# automatic invalidation possible
|
# automatic invalidation possible
|
||||||
for fn in filename_set:
|
for fn in filename_set:
|
||||||
state.document.settings.env.note_dependency(fn)
|
state.document.settings.env.note_dependency(fn)
|
||||||
|
|
||||||
|
# use a custom reporter that correctly assigns lines to source and lineno
|
||||||
|
old_reporter = state.memo.reporter
|
||||||
|
state.memo.reporter = AutodocReporter(result, state.memo.reporter)
|
||||||
if dirname == 'automodule':
|
if dirname == 'automodule':
|
||||||
node = nodes.section()
|
node = nodes.section()
|
||||||
# hack around title style bookkeeping
|
# hack around title style bookkeeping
|
||||||
@ -362,12 +421,13 @@ def _auto_directive(dirname, arguments, options, content, lineno,
|
|||||||
surrounding_section_level = state.memo.section_level
|
surrounding_section_level = state.memo.section_level
|
||||||
state.memo.title_styles = []
|
state.memo.title_styles = []
|
||||||
state.memo.section_level = 0
|
state.memo.section_level = 0
|
||||||
state.nested_parse(result, content_offset, node, match_titles=1)
|
state.nested_parse(result, 0, node, match_titles=1)
|
||||||
state.memo.title_styles = surrounding_title_styles
|
state.memo.title_styles = surrounding_title_styles
|
||||||
state.memo.section_level = surrounding_section_level
|
state.memo.section_level = surrounding_section_level
|
||||||
else:
|
else:
|
||||||
node = nodes.paragraph()
|
node = nodes.paragraph()
|
||||||
state.nested_parse(result, content_offset, node)
|
state.nested_parse(result, 0, node)
|
||||||
|
state.memo.reporter = old_reporter
|
||||||
return warnings + node.children
|
return warnings + node.children
|
||||||
|
|
||||||
def auto_directive(*args, **kwds):
|
def auto_directive(*args, **kwds):
|
||||||
|
Loading…
Reference in New Issue
Block a user