Permalink
Browse files

Merge branch 'enh-numpydoc'

There were some conflicts with the 2to3 work in numpy. I think I got the
fixes right.

* enh-numpydoc:
  DOC: fix doc/source/conf.py to work with Python 3
  BUG: numpydoc: check that it works with sub-classes
  TST: numpydoc: more class tests
  BUG: numpydoc: fix bugs in attribute docstring extraction + improve presentation
  TST: numpydoc: add stub test files, to check that files at least import
  MAINT: always use plot directive from Matplotlib, and prefer Sphinx linkcode
  ENH: numpydoc: Python 2 & 3 in single codebase, restructure as a package
  ENH: numpydoc: deal with duplicated signatures
  DOC: numpydoc/linkcode: mention that the extension will be in Sphinx upstream
  BUG: numpydoc/linkcode: do not detect linkcode config changes

Conflicts:
	doc/sphinxext/numpydoc/docscrape.py
	doc/sphinxext/numpydoc/docscrape_sphinx.py
	doc/sphinxext/numpydoc/linkcode.py
	doc/sphinxext/numpydoc/phantom_import.py
	doc/sphinxext/numpydoc/traitsdoc.py
  • Loading branch information...
2 parents 4b361f6 + 9d8722b commit 629a2d4daa376e5639ad5106289c77b8137f9f15 @charris charris committed Mar 1, 2013
View
@@ -20,24 +20,8 @@
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.pngmath', 'numpydoc',
'sphinx.ext.intersphinx', 'sphinx.ext.coverage',
- 'sphinx.ext.doctest', 'sphinx.ext.autosummary']
-
-# Determine if the matplotlib has a recent enough version of the
-# plot_directive, otherwise use the local fork.
-try:
- from matplotlib.sphinxext import plot_directive
-except ImportError:
- use_matplotlib_plot_directive = False
-else:
- try:
- use_matplotlib_plot_directive = (plot_directive.__version__ >= 2)
- except AttributeError:
- use_matplotlib_plot_directive = False
-
-if use_matplotlib_plot_directive:
- extensions.append('matplotlib.sphinxext.plot_directive')
-else:
- extensions.append('plot_directive')
+ 'sphinx.ext.doctest', 'sphinx.ext.autosummary',
+ 'matplotlib.sphinxext.plot_directive']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -61,7 +45,7 @@
version = re.sub(r'(\.dev\d+).*?$', r'\1', version)
# The full version, including alpha/beta/rc tags.
release = numpy.__version__
-print version, release
+print("%s %s" % (version, release))
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
@@ -283,9 +267,6 @@
'text.usetex': False,
}
-if not use_matplotlib_plot_directive:
- import matplotlib
- matplotlib.rcParams.update(plot_rcparams)
# -----------------------------------------------------------------------------
# Source code links
@@ -294,15 +275,15 @@
import inspect
from os.path import relpath, dirname
-for name in ['sphinx.ext.linkcode', 'linkcode', 'numpydoc.linkcode']:
+for name in ['sphinx.ext.linkcode', 'numpydoc.linkcode']:
try:
__import__(name)
extensions.append(name)
break
except ImportError:
pass
else:
- print "NOTE: linkcode extension not found -- no links to source generated"
+ print("NOTE: linkcode extension not found -- no links to source generated")
def linkcode_resolve(domain, info):
"""
@@ -1,2 +1,2 @@
-recursive-include tests *.py
+recursive-include numpydoc/tests *.py
include *.txt
@@ -1 +0,0 @@
-from numpydoc import setup
@@ -0,0 +1 @@
+from .numpydoc import setup
@@ -1,10 +1,15 @@
-from cStringIO import StringIO
+import sys
+if sys.version_info[0] >= 3:
+ from io import StringIO
+else:
+ from cStringIO import StringIO
+
import compiler
import inspect
import textwrap
import tokenize
-from compiler_unparse import unparse
+from .compiler_unparse import unparse
class Comment(object):
@@ -68,7 +73,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()
@@ -12,11 +12,15 @@
"""
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 cStringIO import StringIO
+
def unparse(ast, single_line_functions=False):
- s = cStringIO.StringIO()
+ s = StringIO()
UnparseCompilerAst(ast, s, single_line_functions)
return s.getvalue().lstrip()
@@ -2,14 +2,19 @@
"""
+import sys
import inspect
import textwrap
import re
import pydoc
-from StringIO import StringIO
from warnings import warn
import collections
+if sys.version_info[0] >= 3:
+ from io import StringIO
+else:
+ from cStringIO import StringIO
+
class Reader(object):
"""A line-based string reader.
@@ -114,7 +119,7 @@ def __getitem__(self,key):
return self._parsed_data[key]
def __setitem__(self,key,val):
- if not key in self._parsed_data:
+ if key not in self._parsed_data:
warn("Unknown section %s" % key)
else:
self._parsed_data[key] = val
@@ -266,13 +271,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():
@@ -371,7 +380,7 @@ def _str_index(self):
idx = self['index']
out = []
out += ['.. index:: %s' % idx.get('default','')]
- for section, references in idx.iteritems():
+ for section, references in idx.items():
if section == 'default':
continue
out += [' :%s: %s' % (section, ', '.join(references))]
@@ -451,8 +460,8 @@ def __str__(self):
'meth': 'method'}
if self._role:
- if not self._role in roles:
- print "Warning: invalid role %s" % 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,''),
func_name)
@@ -482,12 +491,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):
@@ -503,4 +519,6 @@ def properties(self):
if self._cls is None:
return []
return [name for name,func in inspect.getmembers(self._cls)
- if not name.startswith('_') and func is None]
+ if not name.startswith('_') and
+ (func is None or isinstance(func, property) or
+ inspect.isgetsetdescriptor(func))]
@@ -1,7 +1,7 @@
import re, inspect, textwrap, pydoc
import sphinx
-from docscrape import NumpyDocString, FunctionDoc, ClassDoc
import collections
+from .docscrape import NumpyDocString, FunctionDoc, ClassDoc
class SphinxDocString(NumpyDocString):
def __init__(self, docstring, config={}):
@@ -73,7 +73,16 @@ 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))
@@ -83,15 +92,15 @@ def _str_member_list(self, name):
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 = u"="*maxlen_0 + u" " + u"="*10
+ fmt = u'%%%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 = u" ".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
@@ -128,7 +137,7 @@ def _str_index(self):
return out
out += ['.. index:: %s' % idx.get('default','')]
- for section, references in idx.iteritems():
+ for section, references in idx.items():
if section == 'default':
continue
elif section == 'refguide':
@@ -11,8 +11,9 @@
import warnings
import collections
-warnings.warn("This extension has been submitted to Sphinx upstream. "
- "Use the version from there if it is accepted "
+
+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)
@@ -77,4 +78,4 @@ def doctree_read(app, doctree):
def setup(app):
app.connect('doctree-read', doctree_read)
- app.add_config_value('linkcode_resolve', None, 'env')
+ app.add_config_value('linkcode_resolve', None, '')
@@ -22,8 +22,8 @@
if sphinx.__version__ < '1.0.1':
raise RuntimeError("Sphinx 1.0.1 or newer is required")
-import os, re, pydoc
-from docscrape_sphinx import get_doc_object, SphinxDocString
+import os, sys, re, pydoc
+from .docscrape_sphinx import get_doc_object, SphinxDocString
from sphinx.util.compat import Directive
import inspect
@@ -35,28 +35,32 @@ def mangle_docstrings(app, what, name, obj, options, lines,
if what == 'module':
# Strip top title
- title_re = re.compile(ur'^\s*[#*=]{4,}\n[a-z0-9 -]+\n[#*=]{4,}\s*',
+ title_re = re.compile(u'^\\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")
else:
doc = get_doc_object(obj, what, u"\n".join(lines), config=cfg)
- lines[:] = unicode(doc).split(u"\n")
+ if sys.version_info[0] >= 3:
+ doc = str(doc)
+ else:
+ doc = str(doc).decode('utf-8')
+ lines[:] = doc.split(u"\n")
if app.config.numpydoc_edit_link and hasattr(obj, '__name__') and \
obj.__name__:
if hasattr(obj, '__module__'):
v = dict(full_name=u"%s.%s" % (obj.__module__, obj.__name__))
else:
v = dict(full_name=obj.__name__)
- lines += [u'', u'.. htmlonly::', '']
+ lines += [u'', u'.. htmlonly::', u'']
lines += [u' %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(u'^.. \\[([a-z0-9_.-])\\]', line, re.I)
if m:
references.append(m.group(1))
@@ -65,7 +69,7 @@ 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):
+ if re.match(u'^\\d+$', r):
new_r = u"R%d" % (reference_offset[0] + int(r))
else:
new_r = u"%s%d" % (r, reference_offset[0])
@@ -92,6 +96,9 @@ def mangle_signature(app, what, name, obj, options, sig, retann):
return sig, u''
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_
@@ -121,7 +128,7 @@ 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)
Oops, something went wrong.

0 comments on commit 629a2d4

Please sign in to comment.