Skip to content

Commit

Permalink
minor doc fixes
Browse files Browse the repository at this point in the history
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
  • Loading branch information
amueller committed Oct 27, 2016
1 parent 94c2094 commit a8d6e9f
Show file tree
Hide file tree
Showing 18 changed files with 3,326 additions and 138 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ inplace:
test-code: in
$(NOSETESTS) -s -v sklearn
test-sphinxext:
$(NOSETESTS) -s -v doc/sphinxext/
$(NOSETESTS) -s -v doc/sphinxext/ -e numpy_ext
test-doc:
ifeq ($(BITS),64)
$(NOSETESTS) -s -v doc/*.rst doc/modules/ doc/datasets/ \
Expand Down
2 changes: 1 addition & 1 deletion build_tools/travis/flake8_diff.sh
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ echo '--------------------------------------------------------------------------
# We need the following command to exit with 0 hence the echo in case
# there is no match
MODIFIED_FILES=$(git diff --name-only $COMMIT_RANGE | grep -v 'sklearn/externals' | \
grep -v 'doc/sphinxext/sphinx_gallery' || echo "no_match")
grep -v 'doc/sphinxext/sphinx_gallery' | grep -v 'doc/sphinxext' || echo "no_match")

if [[ "$MODIFIED_FILES" == "no_match" ]]; then
echo "No file outside sklearn/externals and doc/sphinxext/sphinx_gallery has been modified"
Expand Down
5 changes: 5 additions & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
'sphinx_issues',
]

# this is needed for some reason...
# http://stackoverflow.com/questions/12206334/sphinx-autosummary-toctree-contains-reference-to-nonexisting-document-warnings
numpydoc_show_class_members = False


# pngmath / imgmath compatibility layer for different sphinx versions
import sphinx
from distutils.version import LooseVersion
Expand Down
3 changes: 3 additions & 0 deletions doc/sphinxext/numpy_ext/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from __future__ import division, absolute_import, print_function

from .numpydoc import setup
169 changes: 169 additions & 0 deletions doc/sphinxext/numpy_ext/comment_eater.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
from __future__ import division, absolute_import, print_function

import sys
if sys.version_info[0] >= 3:
from io import StringIO
else:
from io import StringIO

import compiler
import inspect
import textwrap
import tokenize

from .compiler_unparse import unparse


class Comment(object):
""" A comment block.
"""
is_comment = True
def __init__(self, start_lineno, end_lineno, text):
# int : The first line number in the block. 1-indexed.
self.start_lineno = start_lineno
# int : The last line number. Inclusive!
self.end_lineno = end_lineno
# str : The text block including '#' character but not any leading spaces.
self.text = text

def add(self, string, start, end, line):
""" Add a new comment line.
"""
self.start_lineno = min(self.start_lineno, start[0])
self.end_lineno = max(self.end_lineno, end[0])
self.text += string

def __repr__(self):
return '%s(%r, %r, %r)' % (self.__class__.__name__, self.start_lineno,
self.end_lineno, self.text)


class NonComment(object):
""" A non-comment block of code.
"""
is_comment = False
def __init__(self, start_lineno, end_lineno):
self.start_lineno = start_lineno
self.end_lineno = end_lineno

def add(self, string, start, end, line):
""" Add lines to the block.
"""
if string.strip():
# Only add if not entirely whitespace.
self.start_lineno = min(self.start_lineno, start[0])
self.end_lineno = max(self.end_lineno, end[0])

def __repr__(self):
return '%s(%r, %r)' % (self.__class__.__name__, self.start_lineno,
self.end_lineno)


class CommentBlocker(object):
""" Pull out contiguous comment blocks.
"""
def __init__(self):
# Start with a dummy.
self.current_block = NonComment(0, 0)

# All of the blocks seen so far.
self.blocks = []

# The index mapping lines of code to their associated comment blocks.
self.index = {}

def process_file(self, file):
""" Process a file object.
"""
if sys.version_info[0] >= 3:
nxt = file.__next__
else:
nxt = file.next
for token in tokenize.generate_tokens(nxt):
self.process_token(*token)
self.make_index()

def process_token(self, kind, string, start, end, line):
""" Process a single token.
"""
if self.current_block.is_comment:
if kind == tokenize.COMMENT:
self.current_block.add(string, start, end, line)
else:
self.new_noncomment(start[0], end[0])
else:
if kind == tokenize.COMMENT:
self.new_comment(string, start, end, line)
else:
self.current_block.add(string, start, end, line)

def new_noncomment(self, start_lineno, end_lineno):
""" We are transitioning from a noncomment to a comment.
"""
block = NonComment(start_lineno, end_lineno)
self.blocks.append(block)
self.current_block = block

def new_comment(self, string, start, end, line):
""" Possibly add a new comment.
Only adds a new comment if this comment is the only thing on the line.
Otherwise, it extends the noncomment block.
"""
prefix = line[:start[1]]
if prefix.strip():
# Oops! Trailing comment, not a comment block.
self.current_block.add(string, start, end, line)
else:
# A comment block.
block = Comment(start[0], end[0], string)
self.blocks.append(block)
self.current_block = block

def make_index(self):
""" Make the index mapping lines of actual code to their associated
prefix comments.
"""
for prev, block in zip(self.blocks[:-1], self.blocks[1:]):
if not block.is_comment:
self.index[block.start_lineno] = prev

def search_for_comment(self, lineno, default=None):
""" Find the comment block just before the given line number.
Returns None (or the specified default) if there is no such block.
"""
if not self.index:
self.make_index()
block = self.index.get(lineno, None)
text = getattr(block, 'text', default)
return text


def strip_comment_marker(text):
""" Strip # markers at the front of a block of comment text.
"""
lines = []
for line in text.splitlines():
lines.append(line.lstrip('#'))
text = textwrap.dedent('\n'.join(lines))
return text


def get_class_traits(klass):
""" Yield all of the documentation for trait definitions on a class object.
"""
# FIXME: gracefully handle errors here or in the caller?
source = inspect.getsource(klass)
cb = CommentBlocker()
cb.process_file(StringIO(source))
mod_ast = compiler.parse(source)
class_ast = mod_ast.node.nodes[0]
for node in class_ast.code.nodes:
# FIXME: handle other kinds of assignments?
if isinstance(node, compiler.ast.Assign):
name = node.nodes[0].name
rhs = unparse(node.expr).strip()
doc = strip_comment_marker(cb.search_for_comment(node.lineno, default=''))
yield name, rhs, doc

Loading

0 comments on commit a8d6e9f

Please sign in to comment.