Skip to content

Commit c9b9634

Browse files
authored
Merge pull request #61 from amueller/add_source_lines
Add (possibly slightly off) source lines to mangled docstrings
2 parents 22e5080 + 6546f06 commit c9b9634

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)