diff --git a/numpydoc/docscrape.py b/numpydoc/docscrape.py index 5c09cdaf..470badd3 100644 --- a/numpydoc/docscrape.py +++ b/numpydoc/docscrape.py @@ -24,10 +24,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 +35,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(): @@ -67,8 +67,10 @@ def read_to_condition(self, condition_func): 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) def read_to_next_unindented_line(self): @@ -76,7 +78,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: @@ -139,17 +141,19 @@ def _is_at_section(self): if l1.startswith('.. index::'): return True - l2 = self._doc.peek(1).strip() # ---------- or ========== + 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] @@ -157,7 +161,7 @@ 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() @@ -169,14 +173,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(): @@ -189,13 +193,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 @@ -228,7 +232,8 @@ 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(':'): @@ -307,7 +312,8 @@ def _parse(self): for (section, content) in sections: if not section.startswith('..'): - section = ' '.join([s.capitalize() for s in section.split(' ')]) + section = (s.capitalize() for s in section.split(' ')) + section = ' '.join(section) if section in ('Parameters', 'Returns', 'Yields', 'Raises', 'Warns', 'Other Parameters', 'Attributes', 'Methods'): @@ -332,7 +338,7 @@ def _str_indent(self, doc, indent=4): def _str_signature(self): if self['Signature']: - return [self['Signature'].replace('*','\*')] + [''] + return [self['Signature'].replace('*', '\*')] + [''] else: return [''] @@ -352,7 +358,7 @@ def _str_param_list(self, name): out = [] if self[name]: out += self._str_header(name) - for param,param_type,desc in self[name]: + for param, param_type, desc in self[name]: if param_type: out += ['%s : %s' % (param, param_type)] else: @@ -370,7 +376,8 @@ 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 @@ -397,7 +404,7 @@ def _str_see_also(self, func_role): def _str_index(self): idx = self['index'] out = [] - out += ['.. index:: %s' % idx.get('default','')] + out += ['.. index:: %s' % idx.get('default', '')] for section, references in idx.items(): if section == 'default': continue @@ -414,7 +421,7 @@ def __str__(self, func_role=''): 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) @@ -422,17 +429,19 @@ def __str__(self, func_role=''): return '\n'.join(out) -def indent(str,indent=4): +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' @@ -440,7 +449,7 @@ def header(text, style='-'): 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: @@ -457,7 +466,7 @@ def __init__(self, func, role='func', doc=None, config={}): else: argspec = inspect.getargspec(func) argspec = inspect.formatargspec(*argspec) - argspec = argspec.replace('*','\*') + argspec = argspec.replace('*', '\*') signature = '%s%s' % (func_name, argspec) except TypeError as e: signature = '%s()' % func_name @@ -483,7 +492,7 @@ def __str__(self): if 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) @@ -500,8 +509,8 @@ def __init__(self, cls, doc=None, modulename='', func_doc=FunctionDoc, raise ValueError("Expected a class or None, but got %r" % cls) self._cls = cls - self.show_inherited_members = config.get('show_inherited_class_members', - True) + self.show_inherited_members = config.get( + 'show_inherited_class_members', True) if modulename and not modulename.endswith('.'): modulename += '.' diff --git a/numpydoc/docscrape_sphinx.py b/numpydoc/docscrape_sphinx.py index 0464f11f..6046edec 100644 --- a/numpydoc/docscrape_sphinx.py +++ b/numpydoc/docscrape_sphinx.py @@ -1,8 +1,13 @@ from __future__ import division, absolute_import, print_function -import sys, re, inspect, textwrap, pydoc +import sys +import re +import inspect +import textwrap +import pydoc import sphinx import collections + from .docscrape import NumpyDocString, FunctionDoc, ClassDoc if sys.version_info[0] >= 3: @@ -171,7 +176,7 @@ def _str_index(self): if len(idx) == 0: return out - out += ['.. index:: %s' % idx.get('default','')] + out += ['.. index:: %s' % idx.get('default', '')] for section, references in idx.items(): if section == 'default': continue @@ -192,9 +197,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) @@ -235,25 +240,29 @@ 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.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.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): diff --git a/numpydoc/numpydoc.py b/numpydoc/numpydoc.py index 6d848a6f..9b55086c 100644 --- a/numpydoc/numpydoc.py +++ b/numpydoc/numpydoc.py @@ -10,7 +10,8 @@ - Convert Parameters etc. sections to field lists. - Convert See Also section to a See also entry. - Renumber references. -- Extract the signature from the docstring, if it can't be determined otherwise. +- Extract the signature from the docstring, if it can't be determined + otherwise. .. [1] https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt @@ -39,28 +40,28 @@ 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_inherited_class_members=app.config.numpydoc_show_inherited_class_members, - class_members_toctree=app.config.numpydoc_class_members_toctree, - ) + cfg = {'use_plots': app.config.numpydoc_use_plots, + 'show_class_members': app.config.numpydoc_show_class_members, + 'show_inherited_class_members': + app.config.numpydoc_show_inherited_class_members, + 'class_members_toctree': app.config.numpydoc_class_members_toctree} + u_NL = sixu('\n') if what == 'module': # Strip top title - 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")) + pattern = '^\\s*[#*=]{4,}\\n[a-z0-9 -]+\\n[#*=]{4,}\\s*' + title_re = re.compile(sixu(pattern), re.I | re.S) + lines[:] = title_re.sub(sixu(''), u_NL.join(lines)).split(u_NL) else: - doc = get_doc_object(obj, what, sixu("\n").join(lines), config=cfg) + doc = get_doc_object(obj, what, u_NL.join(lines), config=cfg) if sys.version_info[0] >= 3: doc = str(doc) else: doc = unicode(doc) - lines[:] = doc.split(sixu("\n")) + lines[:] = doc.split(u_NL) - if app.config.numpydoc_edit_link and hasattr(obj, '__name__') and \ - obj.__name__: + if (app.config.numpydoc_edit_link and hasattr(obj, '__name__') and + obj.__name__): if hasattr(obj, '__module__'): v = dict(full_name=sixu("%s.%s") % (obj.__module__, obj.__name__)) else: @@ -93,24 +94,30 @@ def mangle_docstrings(app, what, name, obj, options, lines, 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 (isinstance(obj, collections.Callable) 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(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 + return # probably called by nose, better bail out global get_doc_object get_doc_object = get_doc_object_ @@ -127,14 +134,15 @@ def setup(app, get_doc_object_=get_doc_object): app.add_domain(NumpyPythonDomain) app.add_domain(NumpyCDomain) -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ # Docstring-mangling domains -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ from docutils.statemachine import ViewList from sphinx.domains.c import CDomain from sphinx.domains.python import PythonDomain + class ManglingDomainBase(object): directive_mangling_map = {} @@ -147,6 +155,7 @@ def wrap_mangling_directives(self): self.directives[name] = wrap_mangling_directive( self.directives[name], objtype) + class NumpyPythonDomain(ManglingDomainBase, PythonDomain): name = 'np' directive_mangling_map = { @@ -160,6 +169,7 @@ class NumpyPythonDomain(ManglingDomainBase, PythonDomain): } indices = [] + class NumpyCDomain(ManglingDomainBase, CDomain): name = 'np-c' directive_mangling_map = { @@ -170,6 +180,7 @@ class NumpyCDomain(ManglingDomainBase, CDomain): 'var': 'object', } + def wrap_mangling_directive(base_directive, objtype): class directive(base_directive): def run(self):