diff --git a/doc/sphinxext/MANIFEST.in b/doc/sphinxext/MANIFEST.in deleted file mode 100755 index f88ed785c525f..0000000000000 --- a/doc/sphinxext/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -recursive-include tests *.py -include *.txt diff --git a/doc/sphinxext/__init__.py b/doc/sphinxext/__init__.py deleted file mode 100755 index ae9073bc4115f..0000000000000 --- a/doc/sphinxext/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from numpydoc import setup diff --git a/doc/sphinxext/LICENSE.txt b/doc/sphinxext/numpydoc/LICENSE.txt similarity index 97% rename from doc/sphinxext/LICENSE.txt rename to doc/sphinxext/numpydoc/LICENSE.txt index e00efc31ec257..b15c699dcecaa 100755 --- a/doc/sphinxext/LICENSE.txt +++ b/doc/sphinxext/numpydoc/LICENSE.txt @@ -1,8 +1,6 @@ ------------------------------------------------------------------------------- The files - numpydoc.py - - autosummary.py - - autosummary_generate.py - docscrape.py - docscrape_sphinx.py - phantom_import.py @@ -71,10 +69,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------- - The files - - only_directives.py + The file - plot_directive.py - originate from Matplotlib (http://matplotlib.sf.net/) which has + originates from Matplotlib (http://matplotlib.sf.net/) which has the following license: Copyright (c) 2002-2008 John D. Hunter; All Rights Reserved. diff --git a/doc/sphinxext/README.txt b/doc/sphinxext/numpydoc/README.rst similarity index 79% rename from doc/sphinxext/README.txt rename to doc/sphinxext/numpydoc/README.rst index f3d782c9557d4..89b9f2fd23e9b 100755 --- a/doc/sphinxext/README.txt +++ b/doc/sphinxext/numpydoc/README.rst @@ -14,17 +14,10 @@ The following extensions are available: - ``numpydoc.traitsdoc``: For gathering documentation about Traits attributes. - - ``numpydoc.plot_directives``: Adaptation of Matplotlib's ``plot::`` + - ``numpydoc.plot_directive``: Adaptation of Matplotlib's ``plot::`` directive. Note that this implementation may still undergo severe changes or eventually be deprecated. - - ``numpydoc.only_directives``: (DEPRECATED) - - - ``numpydoc.autosummary``: (DEPRECATED) An ``autosummary::`` directive. - Available in Sphinx 0.6.2 and (to-be) 1.0 as ``sphinx.ext.autosummary``, - and it the Sphinx 1.0 version is recommended over that included in - Numpydoc. - numpydoc ======== @@ -47,6 +40,12 @@ The following options can be set in conf.py: Whether to show all members of a class in the Methods and Attributes sections automatically. +- numpydoc_class_members_toctree: bool + + Whether to create a Sphinx table of contents for the lists of class + methods and attributes. If a table of contents is made, Sphinx expects + each entry to have a separate page. + - numpydoc_edit_link: bool (DEPRECATED -- edit your HTML template instead) Whether to insert an edit link after docstrings. diff --git a/doc/sphinxext/numpydoc/__init__.py b/doc/sphinxext/numpydoc/__init__.py new file mode 100755 index 0000000000000..0fce2cf747e23 --- /dev/null +++ b/doc/sphinxext/numpydoc/__init__.py @@ -0,0 +1,3 @@ +from __future__ import division, absolute_import, print_function + +from .numpydoc import setup diff --git a/doc/sphinxext/comment_eater.py b/doc/sphinxext/numpydoc/comment_eater.py similarity index 89% rename from doc/sphinxext/comment_eater.py rename to doc/sphinxext/numpydoc/comment_eater.py index af1e21d7bb4ee..8cddd3305f0bc 100755 --- a/doc/sphinxext/comment_eater.py +++ b/doc/sphinxext/numpydoc/comment_eater.py @@ -1,25 +1,29 @@ -from cStringIO import StringIO +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 +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. + # str : The text block including '#' character but not any leading spaces. self.text = text def add(self, string, start, end, line): @@ -31,15 +35,13 @@ def add(self, string, start, end, line): def __repr__(self): return '%s(%r, %r, %r)' % (self.__class__.__name__, self.start_lineno, - self.end_lineno, self.text) + 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 @@ -54,14 +56,12 @@ def add(self, string, start, end, line): def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.start_lineno, - self.end_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) @@ -75,7 +75,11 @@ def __init__(self): def process_file(self, file): """ Process a file object. """ - for token in tokenize.generate_tokens(file.next): + 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() @@ -160,6 +164,6 @@ def get_class_traits(klass): 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='')) + doc = strip_comment_marker(cb.search_for_comment(node.lineno, default='')) yield name, rhs, doc + diff --git a/doc/sphinxext/compiler_unparse.py b/doc/sphinxext/numpydoc/compiler_unparse.py similarity index 83% rename from doc/sphinxext/compiler_unparse.py rename to doc/sphinxext/numpydoc/compiler_unparse.py index 8233e968071ec..8933a83db3f23 100755 --- a/doc/sphinxext/compiler_unparse.py +++ b/doc/sphinxext/numpydoc/compiler_unparse.py @@ -10,34 +10,35 @@ fixme: We may want to move to using _ast trees because the compiler for them is about 6 times faster than compiler.compile. """ +from __future__ import division, absolute_import, print_function import sys -import cStringIO from compiler.ast import Const, Name, Tuple, Div, Mul, Sub, Add +if sys.version_info[0] >= 3: + from io import StringIO +else: + from StringIO import StringIO def unparse(ast, single_line_functions=False): - s = cStringIO.StringIO() + s = StringIO() UnparseCompilerAst(ast, s, single_line_functions) return s.getvalue().lstrip() -op_precedence = { - 'compiler.ast.Power': 3, 'compiler.ast.Mul': 2, 'compiler.ast.Div': 2, - 'compiler.ast.Add': 1, 'compiler.ast.Sub': 1} - +op_precedence = { 'compiler.ast.Power':3, 'compiler.ast.Mul':2, 'compiler.ast.Div':2, + 'compiler.ast.Add':1, 'compiler.ast.Sub':1 } class UnparseCompilerAst: - """ Methods in this class recursively traverse an AST and output source code for the abstract syntax; original formatting is disregarged. """ - # + ######################################################################### # object interface. - # + ######################################################################### - def __init__(self, tree, file=sys.stdout, single_line_functions=False): + def __init__(self, tree, file = sys.stdout, single_line_functions=False): """ Unparser(tree, file=sys.stdout) -> None. Print the source for tree to file. @@ -50,16 +51,16 @@ def __init__(self, tree, file=sys.stdout, single_line_functions=False): self._write("\n") self.f.flush() - # + ######################################################################### # Unparser private interface. - # + ######################################################################### - # format, output, and dispatch methods ################################ + ### format, output, and dispatch methods ################################ - def _fill(self, text=""): + def _fill(self, text = ""): "Indent a piece of text, according to the current indentation level" if self._do_indent: - self._write("\n" + " " * self._indent + text) + self._write("\n"+" "*self._indent + text) else: self._write(text) @@ -82,17 +83,19 @@ def _dispatch(self, tree): for t in tree: self._dispatch(t) return - meth = getattr(self, "_" + tree.__class__.__name__) + meth = getattr(self, "_"+tree.__class__.__name__) if tree.__class__.__name__ == 'NoneType' and not self._do_indent: return meth(tree) - # + + ######################################################################### # compiler.ast unparsing methods. # # There should be one method per concrete grammar type. They are # organized in alphabetical order. - # + ######################################################################### + def _Add(self, t): self.__binary_op(t, '+') @@ -100,7 +103,7 @@ def _And(self, t): self._write(" (") for i, node in enumerate(t.nodes): self._dispatch(node) - if i != len(t.nodes) - 1: + if i != len(t.nodes)-1: self._write(") and (") self._write(")") @@ -108,7 +111,7 @@ def _AssAttr(self, t): """ Handle assigning an attribute of an object """ self._dispatch(t.expr) - self._write('.' + t.attrname) + self._write('.'+t.attrname) def _Assign(self, t): """ Expression Assignment such as "a = 1". @@ -150,7 +153,7 @@ def _AugAssign(self, t): self._fill() self._dispatch(t.node) - self._write(' ' + t.op + ' ') + self._write(' '+t.op+' ') self._dispatch(t.expr) if not self._do_indent: self._write(';') @@ -163,7 +166,7 @@ def _Bitand(self, t): self._write("(") self._dispatch(node) self._write(")") - if i != len(t.nodes) - 1: + if i != len(t.nodes)-1: self._write(" & ") def _Bitor(self, t): @@ -174,7 +177,7 @@ def _Bitor(self, t): self._write("(") self._dispatch(node) self._write(")") - if i != len(t.nodes) - 1: + if i != len(t.nodes)-1: self._write(" | ") def _CallFunc(self, t): @@ -184,23 +187,17 @@ def _CallFunc(self, t): self._write("(") comma = False for e in t.args: - if comma: - self._write(", ") - else: - comma = True + if comma: self._write(", ") + else: comma = True self._dispatch(e) if t.star_args: - if comma: - self._write(", ") - else: - comma = True + if comma: self._write(", ") + else: comma = True self._write("*") self._dispatch(t.star_args) if t.dstar_args: - if comma: - self._write(", ") - else: - comma = True + if comma: self._write(", ") + else: comma = True self._write("**") self._dispatch(t.dstar_args) self._write(")") @@ -224,11 +221,11 @@ def _Decorators(self, t): def _Dict(self, t): self._write("{") - for i, (k, v) in enumerate(t.items): + for i, (k, v) in enumerate(t.items): self._dispatch(k) self._write(": ") self._dispatch(v) - if i < len(t.items) - 1: + if i < len(t.items)-1: self._write(", ") self._write("}") @@ -251,12 +248,12 @@ def _From(self, t): self._fill("from ") self._write(t.modname) self._write(" import ") - for i, (name, asname) in enumerate(t.names): + for i, (name,asname) in enumerate(t.names): if i != 0: self._write(", ") self._write(name) if asname is not None: - self._write(" as " + asname) + self._write(" as "+asname) def _Function(self, t): """ Handle function definitions @@ -264,15 +261,14 @@ def _Function(self, t): if t.decorators is not None: self._fill("@") self._dispatch(t.decorators) - self._fill("def " + t.name + "(") - defaults = [None] * ( - len(t.argnames) - len(t.defaults)) + list(t.defaults) + self._fill("def "+t.name + "(") + defaults = [None] * (len(t.argnames) - len(t.defaults)) + list(t.defaults) for i, arg in enumerate(zip(t.argnames, defaults)): self._write(arg[0]) if arg[1] is not None: self._write('=') self._dispatch(arg[1]) - if i < len(t.argnames) - 1: + if i < len(t.argnames)-1: self._write(', ') self._write(")") if self._single_func: @@ -291,13 +287,13 @@ def _Getattr(self, t): self._write(')') else: self._dispatch(t.expr) - - self._write('.' + t.attrname) - + + self._write('.'+t.attrname) + def _If(self, t): self._fill() - - for i, (compare, code) in enumerate(t.tests): + + for i, (compare,code) in enumerate(t.tests): if i == 0: self._write("if ") else: @@ -316,7 +312,7 @@ def _If(self, t): self._dispatch(t.else_) self._leave() self._write("\n") - + def _IfExp(self, t): self._dispatch(t.then) self._write(" if ") @@ -331,13 +327,13 @@ def _Import(self, t): """ Handle "import xyz.foo". """ self._fill("import ") - - for i, (name, asname) in enumerate(t.names): + + for i, (name,asname) in enumerate(t.names): if i != 0: self._write(", ") self._write(name) if asname is not None: - self._write(" as " + asname) + self._write(" as "+asname) def _Keyword(self, t): """ Keyword value assignment within function calls and definitions. @@ -345,12 +341,12 @@ def _Keyword(self, t): self._write(t.name) self._write("=") self._dispatch(t.expr) - + def _List(self, t): self._write("[") - for i, node in enumerate(t.nodes): + for i,node in enumerate(t.nodes): self._dispatch(node) - if i < len(t.nodes) - 1: + if i < len(t.nodes)-1: self._write(", ") self._write("]") @@ -367,20 +363,20 @@ def _Name(self, t): def _NoneType(self, t): self._write("None") - + def _Not(self, t): self._write('not (') self._dispatch(t.expr) self._write(')') - + def _Or(self, t): self._write(" (") for i, node in enumerate(t.nodes): self._dispatch(node) - if i != len(t.nodes) - 1: + if i != len(t.nodes)-1: self._write(") or (") self._write(")") - + def _Pass(self, t): self._write("pass\n") @@ -392,10 +388,8 @@ def _Printnl(self, t): self._write(", ") comma = False for node in t.nodes: - if comma: - self._write(', ') - else: - comma = True + if comma: self._write(', ') + else: comma = True self._dispatch(node) def _Power(self, t): @@ -405,7 +399,7 @@ def _Return(self, t): self._fill("return ") if t.value: if isinstance(t.value, Tuple): - text = ', '.join([name.name for name in t.value.asList()]) + text = ', '.join([ name.name for name in t.value.asList() ]) self._write(text) else: self._dispatch(t.value) @@ -420,7 +414,7 @@ def _Slice(self, t): self._write(":") if t.upper: self._dispatch(t.upper) - # if t.step: + #if t.step: # self._write(":") # self._dispatch(t.step) self._write("]") @@ -463,7 +457,7 @@ def _TryExcept(self, t): self._enter() self._dispatch(handler[2]) self._leave() - + if t.else_: self._fill("else") self._enter() @@ -488,14 +482,14 @@ def _Tuple(self, t): self._dispatch(last_element) self._write(")") - + def _UnaryAdd(self, t): self._write("+") self._dispatch(t.expr) - + def _UnarySub(self, t): self._write("-") - self._dispatch(t.expr) + self._dispatch(t.expr) def _With(self, t): self._fill('with ') @@ -507,7 +501,7 @@ def _With(self, t): self._dispatch(t.body) self._leave() self._write('\n') - + def _int(self, t): self._write(repr(t)) @@ -516,7 +510,7 @@ def __binary_op(self, t, symbol): has_paren = False left_class = str(t.left.__class__) if (left_class in op_precedence.keys() and - op_precedence[left_class] < op_precedence[str(t.__class__)]): + op_precedence[left_class] < op_precedence[str(t.__class__)]): has_paren = True if has_paren: self._write('(') @@ -529,7 +523,7 @@ def __binary_op(self, t, symbol): has_paren = False right_class = str(t.right.__class__) if (right_class in op_precedence.keys() and - op_precedence[right_class] < op_precedence[str(t.__class__)]): + op_precedence[right_class] < op_precedence[str(t.__class__)]): has_paren = True if has_paren: self._write('(') @@ -544,18 +538,18 @@ def _float(self, t): def _str(self, t): self._write(repr(t)) - + def _tuple(self, t): self._write(str(t)) - # + ######################################################################### # These are the methods from the _ast modules unparse. # # As our needs to handle more advanced code increase, we may want to # modify some of the methods below so that they work for compiler.ast. - # + ######################################################################### -# stmt +# # stmt # def _Expr(self, tree): # self._fill() # self._dispatch(tree.value) @@ -572,18 +566,18 @@ def _tuple(self, t): # if a.asname: # self._write(" as "+a.asname) # -# def _ImportFrom(self, t): -# self._fill("from ") -# self._write(t.module) -# self._write(" import ") -# for i, a in enumerate(t.names): -# if i == 0: -# self._write(", ") -# self._write(a.name) -# if a.asname: -# self._write(" as "+a.asname) -# XXX(jpe) what is level for? -# +## def _ImportFrom(self, t): +## self._fill("from ") +## self._write(t.module) +## self._write(" import ") +## for i, a in enumerate(t.names): +## if i == 0: +## self._write(", ") +## self._write(a.name) +## if a.asname: +## self._write(" as "+a.asname) +## # XXX(jpe) what is level for? +## # # def _Break(self, t): # self._fill("break") @@ -725,10 +719,10 @@ def _tuple(self, t): # self._dispatch(t.orelse) # self._leave # -# expr +# # expr # def _Str(self, tree): # self._write(repr(tree.s)) -# +## # def _Repr(self, t): # self._write("`") # self._dispatch(t.value) @@ -799,31 +793,31 @@ def _tuple(self, t): # self._write(".") # self._write(t.attr) # -# def _Call(self, t): -# self._dispatch(t.func) -# self._write("(") -# comma = False -# for e in t.args: -# if comma: self._write(", ") -# else: comma = True -# self._dispatch(e) -# for e in t.keywords: -# if comma: self._write(", ") -# else: comma = True -# self._dispatch(e) -# if t.starargs: -# if comma: self._write(", ") -# else: comma = True -# self._write("*") -# self._dispatch(t.starargs) -# if t.kwargs: -# if comma: self._write(", ") -# else: comma = True -# self._write("**") -# self._dispatch(t.kwargs) -# self._write(")") -# -# slice +## def _Call(self, t): +## self._dispatch(t.func) +## self._write("(") +## comma = False +## for e in t.args: +## if comma: self._write(", ") +## else: comma = True +## self._dispatch(e) +## for e in t.keywords: +## if comma: self._write(", ") +## else: comma = True +## self._dispatch(e) +## if t.starargs: +## if comma: self._write(", ") +## else: comma = True +## self._write("*") +## self._dispatch(t.starargs) +## if t.kwargs: +## if comma: self._write(", ") +## else: comma = True +## self._write("**") +## self._dispatch(t.kwargs) +## self._write(")") +# +# # slice # def _Index(self, t): # self._dispatch(t.value) # @@ -833,7 +827,7 @@ def _tuple(self, t): # self._write(': ') # self._dispatch(d) # -# others +# # others # def _arguments(self, t): # first = True # nonDef = len(t.args)-len(t.defaults) @@ -856,13 +850,16 @@ def _tuple(self, t): # else: self._write(", ") # self._write("**"+t.kwarg) # -# def _keyword(self, t): -# self._write(t.arg) -# self._write("=") -# self._dispatch(t.value) +## def _keyword(self, t): +## self._write(t.arg) +## self._write("=") +## self._dispatch(t.value) # # def _Lambda(self, t): # self._write("lambda ") # self._dispatch(t.args) # self._write(": ") # self._dispatch(t.body) + + + diff --git a/doc/sphinxext/docscrape.py b/doc/sphinxext/numpydoc/docscrape.py similarity index 79% rename from doc/sphinxext/docscrape.py rename to doc/sphinxext/numpydoc/docscrape.py index a8323c2c74361..4ee0f2e400d0e 100755 --- a/doc/sphinxext/docscrape.py +++ b/doc/sphinxext/numpydoc/docscrape.py @@ -1,21 +1,20 @@ """Extract reference documentation from the NumPy source tree. """ +from __future__ import division, absolute_import, print_function import inspect import textwrap import re import pydoc -from StringIO import StringIO from warnings import warn import collections -class Reader(object): +class Reader(object): """A line-based string reader. """ - def __init__(self, data): """ Parameters @@ -24,10 +23,10 @@ def __init__(self, data): String with lines separated by '\n'. """ - if isinstance(data, list): + if isinstance(data,list): self._str = data else: - self._str = data.split('\n') # store string as list of lines + self._str = data.split('\n') # store string as list of lines self.reset() @@ -35,7 +34,7 @@ def __getitem__(self, n): return self._str[n] def reset(self): - self._l = 0 # current line nr + self._l = 0 # current line nr def read(self): if not self.eof(): @@ -62,12 +61,11 @@ def read_to_condition(self, condition_func): return self[start:self._l] self._l += 1 if self.eof(): - return self[start:self._l + 1] + return self[start:self._l+1] return [] def read_to_next_empty_line(self): self.seek_next_non_empty_line() - def is_empty(line): return not line.strip() return self.read_to_condition(is_empty) @@ -77,7 +75,7 @@ def is_unindented(line): return (line.strip() and (len(line.lstrip()) == len(line))) return self.read_to_condition(is_unindented) - def peek(self, n=0): + def peek(self,n=0): if self._l + n < len(self._str): return self[self._l + n] else: @@ -88,7 +86,6 @@ def is_empty(self): class NumpyDocString(object): - def __init__(self, docstring, config={}): docstring = textwrap.dedent(docstring).split('\n') @@ -110,15 +107,15 @@ def __init__(self, docstring, config={}): 'References': '', 'Examples': '', 'index': {} - } + } self._parse() - def __getitem__(self, key): + def __getitem__(self,key): return self._parsed_data[key] - def __setitem__(self, key, val): - if not self._parsed_data.has_key(key): + def __setitem__(self,key,val): + if key not in self._parsed_data: warn("Unknown section %s" % key) else: self._parsed_data[key] = val @@ -134,27 +131,25 @@ def _is_at_section(self): if l1.startswith('.. index::'): return True - l2 = self._doc.peek(1).strip() # ---------- or ========== - return l2.startswith('-' * len(l1)) or l2.startswith('=' * len(l1)) + l2 = self._doc.peek(1).strip() # ---------- or ========== + return l2.startswith('-'*len(l1)) or l2.startswith('='*len(l1)) - def _strip(self, doc): + def _strip(self,doc): i = 0 j = 0 - for i, line in enumerate(doc): - if line.strip(): - break + for i,line in enumerate(doc): + if line.strip(): break - for j, line in enumerate(doc[::-1]): - if line.strip(): - break + for j,line in enumerate(doc[::-1]): + if line.strip(): break - return doc[i:len(doc) - j] + return doc[i:len(doc)-j] def _read_to_next_section(self): section = self._doc.read_to_next_empty_line() while not self._is_at_section() and not self._doc.eof(): - if not self._doc.peek(-1).strip(): # previous line was empty + if not self._doc.peek(-1).strip(): # previous line was empty section += [''] section += self._doc.read_to_next_empty_line() @@ -166,14 +161,14 @@ def _read_sections(self): data = self._read_to_next_section() name = data[0].strip() - if name.startswith('..'): # index section + if name.startswith('..'): # index section yield name, data[1:] elif len(data) < 2: yield StopIteration else: yield name, self._strip(data[2:]) - def _parse_param_list(self, content): + def _parse_param_list(self,content): r = Reader(content) params = [] while not r.eof(): @@ -186,13 +181,13 @@ def _parse_param_list(self, content): desc = r.read_to_next_unindented_line() desc = dedent_lines(desc) - params.append((arg_name, arg_type, desc)) + params.append((arg_name,arg_type,desc)) return params + _name_rgx = re.compile(r"^\s*(:(?P\w+):`(?P[a-zA-Z0-9_.-]+)`|" r" (?P[a-zA-Z0-9_.-]+))\s*", re.X) - def _parse_see_also(self, content): """ func_name : Descriptive text @@ -225,8 +220,7 @@ def push_item(name, rest): rest = [] for line in content: - if not line.strip(): - continue + if not line.strip(): continue m = self._name_rgx.match(line) if m and line[m.end():].strip().startswith(':'): @@ -273,13 +267,17 @@ def _parse_summary(self): if self._is_at_section(): return - summary = self._doc.read_to_next_empty_line() - summary_str = " ".join([s.strip() for s in summary]).strip() - if re.compile('^([\w., ]+=)?\s*[\w\.]+\(.*\)$').match(summary_str): - self['Signature'] = summary_str - if not self._is_at_section(): - self['Summary'] = self._doc.read_to_next_empty_line() - else: + # If several signatures present, take the last one + while True: + summary = self._doc.read_to_next_empty_line() + summary_str = " ".join([s.strip() for s in summary]).strip() + if re.compile('^([\w., ]+=)?\s*[\w\.]+\(.*\)$').match(summary_str): + self['Signature'] = summary_str + if not self._is_at_section(): + continue + break + + if summary is not None: self['Summary'] = summary if not self._is_at_section(): @@ -289,12 +287,11 @@ def _parse(self): self._doc.reset() self._parse_summary() - for (section, content) in self._read_sections(): + for (section,content) in self._read_sections(): if not section.startswith('..'): - section = ' '.join([s.capitalize() - for s in section.split(' ')]) - if section in ('Parameters', 'Attributes', 'Methods', - 'Returns', 'Raises', 'Warns'): + section = ' '.join([s.capitalize() for s in section.split(' ')]) + if section in ('Parameters', 'Returns', 'Raises', 'Warns', + 'Other Parameters', 'Attributes', 'Methods'): self[section] = self._parse_param_list(content) elif section.startswith('.. index::'): self['index'] = self._parse_index(section, content) @@ -306,17 +303,17 @@ def _parse(self): # string conversion routines def _str_header(self, name, symbol='-'): - return [name, len(name) * symbol] + return [name, len(name)*symbol] def _str_indent(self, doc, indent=4): out = [] for line in doc: - out += [' ' * indent + line] + out += [' '*indent + line] return out def _str_signature(self): if self['Signature']: - return [self['Signature'].replace('*', '\*')] + [''] + return [self['Signature'].replace('*','\*')] + [''] else: return [''] @@ -336,8 +333,11 @@ def _str_param_list(self, name): out = [] if self[name]: out += self._str_header(name) - for param, param_type, desc in self[name]: - out += ['%s : %s' % (param, param_type)] + for param,param_type,desc in self[name]: + if param_type: + out += ['%s : %s' % (param, param_type)] + else: + out += [param] out += self._str_indent(desc) out += [''] return out @@ -351,8 +351,7 @@ def _str_section(self, name): return out def _str_see_also(self, func_role): - if not self['See Also']: - return [] + if not self['See Also']: return [] out = [] out += self._str_header("See Also") last_had_desc = True @@ -379,8 +378,8 @@ def _str_see_also(self, func_role): def _str_index(self): idx = self['index'] out = [] - out += ['.. index:: %s' % idx.get('default', '')] - for section, references in idx.iteritems(): + out += ['.. index:: %s' % idx.get('default','')] + for section, references in idx.items(): if section == 'default': continue out += [' :%s: %s' % (section, ', '.join(references))] @@ -391,11 +390,12 @@ def __str__(self, func_role=''): out += self._str_signature() out += self._str_summary() out += self._str_extended_summary() - for param_list in ('Parameters', 'Returns', 'Raises'): + for param_list in ('Parameters', 'Returns', 'Other Parameters', + 'Raises', 'Warns'): out += self._str_param_list(param_list) out += self._str_section('Warnings') out += self._str_see_also(func_role) - for s in ('Notes', 'References', 'Examples'): + for s in ('Notes','References','Examples'): out += self._str_section(s) for param_list in ('Attributes', 'Methods'): out += self._str_param_list(param_list) @@ -403,28 +403,25 @@ def __str__(self, func_role=''): return '\n'.join(out) -def indent(str, indent=4): - indent_str = ' ' * indent +def indent(str,indent=4): + indent_str = ' '*indent if str is None: return indent_str lines = str.split('\n') return '\n'.join(indent_str + l for l in lines) - def dedent_lines(lines): """Deindent a list of lines maximally""" return textwrap.dedent("\n".join(lines)).split("\n") - def header(text, style='-'): - return text + '\n' + style * len(text) + '\n' + return text + '\n' + style*len(text) + '\n' class FunctionDoc(NumpyDocString): - def __init__(self, func, role='func', doc=None, config={}): self._f = func - self._role = role # e.g. "func" or "meth" + self._role = role # e.g. "func" or "meth" if doc is None: if func is None: @@ -438,9 +435,9 @@ def __init__(self, func, role='func', doc=None, config={}): # try to read signature argspec = inspect.getargspec(func) argspec = inspect.formatargspec(*argspec) - argspec = argspec.replace('*', '\*') + argspec = argspec.replace('*','\*') signature = '%s%s' % (func_name, argspec) - except TypeError, e: + except TypeError as e: signature = '%s()' % func_name self['Signature'] = signature @@ -462,9 +459,9 @@ def __str__(self): 'meth': 'method'} if self._role: - if not roles.has_key(self._role): + if self._role not in roles: print("Warning: invalid role %s" % self._role) - out += '.. %s:: %s\n \n\n' % (roles.get(self._role, ''), + out += '.. %s:: %s\n \n\n' % (roles.get(self._role,''), func_name) out += super(FunctionDoc, self).__str__(func_role=self._role) @@ -493,12 +490,19 @@ def __init__(self, cls, doc=None, modulename='', func_doc=FunctionDoc, NumpyDocString.__init__(self, doc) if config.get('show_class_members', True): - if not self['Methods']: - self['Methods'] = [(name, '', '') - for name in sorted(self.methods)] - if not self['Attributes']: - self['Attributes'] = [(name, '', '') - for name in sorted(self.properties)] + def splitlines_x(s): + if not s: + return [] + else: + return s.splitlines() + + for field, items in [('Methods', self.methods), + ('Attributes', self.properties)]: + if not self[field]: + self[field] = [ + (name, '', + splitlines_x(pydoc.getdoc(getattr(self._cls, name)))) + for name in sorted(items)] @property def methods(self): @@ -516,4 +520,4 @@ def properties(self): return [name for name,func in inspect.getmembers(self._cls) if not name.startswith('_') and (func is None or isinstance(func, property) or - inspect.isgetsetdescriptor(func))] \ No newline at end of file + inspect.isgetsetdescriptor(func))] diff --git a/doc/sphinxext/docscrape_sphinx.py b/doc/sphinxext/numpydoc/docscrape_sphinx.py similarity index 66% rename from doc/sphinxext/docscrape_sphinx.py rename to doc/sphinxext/numpydoc/docscrape_sphinx.py index cf3873c3a5f0c..ba93b2eab779d 100755 --- a/doc/sphinxext/docscrape_sphinx.py +++ b/doc/sphinxext/numpydoc/docscrape_sphinx.py @@ -1,17 +1,25 @@ -import re -import inspect -import textwrap -import pydoc +from __future__ import division, absolute_import, print_function + +import sys, re, inspect, textwrap, pydoc import sphinx -from docscrape import NumpyDocString, FunctionDoc, ClassDoc +import collections +from .docscrape import NumpyDocString, FunctionDoc, ClassDoc +if sys.version_info[0] >= 3: + sixu = lambda s: s +else: + sixu = lambda s: unicode(s, 'unicode_escape') -class SphinxDocString(NumpyDocString): +class SphinxDocString(NumpyDocString): def __init__(self, docstring, config={}): - self.use_plots = config.get('use_plots', False) + # Subclasses seemingly do not call this. NumpyDocString.__init__(self, docstring, config=config) + def load_config(self, config): + self.use_plots = config.get('use_plots', False) + self.class_members_toctree = config.get('class_members_toctree', True) + # string conversion routines def _str_header(self, name, symbol='`'): return ['.. rubric:: ' + name, ''] @@ -22,7 +30,7 @@ def _str_field_list(self, name): def _str_indent(self, doc, indent=4): out = [] for line in doc: - out += [' ' * indent + line] + out += [' '*indent + line] return out def _str_signature(self): @@ -38,16 +46,37 @@ def _str_summary(self): def _str_extended_summary(self): return self['Extended Summary'] + [''] + def _str_returns(self): + out = [] + if self['Returns']: + out += self._str_field_list('Returns') + out += [''] + for param, param_type, desc in self['Returns']: + if param_type: + out += self._str_indent(['**%s** : %s' % (param.strip(), + param_type)]) + else: + out += self._str_indent([param.strip()]) + if desc: + out += [''] + out += self._str_indent(desc, 8) + out += [''] + return out + def _str_param_list(self, name): out = [] if self[name]: out += self._str_field_list(name) out += [''] for param, param_type, desc in self[name]: - out += self._str_indent(['**%s** : %s' % (param.strip(), - param_type)]) - out += [''] - out += self._str_indent(desc, 8) + if param_type: + out += self._str_indent(['**%s** : %s' % (param.strip(), + param_type)]) + else: + out += self._str_indent(['**%s**' % param.strip()]) + if desc: + out += [''] + out += self._str_indent(desc, 8) out += [''] return out @@ -77,25 +106,36 @@ def _str_member_list(self, name): others = [] for param, param_type, desc in self[name]: param = param.strip() - if not self._obj or hasattr(self._obj, param): + + # Check if the referenced member can have a docstring or not + param_obj = getattr(self._obj, param, None) + if not (callable(param_obj) + or isinstance(param_obj, property) + or inspect.isgetsetdescriptor(param_obj)): + param_obj = None + + if param_obj and (pydoc.getdoc(param_obj) or not desc): + # Referenced object has a docstring autosum += [" %s%s" % (prefix, param)] else: others.append((param, param_type, desc)) if autosum: - out += ['.. autosummary::', ' :toctree:', ''] - out += autosum + out += ['.. autosummary::'] + if self.class_members_toctree: + out += [' :toctree:'] + out += [''] + autosum if others: - maxlen_0 = max([len(x[0]) for x in others]) - maxlen_1 = max([len(x[1]) for x in others]) - hdr = "=" * maxlen_0 + " " + "=" * maxlen_1 + " " + "=" * 10 - fmt = '%%%ds %%%ds ' % (maxlen_0, maxlen_1) - n_indent = maxlen_0 + maxlen_1 + 4 - out += [hdr] + maxlen_0 = max(3, max([len(x[0]) for x in others])) + hdr = sixu("=")*maxlen_0 + sixu(" ") + sixu("=")*10 + fmt = sixu('%%%ds %%s ') % (maxlen_0,) + out += ['', hdr] for param, param_type, desc in others: - out += [fmt % (param.strip(), param_type)] - out += self._str_indent(desc, n_indent) + desc = sixu(" ").join(x.strip() for x in desc).strip() + if param_type: + desc = "(%s) %s" % (param_type, desc) + out += [fmt % (param.strip(), desc)] out += [hdr] out += [''] return out @@ -131,8 +171,8 @@ def _str_index(self): if len(idx) == 0: return out - out += ['.. index:: %s' % idx.get('default', '')] - for section, references in idx.iteritems(): + out += ['.. index:: %s' % idx.get('default','')] + for section, references in idx.items(): if section == 'default': continue elif section == 'refguide': @@ -152,9 +192,9 @@ def _str_references(self): # Latex collects all references to a separate bibliography, # so we need to insert links to it if sphinx.__version__ >= "0.6": - out += ['.. only:: latex', ''] + out += ['.. only:: latex',''] else: - out += ['.. latexonly::', ''] + out += ['.. latexonly::',''] items = [] for line in self['References']: m = re.match(r'.. \[([a-z0-9._-]+)\]', line, re.I) @@ -183,7 +223,9 @@ def __str__(self, indent=0, func_role="obj"): out += self._str_index() + [''] out += self._str_summary() out += self._str_extended_summary() - for param_list in ('Parameters', 'Returns', 'Raises'): + out += self._str_param_list('Parameters') + out += self._str_returns() + for param_list in ('Other Parameters', 'Raises', 'Warns'): out += self._str_param_list(param_list) out += self._str_warnings() out += self._str_see_also(func_role) @@ -192,38 +234,32 @@ def __str__(self, indent=0, func_role="obj"): out += self._str_examples() for param_list in ('Attributes', 'Methods'): out += self._str_member_list(param_list) - out = self._str_indent(out, indent) + out = self._str_indent(out,indent) return '\n'.join(out) - class SphinxFunctionDoc(SphinxDocString, FunctionDoc): - def __init__(self, obj, doc=None, config={}): - self.use_plots = config.get('use_plots', False) + self.load_config(config) FunctionDoc.__init__(self, obj, doc=doc, config=config) - class SphinxClassDoc(SphinxDocString, ClassDoc): - def __init__(self, obj, doc=None, func_doc=None, config={}): - self.use_plots = config.get('use_plots', False) + self.load_config(config) ClassDoc.__init__(self, obj, doc=doc, func_doc=None, config=config) - class SphinxObjDoc(SphinxDocString): - def __init__(self, obj, doc=None, config={}): self._f = obj + self.load_config(config) SphinxDocString.__init__(self, doc, config=config) - def get_doc_object(obj, what=None, doc=None, config={}): if what is None: if inspect.isclass(obj): what = 'class' elif inspect.ismodule(obj): what = 'module' - elif callable(obj): + elif isinstance(obj, collections.Callable): what = 'function' else: what = 'object' diff --git a/doc/sphinxext/numpydoc/linkcode.py b/doc/sphinxext/numpydoc/linkcode.py new file mode 100644 index 0000000000000..1ad3ab82cb49c --- /dev/null +++ b/doc/sphinxext/numpydoc/linkcode.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +""" + linkcode + ~~~~~~~~ + + Add external links to module code in Python object descriptions. + + :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. + +""" +from __future__ import division, absolute_import, print_function + +import warnings +import collections + +warnings.warn("This extension has been accepted to Sphinx upstream. " + "Use the version from there (Sphinx >= 1.2) " + "https://bitbucket.org/birkenfeld/sphinx/pull-request/47/sphinxextlinkcode", + FutureWarning, stacklevel=1) + + +from docutils import nodes + +from sphinx import addnodes +from sphinx.locale import _ +from sphinx.errors import SphinxError + +class LinkcodeError(SphinxError): + category = "linkcode error" + +def doctree_read(app, doctree): + env = app.builder.env + + resolve_target = getattr(env.config, 'linkcode_resolve', None) + if not isinstance(env.config.linkcode_resolve, collections.Callable): + raise LinkcodeError( + "Function `linkcode_resolve` is not given in conf.py") + + domain_keys = dict( + py=['module', 'fullname'], + c=['names'], + cpp=['names'], + js=['object', 'fullname'], + ) + + for objnode in doctree.traverse(addnodes.desc): + domain = objnode.get('domain') + uris = set() + for signode in objnode: + if not isinstance(signode, addnodes.desc_signature): + continue + + # Convert signode to a specified format + info = {} + for key in domain_keys.get(domain, []): + value = signode.get(key) + if not value: + value = '' + info[key] = value + if not info: + continue + + # Call user code to resolve the link + uri = resolve_target(domain, info) + if not uri: + # no source + continue + + if uri in uris or not uri: + # only one link per name, please + continue + uris.add(uri) + + onlynode = addnodes.only(expr='html') + onlynode += nodes.reference('', '', internal=False, refuri=uri) + onlynode[0] += nodes.inline('', _('[source]'), + classes=['viewcode-link']) + signode += onlynode + +def setup(app): + app.connect('doctree-read', doctree_read) + app.add_config_value('linkcode_resolve', None, '') diff --git a/doc/sphinxext/numpydoc.py b/doc/sphinxext/numpydoc/numpydoc.py similarity index 65% rename from doc/sphinxext/numpydoc.py rename to doc/sphinxext/numpydoc/numpydoc.py index 1cba77cd7412e..2bc2d1e91ed3f 100755 --- a/doc/sphinxext/numpydoc.py +++ b/doc/sphinxext/numpydoc/numpydoc.py @@ -12,53 +12,64 @@ - Renumber references. - Extract the signature from the docstring, if it can't be determined otherwise. -.. [1] http://projects.scipy.org/numpy/wiki/CodingStyleGuidelines#docstring-standard +.. [1] https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt """ +from __future__ import division, absolute_import, print_function +import os, sys, re, pydoc import sphinx +import inspect +import collections if sphinx.__version__ < '1.0.1': raise RuntimeError("Sphinx 1.0.1 or newer is required") -import os -import re -import pydoc -from docscrape_sphinx import get_doc_object, SphinxDocString +from .docscrape_sphinx import get_doc_object, SphinxDocString from sphinx.util.compat import Directive -import inspect + +if sys.version_info[0] >= 3: + sixu = lambda s: s +else: + sixu = lambda s: unicode(s, 'unicode_escape') def mangle_docstrings(app, what, name, obj, options, lines, reference_offset=[0]): cfg = dict(use_plots=app.config.numpydoc_use_plots, - show_class_members=app.config.numpydoc_show_class_members) + show_class_members=app.config.numpydoc_show_class_members, + class_members_toctree=app.config.numpydoc_class_members_toctree, + ) if what == 'module': # Strip top title - title_re = re.compile(ur'^\s*[#*=]{4,}\n[a-z0-9 -]+\n[#*=]{4,}\s*', - re.I | re.S) - lines[:] = title_re.sub(u'', u"\n".join(lines)).split(u"\n") + title_re = re.compile(sixu('^\\s*[#*=]{4,}\\n[a-z0-9 -]+\\n[#*=]{4,}\\s*'), + re.I|re.S) + lines[:] = title_re.sub(sixu(''), sixu("\n").join(lines)).split(sixu("\n")) else: - doc = get_doc_object(obj, what, u"\n".join(lines), config=cfg) - lines[:] = unicode(doc).split(u"\n") + doc = get_doc_object(obj, what, sixu("\n").join(lines), config=cfg) + if sys.version_info[0] >= 3: + doc = str(doc) + else: + doc = unicode(doc) + lines[:] = doc.split(sixu("\n")) if app.config.numpydoc_edit_link and hasattr(obj, '__name__') and \ - obj.__name__: + obj.__name__: if hasattr(obj, '__module__'): - v = dict(full_name=u"%s.%s" % (obj.__module__, obj.__name__)) + v = dict(full_name=sixu("%s.%s") % (obj.__module__, obj.__name__)) else: v = dict(full_name=obj.__name__) - lines += [u'', u'.. htmlonly::', ''] - lines += [u' %s' % x for x in + lines += [sixu(''), sixu('.. htmlonly::'), sixu('')] + lines += [sixu(' %s') % x for x in (app.config.numpydoc_edit_link % v).split("\n")] # replace reference numbers so that there are no duplicates references = [] for line in lines: line = line.strip() - m = re.match(ur'^.. \[([a-z0-9_.-])\]', line, re.I) + m = re.match(sixu('^.. \\[([a-z0-9_.-])\\]'), line, re.I) if m: references.append(m.group(1)) @@ -67,37 +78,36 @@ def mangle_docstrings(app, what, name, obj, options, lines, if references: for i, line in enumerate(lines): for r in references: - if re.match(ur'^\d+$', r): - new_r = u"R%d" % (reference_offset[0] + int(r)) + if re.match(sixu('^\\d+$'), r): + new_r = sixu("R%d") % (reference_offset[0] + int(r)) else: - new_r = u"%s%d" % (r, reference_offset[0]) - lines[i] = lines[i].replace(u'[%s]_' % r, - u'[%s]_' % new_r) - lines[i] = lines[i].replace(u'.. [%s]' % r, - u'.. [%s]' % new_r) + new_r = sixu("%s%d") % (r, reference_offset[0]) + lines[i] = lines[i].replace(sixu('[%s]_') % r, + sixu('[%s]_') % new_r) + lines[i] = lines[i].replace(sixu('.. [%s]') % r, + sixu('.. [%s]') % new_r) reference_offset[0] += len(references) - def mangle_signature(app, what, name, obj, options, sig, retann): # Do not try to inspect classes that don't define `__init__` if (inspect.isclass(obj) and (not hasattr(obj, '__init__') or - 'initializes x; see ' in pydoc.getdoc(obj.__init__))): + 'initializes x; see ' in pydoc.getdoc(obj.__init__))): return '', '' - if not (callable(obj) or hasattr(obj, '__argspec_is_invalid_')): - return - if not hasattr(obj, '__doc__'): - return + if not (isinstance(obj, collections.Callable) or hasattr(obj, '__argspec_is_invalid_')): return + if not hasattr(obj, '__doc__'): return doc = SphinxDocString(pydoc.getdoc(obj)) if doc['Signature']: - sig = re.sub(u"^[^(]*", u"", doc['Signature']) - return sig, u'' - + sig = re.sub(sixu("^[^(]*"), sixu(""), doc['Signature']) + return sig, sixu('') def setup(app, get_doc_object_=get_doc_object): + if not hasattr(app, 'add_config_value'): + return # probably called by nose, better bail out + global get_doc_object get_doc_object = get_doc_object_ @@ -106,6 +116,7 @@ def setup(app, get_doc_object_=get_doc_object): app.add_config_value('numpydoc_edit_link', None, False) app.add_config_value('numpydoc_use_plots', None, False) app.add_config_value('numpydoc_show_class_members', True, True) + app.add_config_value('numpydoc_class_members_toctree', True, True) # Extra mangling domains app.add_domain(NumpyPythonDomain) @@ -119,7 +130,6 @@ def setup(app, get_doc_object_=get_doc_object): from sphinx.domains.c import CDomain from sphinx.domains.python import PythonDomain - class ManglingDomainBase(object): directive_mangling_map = {} @@ -128,11 +138,10 @@ def __init__(self, *a, **kw): self.wrap_mangling_directives() def wrap_mangling_directives(self): - for name, objtype in self.directive_mangling_map.items(): + for name, objtype in list(self.directive_mangling_map.items()): self.directives[name] = wrap_mangling_directive( self.directives[name], objtype) - class NumpyPythonDomain(ManglingDomainBase, PythonDomain): name = 'np' directive_mangling_map = { @@ -144,7 +153,7 @@ class NumpyPythonDomain(ManglingDomainBase, PythonDomain): 'staticmethod': 'function', 'attribute': 'attribute', } - + indices = [] class NumpyCDomain(ManglingDomainBase, CDomain): name = 'np-c' @@ -156,10 +165,8 @@ class NumpyCDomain(ManglingDomainBase, CDomain): 'var': 'object', } - def wrap_mangling_directive(base_directive, objtype): class directive(base_directive): - def run(self): env = self.state.document.settings.env diff --git a/doc/sphinxext/phantom_import.py b/doc/sphinxext/numpydoc/phantom_import.py similarity index 88% rename from doc/sphinxext/phantom_import.py rename to doc/sphinxext/numpydoc/phantom_import.py index b69f09ea612a0..9a60b4a35b18f 100755 --- a/doc/sphinxext/phantom_import.py +++ b/doc/sphinxext/numpydoc/phantom_import.py @@ -14,20 +14,14 @@ .. [1] http://code.google.com/p/pydocweb """ -import imp -import sys -import compiler -import types -import os -import inspect -import re +from __future__ import division, absolute_import, print_function +import imp, sys, compiler, types, os, inspect, re def setup(app): app.connect('builder-inited', initialize) app.add_config_value('phantom_import_file', None, True) - def initialize(app): fn = app.config.phantom_import_file if (fn and os.path.isfile(fn)): @@ -37,8 +31,6 @@ def initialize(app): #------------------------------------------------------------------------------ # Creating 'phantom' modules from an XML description #------------------------------------------------------------------------------ - - def import_phantom_module(xml_file): """ Insert a fake Python module to sys.modules, based on a XML file. @@ -56,7 +48,7 @@ def import_phantom_module(xml_file): ---------- xml_file : str Name of an XML file to read - + """ import lxml.etree as etree @@ -69,7 +61,7 @@ def import_phantom_module(xml_file): # - Base classes come before classes inherited from them # - Modules come before their contents all_nodes = dict([(n.attrib['id'], n) for n in root]) - + def _get_bases(node, recurse=False): bases = [x.attrib['ref'] for x in node.findall('base')] if recurse: @@ -77,31 +69,26 @@ def _get_bases(node, recurse=False): while True: try: b = bases[j] - except IndexError: - break + except IndexError: break if b in all_nodes: bases.extend(_get_bases(all_nodes[b])) j += 1 return bases type_index = ['module', 'class', 'callable', 'object'] - + def base_cmp(a, b): x = cmp(type_index.index(a.tag), type_index.index(b.tag)) - if x != 0: - return x + if x != 0: return x if a.tag == 'class' and b.tag == 'class': a_bases = _get_bases(a, recurse=True) b_bases = _get_bases(b, recurse=True) x = cmp(len(a_bases), len(b_bases)) - if x != 0: - return x - if a.attrib['id'] in b_bases: - return -1 - if b.attrib['id'] in a_bases: - return 1 - + if x != 0: return x + if a.attrib['id'] in b_bases: return -1 + if b.attrib['id'] in a_bases: return 1 + return cmp(a.attrib['id'].count('.'), b.attrib['id'].count('.')) nodes = root.getchildren() @@ -111,17 +98,14 @@ def base_cmp(a, b): for node in nodes: name = node.attrib['id'] doc = (node.text or '').decode('string-escape') + "\n" - if doc == "\n": - doc = "" + if doc == "\n": doc = "" # create parent, if missing parent = name while True: parent = '.'.join(parent.split('.')[:-1]) - if not parent: - break - if parent in object_cache: - break + if not parent: break + if parent in object_cache: break obj = imp.new_module(parent) object_cache[parent] = obj sys.modules[parent] = obj @@ -147,14 +131,16 @@ def base_cmp(a, b): doc = "%s%s\n\n%s" % (funcname, argspec, doc) obj = lambda: 0 obj.__argspec_is_invalid_ = True - obj.func_name = funcname + if sys.version_info[0] >= 3: + obj.__name__ = funcname + else: + obj.func_name = funcname obj.__name__ = name obj.__doc__ = doc if inspect.isclass(object_cache[parent]): obj.__objclass__ = object_cache[parent] else: - class Dummy(object): - pass + class Dummy(object): pass obj = Dummy() obj.__name__ = name obj.__doc__ = doc @@ -170,8 +156,7 @@ class Dummy(object): # Populate items for node in root: obj = object_cache.get(node.attrib['id']) - if obj is None: - continue + if obj is None: continue for ref in node.findall('ref'): if node.tag == 'class': if ref.attrib['ref'].startswith(node.attrib['id'] + '.'): diff --git a/doc/sphinxext/plot_directive.py b/doc/sphinxext/numpydoc/plot_directive.py similarity index 93% rename from doc/sphinxext/plot_directive.py rename to doc/sphinxext/numpydoc/plot_directive.py index 0a85c6c7f108a..2014f857076c1 100755 --- a/doc/sphinxext/plot_directive.py +++ b/doc/sphinxext/numpydoc/plot_directive.py @@ -74,19 +74,16 @@ to make them appear side-by-side, or in floats. """ +from __future__ import division, absolute_import, print_function -import sys -import os -import glob -import shutil -import imp -import warnings -import cStringIO -import re -import textwrap -import traceback +import sys, os, glob, shutil, imp, warnings, re, textwrap, traceback import sphinx +if sys.version_info[0] >= 3: + from io import StringIO +else: + from io import StringIO + import warnings warnings.warn("A plot_directive module is also available under " "matplotlib.sphinxext; expect this numpydoc.plot_directive " @@ -119,13 +116,11 @@ def setup(app): from docutils.parsers.rst import directives from docutils import nodes - def plot_directive(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine): return run(arguments, content, options, state_machine, state, lineno) plot_directive.__doc__ = __doc__ - def _option_boolean(arg): if not arg or not arg.strip(): # no argument given, assume used as a flag @@ -137,11 +132,9 @@ def _option_boolean(arg): else: raise ValueError('"%s" unknown boolean' % arg) - def _option_format(arg): return directives.choice(arg, ('python', 'lisp')) - def _option_align(arg): return directives.choice(arg, ("top", "middle", "bottom", "left", "center", "right")) @@ -165,12 +158,10 @@ def _option_align(arg): try: # Sphinx depends on either Jinja or Jinja2 import jinja2 - def format_template(template, **kw): return jinja2.Template(template).render(**kw) except ImportError: import jinja - def format_template(template, **kw): return jinja.from_string(template, **kw) @@ -219,9 +210,7 @@ def format_template(template, **kw): """ - class ImageFile(object): - def __init__(self, basename, dirname): self.basename = basename self.dirname = dirname @@ -233,7 +222,6 @@ def filename(self, format): def filenames(self): return [self.filename(fmt) for fmt in self.formats] - def run(arguments, content, options, state_machine, state, lineno): if arguments and content: raise RuntimeError("plot:: directive can't have both args and content") @@ -275,7 +263,7 @@ def run(arguments, content, options, state_machine, state, lineno): # is it in doctest format? is_doctest = contains_doctest(code) - if options.has_key('format'): + if 'format' in options: if options['format'] == 'python': is_doctest = False else: @@ -309,7 +297,7 @@ def run(arguments, content, options, state_machine, state, lineno): results = makefig(code, source_file_name, build_dir, output_base, config) errors = [] - except PlotError, err: + except PlotError as err: reporter = state.memo.reporter sm = reporter.system_message( 2, "Exception occurred in plotting %s: %s" % (output_base, err), @@ -332,15 +320,11 @@ def run(arguments, content, options, state_machine, state, lineno): else: source_code = "" - opts = [':%s: %s' % (key, val) for key, val in options.items() + opts = [':%s: %s' % (key, val) for key, val in list(options.items()) if key in ('alt', 'height', 'width', 'scale', 'align', 'class')] - if sphinx.__version__ >= "0.6": - only_html = ".. only:: html" - only_latex = ".. only:: latex" - else: - only_html = ".. htmlonly::" - only_latex = ".. latexonly::" + only_html = ".. only:: html" + only_latex = ".. only:: latex" if j == 0: src_link = source_link @@ -398,7 +382,6 @@ def run(arguments, content, options, state_machine, state, lineno): import exceptions - def contains_doctest(text): try: # check if it's valid Python as-is @@ -410,7 +393,6 @@ def contains_doctest(text): m = r.search(text) return bool(m) - def unescape_doctest(text): """ Extract code from a piece of text, which contains either Python code @@ -431,7 +413,6 @@ def unescape_doctest(text): code += "\n" return code - def split_code_at_show(text): """ Split code at plt.show() @@ -444,7 +425,7 @@ def split_code_at_show(text): part = [] for line in text.split("\n"): if (not is_doctest and line.strip() == 'plt.show()') or \ - (is_doctest and line.strip() == '>>> plt.show()'): + (is_doctest and line.strip() == '>>> plt.show()'): part.append(line) parts.append("\n".join(part)) part = [] @@ -454,11 +435,9 @@ def split_code_at_show(text): parts.append("\n".join(part)) return parts - class PlotError(RuntimeError): pass - def run_code(code, code_path, ns=None): # Change the working directory to the directory of the example, so # it can get at its data files, if any. @@ -471,21 +450,21 @@ def run_code(code, code_path, ns=None): # Redirect stdout stdout = sys.stdout - sys.stdout = cStringIO.StringIO() + sys.stdout = StringIO() # Reset sys.argv old_sys_argv = sys.argv sys.argv = [code_path] - + try: try: code = unescape_doctest(code) if ns is None: ns = {} if not ns: - exec setup.config.plot_pre_code in ns - exec code in ns - except (Exception, SystemExit), err: + exec(setup.config.plot_pre_code, ns) + exec(code, ns) + except (Exception, SystemExit) as err: raise PlotError(traceback.format_exc()) finally: os.chdir(pwd) @@ -521,7 +500,7 @@ def makefig(code, code_path, output_dir, output_base, config): for fmt in config.plot_formats: if isinstance(fmt, str): formats.append((fmt, default_dpi.get(fmt, 80))) - elif type(fmt) in (tuple, list) and len(fmt) == 2: + elif type(fmt) in (tuple, list) and len(fmt)==2: formats.append((str(fmt[0]), int(fmt[1]))) else: raise PlotError('invalid image format "%r" in plot_formats' % fmt) @@ -547,7 +526,7 @@ def makefig(code, code_path, output_dir, output_base, config): all_exists = True for i, code_piece in enumerate(code_pieces): images = [] - for j in xrange(1000): + for j in range(1000): img = ImageFile('%s_%02d_%02d' % (output_base, i, j), output_dir) for format, dpi in formats: if out_of_date(code_path, img.filename(format)): @@ -591,9 +570,8 @@ def makefig(code, code_path, output_dir, output_base, config): images.append(img) for format, dpi in formats: try: - figman.canvas.figure.savefig(img.filename(format), dpi=dpi, - bbox_inches='tight') - except exceptions.BaseException, err: + figman.canvas.figure.savefig(img.filename(format), dpi=dpi) + except exceptions.BaseException as err: raise PlotError(traceback.format_exc()) img.formats.append(format) @@ -615,7 +593,7 @@ def makefig(code, code_path, output_dir, output_base, config): def relpath(path, start=os.path.curdir): """Return a relative version of a path""" from os.path import sep, curdir, join, abspath, commonprefix, \ - pardir + pardir if not path: raise ValueError("no path specified") @@ -626,7 +604,7 @@ def relpath(path, start=os.path.curdir): # Work out how much of the filepath is shared by start and path. i = len(commonprefix([start_list, path_list])) - rel_list = [pardir] * (len(start_list) - i) + path_list[i:] + rel_list = [pardir] * (len(start_list)-i) + path_list[i:] if not rel_list: return curdir return join(*rel_list) @@ -634,7 +612,7 @@ def relpath(path, start=os.path.curdir): def relpath(path, start=os.path.curdir): """Return a relative version of a path""" from os.path import sep, curdir, join, abspath, commonprefix, \ - pardir, splitunc + pardir, splitunc if not path: raise ValueError("no path specified") @@ -645,10 +623,10 @@ def relpath(path, start=os.path.curdir): unc_start, rest = splitunc(start) if bool(unc_path) ^ bool(unc_start): raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" - % (path, start)) + % (path, start)) else: raise ValueError("path is on drive %s, start on drive %s" - % (path_list[0], start_list[0])) + % (path_list[0], start_list[0])) # Work out how much of the filepath is shared by start and path. for i in range(min(len(start_list), len(path_list))): if start_list[i].lower() != path_list[i].lower(): @@ -656,7 +634,7 @@ def relpath(path, start=os.path.curdir): else: i += 1 - rel_list = [pardir] * (len(start_list) - i) + path_list[i:] + rel_list = [pardir] * (len(start_list)-i) + path_list[i:] if not rel_list: return curdir return join(*rel_list) diff --git a/doc/sphinxext/tests/test_docscrape.py b/doc/sphinxext/numpydoc/tests/test_docscrape.py similarity index 70% rename from doc/sphinxext/tests/test_docscrape.py rename to doc/sphinxext/numpydoc/tests/test_docscrape.py index a66e4222b380d..b682504e1618f 100755 --- a/doc/sphinxext/tests/test_docscrape.py +++ b/doc/sphinxext/numpydoc/tests/test_docscrape.py @@ -1,15 +1,20 @@ # -*- encoding:utf-8 -*- +from __future__ import division, absolute_import, print_function -import sys -import os -sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +import sys, textwrap -from docscrape import NumpyDocString, FunctionDoc, ClassDoc -from docscrape_sphinx import SphinxDocString, SphinxClassDoc +from numpydoc.docscrape import NumpyDocString, FunctionDoc, ClassDoc +from numpydoc.docscrape_sphinx import SphinxDocString, SphinxClassDoc from nose.tools import * +if sys.version_info[0] >= 3: + sixu = lambda s: s +else: + sixu = lambda s: unicode(s, 'unicode_escape') + + doc_txt = '''\ - numpy.multivariate_normal(mean, cov, shape=None) + numpy.multivariate_normal(mean, cov, shape=None, spam=None) Draw values from a multivariate normal distribution with specified mean and covariance. @@ -26,7 +31,7 @@ (1+2+3)/3 - cov : (N,N) ndarray + cov : (N, N) ndarray Covariance matrix of the distribution. shape : tuple of ints Given a shape of, for example, (m,n,k), m*n*k samples are @@ -42,6 +47,24 @@ In other words, each entry ``out[i,j,...,:]`` is an N-dimensional value drawn from the distribution. + list of str + This is not a real return value. It exists to test + anonymous return values. + + Other Parameters + ---------------- + spam : parrot + A parrot off its mortal coil. + + Raises + ------ + RuntimeError + Some error + + Warns + ----- + RuntimeWarning + Some warning Warnings -------- @@ -49,7 +72,6 @@ Notes ----- - Instead of specifying the full covariance matrix, popular approximations include: @@ -85,13 +107,13 @@ >>> mean = (1,2) >>> cov = [[1,0],[1,0]] >>> x = multivariate_normal(mean,cov,(3,3)) - >>> print(x.shape) + >>> print x.shape (3, 3, 2) The following is probably true, given that 0.6 is roughly twice the standard deviation: - >>> print(list( (x[0,0,:] - mean) < 0.6 )) + >>> print list( (x[0,0,:] - mean) < 0.6 ) [True, True] .. index:: random @@ -103,74 +125,76 @@ def test_signature(): assert doc['Signature'].startswith('numpy.multivariate_normal(') - assert doc['Signature'].endswith('shape=None)') - + assert doc['Signature'].endswith('spam=None)') def test_summary(): assert doc['Summary'][0].startswith('Draw values') assert doc['Summary'][-1].endswith('covariance.') - def test_extended_summary(): assert doc['Extended Summary'][0].startswith('The multivariate normal') - def test_parameters(): assert_equal(len(doc['Parameters']), 3) - assert_equal( - [n for n, _, _ in doc['Parameters']], ['mean', 'cov', 'shape']) + assert_equal([n for n,_,_ in doc['Parameters']], ['mean','cov','shape']) arg, arg_type, desc = doc['Parameters'][1] - assert_equal(arg_type, '(N,N) ndarray') + assert_equal(arg_type, '(N, N) ndarray') assert desc[0].startswith('Covariance matrix') assert doc['Parameters'][0][-1][-2] == ' (1+2+3)/3' +def test_other_parameters(): + assert_equal(len(doc['Other Parameters']), 1) + assert_equal([n for n,_,_ in doc['Other Parameters']], ['spam']) + arg, arg_type, desc = doc['Other Parameters'][0] + assert_equal(arg_type, 'parrot') + assert desc[0].startswith('A parrot off its mortal coil') def test_returns(): - assert_equal(len(doc['Returns']), 1) + assert_equal(len(doc['Returns']), 2) arg, arg_type, desc = doc['Returns'][0] assert_equal(arg, 'out') assert_equal(arg_type, 'ndarray') assert desc[0].startswith('The drawn samples') assert desc[-1].endswith('distribution.') + arg, arg_type, desc = doc['Returns'][1] + assert_equal(arg, 'list of str') + assert_equal(arg_type, '') + assert desc[0].startswith('This is not a real') + assert desc[-1].endswith('anonymous return values.') def test_notes(): assert doc['Notes'][0].startswith('Instead') assert doc['Notes'][-1].endswith('definite.') assert_equal(len(doc['Notes']), 17) - def test_references(): assert doc['References'][0].startswith('..') assert doc['References'][-1].endswith('2001.') - def test_examples(): assert doc['Examples'][0].startswith('>>>') assert doc['Examples'][-1].endswith('True]') - def test_index(): assert_equal(doc['index']['default'], 'random') - print(doc['index']) assert_equal(len(doc['index']), 2) assert_equal(len(doc['index']['refguide']), 2) - -def non_blank_line_by_line_compare(a, b): - a = [l for l in a.split('\n') if l.strip()] - b = [l for l in b.split('\n') if l.strip()] - for n, line in enumerate(a): +def non_blank_line_by_line_compare(a,b): + a = textwrap.dedent(a) + b = textwrap.dedent(b) + a = [l.rstrip() for l in a.split('\n') if l.strip()] + b = [l.rstrip() for l in b.split('\n') if l.strip()] + for n,line in enumerate(a): if not line == b[n]: raise AssertionError("Lines %s of a and b differ: " "\n>>> %s\n<<< %s\n" % - (n, line, b[n])) - - + (n,line,b[n])) def test_str(): non_blank_line_by_line_compare(str(doc), - """numpy.multivariate_normal(mean, cov, shape=None) +"""numpy.multivariate_normal(mean, cov, shape=None, spam=None) Draw values from a multivariate normal distribution with specified mean and covariance. @@ -187,7 +211,7 @@ def test_str(): (1+2+3)/3 -cov : (N,N) ndarray +cov : (N, N) ndarray Covariance matrix of the distribution. shape : tuple of ints Given a shape of, for example, (m,n,k), m*n*k samples are @@ -203,6 +227,24 @@ def test_str(): In other words, each entry ``out[i,j,...,:]`` is an N-dimensional value drawn from the distribution. +list of str + This is not a real return value. It exists to test + anonymous return values. + +Other Parameters +---------------- +spam : parrot + A parrot off its mortal coil. + +Raises +------ +RuntimeError + Some error + +Warns +----- +RuntimeWarning + Some warning Warnings -------- @@ -247,13 +289,13 @@ def test_str(): >>> mean = (1,2) >>> cov = [[1,0],[1,0]] >>> x = multivariate_normal(mean,cov,(3,3)) ->>> print(x.shape) +>>> print x.shape (3, 3, 2) The following is probably true, given that 0.6 is roughly twice the standard deviation: ->>> print(list( (x[0,0,:] - mean) < 0.6 )) +>>> print list( (x[0,0,:] - mean) < 0.6 ) [True, True] .. index:: random @@ -263,7 +305,7 @@ def test_str(): def test_sphinx_str(): sphinx_doc = SphinxDocString(doc_txt) non_blank_line_by_line_compare(str(sphinx_doc), - """ +""" .. index:: random single: random;distributions, random;gauss @@ -283,7 +325,7 @@ def test_sphinx_str(): (1+2+3)/3 - **cov** : (N,N) ndarray + **cov** : (N, N) ndarray Covariance matrix of the distribution. @@ -304,6 +346,29 @@ def test_sphinx_str(): In other words, each entry ``out[i,j,...,:]`` is an N-dimensional value drawn from the distribution. + list of str + + This is not a real return value. It exists to test + anonymous return values. + +:Other Parameters: + + **spam** : parrot + + A parrot off its mortal coil. + +:Raises: + + **RuntimeError** + + Some error + +:Warns: + + **RuntimeWarning** + + Some warning + .. warning:: Certain warnings apply. @@ -351,13 +416,13 @@ def test_sphinx_str(): >>> mean = (1,2) >>> cov = [[1,0],[1,0]] >>> x = multivariate_normal(mean,cov,(3,3)) ->>> print(x.shape) +>>> print x.shape (3, 3, 2) The following is probably true, given that 0.6 is roughly twice the standard deviation: ->>> print(list( (x[0,0,:] - mean) < 0.6 )) +>>> print list( (x[0,0,:] - mean) < 0.6 ) [True, True] """) @@ -373,7 +438,6 @@ def test_sphinx_str(): If None, the index is into the flattened array, otherwise along the specified axis""") - def test_parameters_without_extended_description(): assert_equal(len(doc2['Parameters']), 2) @@ -383,7 +447,6 @@ def test_parameters_without_extended_description(): Return this and that. """) - def test_escape_stars(): signature = str(doc3).split('\n')[0] assert_equal(signature, 'my_signature(\*params, \*\*kwds)') @@ -393,7 +456,6 @@ def test_escape_stars(): Return an array with all complex-valued elements conjugated.""") - def test_empty_extended_summary(): assert_equal(doc4['Extended Summary'], []) @@ -406,19 +468,27 @@ def test_empty_extended_summary(): LinAlgException If array is singular. + Warns + ----- + SomeWarning + If needed """) - def test_raises(): assert_equal(len(doc5['Raises']), 1) - name, _, desc = doc5['Raises'][0] - assert_equal(name, 'LinAlgException') - assert_equal(desc, ['If array is singular.']) + name,_,desc = doc5['Raises'][0] + assert_equal(name,'LinAlgException') + assert_equal(desc,['If array is singular.']) +def test_warns(): + assert_equal(len(doc5['Warns']), 1) + name,_,desc = doc5['Warns'][0] + assert_equal(name,'SomeWarning') + assert_equal(desc,['If needed']) def test_see_also(): doc6 = NumpyDocString( - """ + """ z(x,theta) See Also @@ -458,10 +528,8 @@ def test_see_also(): elif func == 'class_j': assert desc == ['fubar', 'foobar'] - def test_see_also_print(): class Dummy(object): - """ See Also -------- @@ -484,7 +552,6 @@ class Dummy(object): """) - def test_empty_first_line(): assert doc7['Summary'][0].startswith('Doc starts') @@ -512,8 +579,8 @@ def test_unicode(): äää """) - assert doc['Summary'][0] == u'öäöäöäöäöåååå'.encode('utf-8') - + assert isinstance(doc['Summary'][0], str) + assert doc['Summary'][0] == 'öäöäöäöäöåååå' def test_plot_examples(): cfg = dict(use_plots=True) @@ -538,34 +605,163 @@ def test_plot_examples(): """, config=cfg) assert str(doc).count('plot::') == 1, str(doc) - def test_class_members(): class Dummy(object): - """ Dummy class. """ - def spam(self, a, b): """Spam\n\nSpam spam.""" pass - def ham(self, c, d): """Cheese\n\nNo cheese.""" pass + @property + def spammity(self): + """Spammity index""" + return 0.95 + + class Ignorable(object): + """local class, to be ignored""" + pass for cls in (ClassDoc, SphinxClassDoc): doc = cls(Dummy, config=dict(show_class_members=False)) assert 'Methods' not in str(doc), (cls, str(doc)) assert 'spam' not in str(doc), (cls, str(doc)) assert 'ham' not in str(doc), (cls, str(doc)) + assert 'spammity' not in str(doc), (cls, str(doc)) + assert 'Spammity index' not in str(doc), (cls, str(doc)) doc = cls(Dummy, config=dict(show_class_members=True)) assert 'Methods' in str(doc), (cls, str(doc)) assert 'spam' in str(doc), (cls, str(doc)) assert 'ham' in str(doc), (cls, str(doc)) + assert 'spammity' in str(doc), (cls, str(doc)) if cls is SphinxClassDoc: assert '.. autosummary::' in str(doc), str(doc) + else: + assert 'Spammity index' in str(doc), str(doc) + +def test_duplicate_signature(): + # Duplicate function signatures occur e.g. in ufuncs, when the + # automatic mechanism adds one, and a more detailed comes from the + # docstring itself. + + doc = NumpyDocString( + """ + z(x1, x2) + + z(a, theta) + """) + + assert doc['Signature'].strip() == 'z(a, theta)' + + +class_doc_txt = """ + Foo + + Parameters + ---------- + f : callable ``f(t, y, *f_args)`` + Aaa. + jac : callable ``jac(t, y, *jac_args)`` + Bbb. + + Attributes + ---------- + t : float + Current time. + y : ndarray + Current variable values. + + Methods + ------- + a + b + c + + Examples + -------- + For usage examples, see `ode`. +""" + +def test_class_members_doc(): + doc = ClassDoc(None, class_doc_txt) + non_blank_line_by_line_compare(str(doc), + """ + Foo + + Parameters + ---------- + f : callable ``f(t, y, *f_args)`` + Aaa. + jac : callable ``jac(t, y, *jac_args)`` + Bbb. + + Examples + -------- + For usage examples, see `ode`. + + Attributes + ---------- + t : float + Current time. + y : ndarray + Current variable values. + + Methods + ------- + a + + b + + c + + .. index:: + + """) + +def test_class_members_doc_sphinx(): + doc = SphinxClassDoc(None, class_doc_txt) + non_blank_line_by_line_compare(str(doc), + """ + Foo + + :Parameters: + + **f** : callable ``f(t, y, *f_args)`` + + Aaa. + + **jac** : callable ``jac(t, y, *jac_args)`` + + Bbb. + + .. rubric:: Examples + + For usage examples, see `ode`. + + .. rubric:: Attributes + + === ========== + t (float) Current time. + y (ndarray) Current variable values. + === ========== + + .. rubric:: Methods + + === ========== + a + b + c + === ========== + + """) + +if __name__ == "__main__": + import nose + nose.run() diff --git a/doc/sphinxext/numpydoc/tests/test_linkcode.py b/doc/sphinxext/numpydoc/tests/test_linkcode.py new file mode 100644 index 0000000000000..340166a485fcd --- /dev/null +++ b/doc/sphinxext/numpydoc/tests/test_linkcode.py @@ -0,0 +1,5 @@ +from __future__ import division, absolute_import, print_function + +import numpydoc.linkcode + +# No tests at the moment... diff --git a/doc/sphinxext/numpydoc/tests/test_phantom_import.py b/doc/sphinxext/numpydoc/tests/test_phantom_import.py new file mode 100644 index 0000000000000..173b5662b8df7 --- /dev/null +++ b/doc/sphinxext/numpydoc/tests/test_phantom_import.py @@ -0,0 +1,5 @@ +from __future__ import division, absolute_import, print_function + +import numpydoc.phantom_import + +# No tests at the moment... diff --git a/doc/sphinxext/numpydoc/tests/test_plot_directive.py b/doc/sphinxext/numpydoc/tests/test_plot_directive.py new file mode 100644 index 0000000000000..0e511fcbc1428 --- /dev/null +++ b/doc/sphinxext/numpydoc/tests/test_plot_directive.py @@ -0,0 +1,5 @@ +from __future__ import division, absolute_import, print_function + +import numpydoc.plot_directive + +# No tests at the moment... diff --git a/doc/sphinxext/numpydoc/tests/test_traitsdoc.py b/doc/sphinxext/numpydoc/tests/test_traitsdoc.py new file mode 100644 index 0000000000000..d36e5ddbd751f --- /dev/null +++ b/doc/sphinxext/numpydoc/tests/test_traitsdoc.py @@ -0,0 +1,5 @@ +from __future__ import division, absolute_import, print_function + +import numpydoc.traitsdoc + +# No tests at the moment... diff --git a/doc/sphinxext/traitsdoc.py b/doc/sphinxext/numpydoc/traitsdoc.py similarity index 86% rename from doc/sphinxext/traitsdoc.py rename to doc/sphinxext/numpydoc/traitsdoc.py index f39fe0c2e23da..596c54eb389a3 100755 --- a/doc/sphinxext/traitsdoc.py +++ b/doc/sphinxext/numpydoc/traitsdoc.py @@ -13,20 +13,22 @@ .. [2] http://code.enthought.com/projects/traits/ """ +from __future__ import division, absolute_import, print_function import inspect +import os import pydoc +import collections -import docscrape -from docscrape_sphinx import SphinxClassDoc, SphinxFunctionDoc, SphinxDocString +from . import docscrape +from . import docscrape_sphinx +from .docscrape_sphinx import SphinxClassDoc, SphinxFunctionDoc, SphinxDocString -import numpydoc - -import comment_eater +from . import numpydoc +from . import comment_eater class SphinxTraitsDoc(SphinxClassDoc): - def __init__(self, cls, modulename='', func_doc=SphinxFunctionDoc): if not inspect.isclass(cls): raise ValueError("Initialise using a class. Got %r" % cls) @@ -48,7 +50,7 @@ def __init__(self, cls, modulename='', func_doc=SphinxFunctionDoc): except ValueError: indent = 0 - for n, line in enumerate(docstring): + for n,line in enumerate(docstring): docstring[n] = docstring[n][indent:] self._doc = docscrape.Reader(docstring) @@ -70,7 +72,7 @@ def __init__(self, cls, modulename='', func_doc=SphinxFunctionDoc): 'Example': '', 'Examples': '', 'index': {} - } + } self._parse() @@ -87,17 +89,16 @@ def __str__(self, indent=0, func_role="func"): out += self._str_summary() out += self._str_extended_summary() for param_list in ('Parameters', 'Traits', 'Methods', - 'Returns', 'Raises'): + 'Returns','Raises'): out += self._str_param_list(param_list) out += self._str_see_also("obj") out += self._str_section('Notes') out += self._str_references() out += self._str_section('Example') out += self._str_section('Examples') - out = self._str_indent(out, indent) + out = self._str_indent(out,indent) return '\n'.join(out) - def looks_like_issubclass(obj, classname): """ Return True if the object has a class or superclass with the given class name. @@ -112,20 +113,18 @@ def looks_like_issubclass(obj, classname): return True return False - def get_doc_object(obj, what=None, config=None): if what is None: if inspect.isclass(obj): what = 'class' elif inspect.ismodule(obj): what = 'module' - elif callable(obj): + elif isinstance(obj, collections.Callable): what = 'function' else: what = 'object' if what == 'class': - doc = SphinxTraitsDoc( - obj, '', func_doc=SphinxFunctionDoc, config=config) + doc = SphinxTraitsDoc(obj, '', func_doc=SphinxFunctionDoc, config=config) if looks_like_issubclass(obj, 'HasTraits'): for name, trait, comment in comment_eater.get_class_traits(obj): # Exclude private traits. @@ -137,7 +136,7 @@ def get_doc_object(obj, what=None, config=None): else: return SphinxDocString(pydoc.getdoc(obj), config=config) - def setup(app): # init numpydoc numpydoc.setup(app, get_doc_object) + diff --git a/doc/sphinxext/only_directives.py b/doc/sphinxext/only_directives.py deleted file mode 100755 index 25cef30d21dc8..0000000000000 --- a/doc/sphinxext/only_directives.py +++ /dev/null @@ -1,103 +0,0 @@ -# -# A pair of directives for inserting content that will only appear in -# either html or latex. -# - -from docutils.nodes import Body, Element -from docutils.writers.html4css1 import HTMLTranslator -try: - from sphinx.latexwriter import LaTeXTranslator -except ImportError: - from sphinx.writers.latex import LaTeXTranslator - - import warnings - warnings.warn("The numpydoc.only_directives module is deprecated;" - "please use the only:: directive available in Sphinx >= 0.6", - DeprecationWarning, stacklevel=2) - -from docutils.parsers.rst import directives - - -class html_only(Body, Element): - pass - - -class latex_only(Body, Element): - pass - - -def run(content, node_class, state, content_offset): - text = '\n'.join(content) - node = node_class(text) - state.nested_parse(content, content_offset, node) - return [node] - -try: - from docutils.parsers.rst import Directive -except ImportError: - from docutils.parsers.rst.directives import _directives - - def html_only_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - return run(content, html_only, state, content_offset) - - def latex_only_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - return run(content, latex_only, state, content_offset) - - for func in (html_only_directive, latex_only_directive): - func.content = 1 - func.options = {} - func.arguments = None - - _directives['htmlonly'] = html_only_directive - _directives['latexonly'] = latex_only_directive -else: - class OnlyDirective(Directive): - has_content = True - required_arguments = 0 - optional_arguments = 0 - final_argument_whitespace = True - option_spec = {} - - def run(self): - self.assert_has_content() - return run(self.content, self.node_class, - self.state, self.content_offset) - - class HtmlOnlyDirective(OnlyDirective): - node_class = html_only - - class LatexOnlyDirective(OnlyDirective): - node_class = latex_only - - directives.register_directive('htmlonly', HtmlOnlyDirective) - directives.register_directive('latexonly', LatexOnlyDirective) - - -def setup(app): - app.add_node(html_only) - app.add_node(latex_only) - - # Add visit/depart methods to HTML-Translator: - def visit_perform(self, node): - pass - - def depart_perform(self, node): - pass - - def visit_ignore(self, node): - node.children = [] - - def depart_ignore(self, node): - node.children = [] - - HTMLTranslator.visit_html_only = visit_perform - HTMLTranslator.depart_html_only = depart_perform - HTMLTranslator.visit_latex_only = visit_ignore - HTMLTranslator.depart_latex_only = depart_ignore - - LaTeXTranslator.visit_html_only = visit_ignore - LaTeXTranslator.depart_html_only = depart_ignore - LaTeXTranslator.visit_latex_only = visit_perform - LaTeXTranslator.depart_latex_only = depart_perform diff --git a/doc/sphinxext/setup.py b/doc/sphinxext/setup.py deleted file mode 100755 index f73287eee2351..0000000000000 --- a/doc/sphinxext/setup.py +++ /dev/null @@ -1,32 +0,0 @@ -from distutils.core import setup -import setuptools -import sys -import os - -version = "0.3.dev" - -setup( - name="numpydoc", - packages=["numpydoc"], - package_dir={"numpydoc": ""}, - version=version, - description="Sphinx extension to support docstrings in Numpy format", - # classifiers from http://pypi.python.org/pypi?%3Aaction=list_classifiers - classifiers=["Development Status :: 3 - Alpha", - "Environment :: Plugins", - "License :: OSI Approved :: BSD License", - "Topic :: Documentation"], - keywords="sphinx numpy", - author="Pauli Virtanen and others", - author_email="pav@iki.fi", - url="http://projects.scipy.org/numpy/browser/trunk/doc/sphinxext", - license="BSD", - zip_safe=False, - install_requires=["Sphinx >= 0.5"], - package_data={'numpydoc': 'tests', '': ''}, - entry_points={ - "console_scripts": [ - "autosummary_generate = numpydoc.autosummary_generate:main", - ], - }, -)