Skip to content

Commit 6546f06

Browse files
committed
add (possibly slightly off) source lines to mangled docstrings.
more agressive change to match up new lines with old lines. adjust expectations in docstring strip() instead of strip(" ") added really long explanation and doctest make lines actually 80 chars long and dont guess typo
1 parent 4835c31 commit 6546f06

File tree

1 file changed

+61
-3
lines changed

1 file changed

+61
-3
lines changed

numpydoc/numpydoc.py

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
raise RuntimeError("Sphinx 1.0.1 or newer is required")
3030

3131
from .docscrape_sphinx import get_doc_object, SphinxDocString
32-
from sphinx.util.compat import Directive
3332

3433
if sys.version_info[0] >= 3:
3534
sixu = lambda s: s
@@ -139,7 +138,7 @@ def setup(app, get_doc_object_=get_doc_object):
139138
# Extra mangling domains
140139
app.add_domain(NumpyPythonDomain)
141140
app.add_domain(NumpyCDomain)
142-
141+
143142
metadata = {'parallel_read_safe': True}
144143
return metadata
145144

@@ -190,6 +189,62 @@ class NumpyCDomain(ManglingDomainBase, CDomain):
190189
}
191190

192191

192+
def match_items(lines, content_old):
193+
"""Create items for mangled lines.
194+
195+
This function tries to match the lines in ``lines`` with the items (source
196+
file references and line numbers) in ``content_old``. The
197+
``mangle_docstrings`` function changes the actual docstrings, but doesn't
198+
keep track of where each line came from. The manging does many operations
199+
on the original lines, which are hard to track afterwards.
200+
201+
Many of the line changes come from deleting or inserting blank lines. This
202+
function tries to match lines by ignoring blank lines. All other changes
203+
(such as inserting figures or changes in the references) are completely
204+
ignored, so the generated line numbers will be off if ``mangle_docstrings``
205+
does anything non-trivial.
206+
207+
This is a best-effort function and the real fix would be to make
208+
``mangle_docstrings`` actually keep track of the ``items`` together with
209+
the ``lines``.
210+
211+
Examples
212+
--------
213+
>>> lines = ['', 'A', '', 'B', ' ', '', 'C', 'D']
214+
>>> lines_old = ['a', '', '', 'b', '', 'c']
215+
>>> items_old = [('file1.py', 0), ('file1.py', 1), ('file1.py', 2),
216+
... ('file2.py', 0), ('file2.py', 1), ('file2.py', 2)]
217+
>>> content_old = ViewList(lines_old, items=items_old)
218+
>>> match_items(lines, content_old) # doctest: +NORMALIZE_WHITESPACE
219+
[('file1.py', 0), ('file1.py', 0), ('file2.py', 0), ('file2.py', 0),
220+
('file2.py', 2), ('file2.py', 2), ('file2.py', 2), ('file2.py', 2)]
221+
>>> # first 2 ``lines`` are matched to 'a', second 2 to 'b', rest to 'c'
222+
>>> # actual content is completely ignored.
223+
224+
Notes
225+
-----
226+
The algorithm tries to match any line in ``lines`` with one in
227+
``lines_old``. It skips over all empty lines in ``lines_old`` and assigns
228+
this line number to all lines in ``lines``, unless a non-empty line is
229+
found in ``lines`` in which case it goes to the next line in ``lines_old``.
230+
231+
"""
232+
items_new = []
233+
lines_old = content_old.data
234+
items_old = content_old.items
235+
j = 0
236+
for i, line in enumerate(lines):
237+
# go to next non-empty line in old:
238+
# line.strip() checks whether the string is all whitespace
239+
while j < len(lines_old) - 1 and not lines_old[j].strip():
240+
j += 1
241+
items_new.append(items_old[j])
242+
if line.strip() and j < len(lines_old) - 1:
243+
j += 1
244+
assert(len(items_new) == len(lines))
245+
return items_new
246+
247+
193248
def wrap_mangling_directive(base_directive, objtype):
194249
class directive(base_directive):
195250
def run(self):
@@ -205,7 +260,10 @@ def run(self):
205260

206261
lines = list(self.content)
207262
mangle_docstrings(env.app, objtype, name, None, None, lines)
208-
self.content = ViewList(lines, self.content.parent)
263+
if self.content:
264+
items = match_items(lines, self.content)
265+
self.content = ViewList(lines, items=items,
266+
parent=self.content.parent)
209267

210268
return base_directive.run(self)
211269

0 commit comments

Comments
 (0)