Skip to content

Commit e1c5820

Browse files
committed
update numpydoc to upstream, include fix for line numbers
1 parent 6c12514 commit e1c5820

16 files changed

+3324
-136
lines changed

doc/conf.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@
3737
'sphinx_gallery.gen_gallery',
3838
]
3939

40+
# this is needed for some reason...
41+
# http://stackoverflow.com/questions/12206334/sphinx-autosummary-toctree-contains-reference-to-nonexisting-document-warnings
42+
numpydoc_show_class_members = False
43+
44+
4045
# pngmath / imgmath compatibility layer for different sphinx versions
4146
import sphinx
4247
from distutils.version import LooseVersion

doc/sphinxext/numpy_ext/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from __future__ import division, absolute_import, print_function
2+
3+
from .numpydoc import setup
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
from __future__ import division, absolute_import, print_function
2+
3+
import sys
4+
if sys.version_info[0] >= 3:
5+
from io import StringIO
6+
else:
7+
from io import StringIO
8+
9+
import compiler
10+
import inspect
11+
import textwrap
12+
import tokenize
13+
14+
from .compiler_unparse import unparse
15+
16+
17+
class Comment(object):
18+
""" A comment block.
19+
"""
20+
is_comment = True
21+
def __init__(self, start_lineno, end_lineno, text):
22+
# int : The first line number in the block. 1-indexed.
23+
self.start_lineno = start_lineno
24+
# int : The last line number. Inclusive!
25+
self.end_lineno = end_lineno
26+
# str : The text block including '#' character but not any leading spaces.
27+
self.text = text
28+
29+
def add(self, string, start, end, line):
30+
""" Add a new comment line.
31+
"""
32+
self.start_lineno = min(self.start_lineno, start[0])
33+
self.end_lineno = max(self.end_lineno, end[0])
34+
self.text += string
35+
36+
def __repr__(self):
37+
return '%s(%r, %r, %r)' % (self.__class__.__name__, self.start_lineno,
38+
self.end_lineno, self.text)
39+
40+
41+
class NonComment(object):
42+
""" A non-comment block of code.
43+
"""
44+
is_comment = False
45+
def __init__(self, start_lineno, end_lineno):
46+
self.start_lineno = start_lineno
47+
self.end_lineno = end_lineno
48+
49+
def add(self, string, start, end, line):
50+
""" Add lines to the block.
51+
"""
52+
if string.strip():
53+
# Only add if not entirely whitespace.
54+
self.start_lineno = min(self.start_lineno, start[0])
55+
self.end_lineno = max(self.end_lineno, end[0])
56+
57+
def __repr__(self):
58+
return '%s(%r, %r)' % (self.__class__.__name__, self.start_lineno,
59+
self.end_lineno)
60+
61+
62+
class CommentBlocker(object):
63+
""" Pull out contiguous comment blocks.
64+
"""
65+
def __init__(self):
66+
# Start with a dummy.
67+
self.current_block = NonComment(0, 0)
68+
69+
# All of the blocks seen so far.
70+
self.blocks = []
71+
72+
# The index mapping lines of code to their associated comment blocks.
73+
self.index = {}
74+
75+
def process_file(self, file):
76+
""" Process a file object.
77+
"""
78+
if sys.version_info[0] >= 3:
79+
nxt = file.__next__
80+
else:
81+
nxt = file.next
82+
for token in tokenize.generate_tokens(nxt):
83+
self.process_token(*token)
84+
self.make_index()
85+
86+
def process_token(self, kind, string, start, end, line):
87+
""" Process a single token.
88+
"""
89+
if self.current_block.is_comment:
90+
if kind == tokenize.COMMENT:
91+
self.current_block.add(string, start, end, line)
92+
else:
93+
self.new_noncomment(start[0], end[0])
94+
else:
95+
if kind == tokenize.COMMENT:
96+
self.new_comment(string, start, end, line)
97+
else:
98+
self.current_block.add(string, start, end, line)
99+
100+
def new_noncomment(self, start_lineno, end_lineno):
101+
""" We are transitioning from a noncomment to a comment.
102+
"""
103+
block = NonComment(start_lineno, end_lineno)
104+
self.blocks.append(block)
105+
self.current_block = block
106+
107+
def new_comment(self, string, start, end, line):
108+
""" Possibly add a new comment.
109+
110+
Only adds a new comment if this comment is the only thing on the line.
111+
Otherwise, it extends the noncomment block.
112+
"""
113+
prefix = line[:start[1]]
114+
if prefix.strip():
115+
# Oops! Trailing comment, not a comment block.
116+
self.current_block.add(string, start, end, line)
117+
else:
118+
# A comment block.
119+
block = Comment(start[0], end[0], string)
120+
self.blocks.append(block)
121+
self.current_block = block
122+
123+
def make_index(self):
124+
""" Make the index mapping lines of actual code to their associated
125+
prefix comments.
126+
"""
127+
for prev, block in zip(self.blocks[:-1], self.blocks[1:]):
128+
if not block.is_comment:
129+
self.index[block.start_lineno] = prev
130+
131+
def search_for_comment(self, lineno, default=None):
132+
""" Find the comment block just before the given line number.
133+
134+
Returns None (or the specified default) if there is no such block.
135+
"""
136+
if not self.index:
137+
self.make_index()
138+
block = self.index.get(lineno, None)
139+
text = getattr(block, 'text', default)
140+
return text
141+
142+
143+
def strip_comment_marker(text):
144+
""" Strip # markers at the front of a block of comment text.
145+
"""
146+
lines = []
147+
for line in text.splitlines():
148+
lines.append(line.lstrip('#'))
149+
text = textwrap.dedent('\n'.join(lines))
150+
return text
151+
152+
153+
def get_class_traits(klass):
154+
""" Yield all of the documentation for trait definitions on a class object.
155+
"""
156+
# FIXME: gracefully handle errors here or in the caller?
157+
source = inspect.getsource(klass)
158+
cb = CommentBlocker()
159+
cb.process_file(StringIO(source))
160+
mod_ast = compiler.parse(source)
161+
class_ast = mod_ast.node.nodes[0]
162+
for node in class_ast.code.nodes:
163+
# FIXME: handle other kinds of assignments?
164+
if isinstance(node, compiler.ast.Assign):
165+
name = node.nodes[0].name
166+
rhs = unparse(node.expr).strip()
167+
doc = strip_comment_marker(cb.search_for_comment(node.lineno, default=''))
168+
yield name, rhs, doc
169+

0 commit comments

Comments
 (0)