Skip to content

Commit a8d6e9f

Browse files
committed
minor doc fixes
update numpydoc to upstream, include fix for line numbers try to exclude numpydoc from unit test (why would we test those?!) don't run flake8 on the sphinxext folder. make attributes bold (and literal) so we don't have to worry about trailing underscores. numpy/numpydoc#63 actually exclude numpydoc from tests
1 parent 94c2094 commit a8d6e9f

18 files changed

+3326
-138
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ inplace:
3131
test-code: in
3232
$(NOSETESTS) -s -v sklearn
3333
test-sphinxext:
34-
$(NOSETESTS) -s -v doc/sphinxext/
34+
$(NOSETESTS) -s -v doc/sphinxext/ -e numpy_ext
3535
test-doc:
3636
ifeq ($(BITS),64)
3737
$(NOSETESTS) -s -v doc/*.rst doc/modules/ doc/datasets/ \

build_tools/travis/flake8_diff.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ echo '--------------------------------------------------------------------------
121121
# We need the following command to exit with 0 hence the echo in case
122122
# there is no match
123123
MODIFIED_FILES=$(git diff --name-only $COMMIT_RANGE | grep -v 'sklearn/externals' | \
124-
grep -v 'doc/sphinxext/sphinx_gallery' || echo "no_match")
124+
grep -v 'doc/sphinxext/sphinx_gallery' | grep -v 'doc/sphinxext' || echo "no_match")
125125

126126
if [[ "$MODIFIED_FILES" == "no_match" ]]; then
127127
echo "No file outside sklearn/externals and doc/sphinxext/sphinx_gallery has been modified"

doc/conf.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@
3838
'sphinx_issues',
3939
]
4040

41+
# this is needed for some reason...
42+
# http://stackoverflow.com/questions/12206334/sphinx-autosummary-toctree-contains-reference-to-nonexisting-document-warnings
43+
numpydoc_show_class_members = False
44+
45+
4146
# pngmath / imgmath compatibility layer for different sphinx versions
4247
import sphinx
4348
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)