Skip to content

add (possibly slightly off) source lines to mangled docstrings. #61

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 4, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 61 additions & 3 deletions numpydoc/numpydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
raise RuntimeError("Sphinx 1.0.1 or newer is required")

from .docscrape_sphinx import get_doc_object, SphinxDocString
from sphinx.util.compat import Directive

if sys.version_info[0] >= 3:
sixu = lambda s: s
Expand Down Expand Up @@ -139,7 +138,7 @@ def setup(app, get_doc_object_=get_doc_object):
# Extra mangling domains
app.add_domain(NumpyPythonDomain)
app.add_domain(NumpyCDomain)

metadata = {'parallel_read_safe': True}
return metadata

Expand Down Expand Up @@ -190,6 +189,62 @@ class NumpyCDomain(ManglingDomainBase, CDomain):
}


def match_items(lines, content_old):
"""Create items for mangled lines.

This function tries to match the lines in ``lines`` with the items (source
file references and line numbers) in ``content_old``. The
``mangle_docstrings`` function changes the actual docstrings, but doesn't
keep track of where each line came from. The manging does many operations
on the original lines, which are hard to track afterwards.

Many of the line changes come from deleting or inserting blank lines. This
function tries to match lines by ignoring blank lines. All other changes
(such as inserting figures or changes in the references) are completely
ignored, so the generated line numbers will be off if ``mangle_docstrings``
does anything non-trivial.

This is a best-effort function and the real fix would be to make
``mangle_docstrings`` actually keep track of the ``items`` together with
the ``lines``.

Examples
--------
>>> lines = ['', 'A', '', 'B', ' ', '', 'C', 'D']
>>> lines_old = ['a', '', '', 'b', '', 'c']
>>> items_old = [('file1.py', 0), ('file1.py', 1), ('file1.py', 2),
... ('file2.py', 0), ('file2.py', 1), ('file2.py', 2)]
>>> content_old = ViewList(lines_old, items=items_old)
>>> match_items(lines, content_old) # doctest: +NORMALIZE_WHITESPACE
[('file1.py', 0), ('file1.py', 0), ('file2.py', 0), ('file2.py', 0),
('file2.py', 2), ('file2.py', 2), ('file2.py', 2), ('file2.py', 2)]
>>> # first 2 ``lines`` are matched to 'a', second 2 to 'b', rest to 'c'
>>> # actual content is completely ignored.

Notes
-----
The algorithm tries to match any line in ``lines`` with one in
``lines_old``. It skips over all empty lines in ``lines_old`` and assigns
this line number to all lines in ``lines``, unless a non-empty line is
found in ``lines`` in which case it goes to the next line in ``lines_old``.

"""
items_new = []
lines_old = content_old.data
items_old = content_old.items
j = 0
for i, line in enumerate(lines):
# go to next non-empty line in old:
# line.strip() checks whether the string is all whitespace
while j < len(lines_old) - 1 and not lines_old[j].strip():
j += 1
items_new.append(items_old[j])
if line.strip() and j < len(lines_old) - 1:
j += 1
assert(len(items_new) == len(lines))
return items_new


def wrap_mangling_directive(base_directive, objtype):
class directive(base_directive):
def run(self):
Expand All @@ -205,7 +260,10 @@ def run(self):

lines = list(self.content)
mangle_docstrings(env.app, objtype, name, None, None, lines)
self.content = ViewList(lines, self.content.parent)
if self.content:
items = match_items(lines, self.content)
self.content = ViewList(lines, items=items,
parent=self.content.parent)

return base_directive.run(self)

Expand Down