1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
|
import sys
import re
import os
from inspect import ismodule
from warnings import warn
from sphinx.ext.autosummary.generate import find_autosummary_in_docstring
__all__ = ['cleanup_whitespace',
'find_mod_objs', 'find_autosummary_in_lines_for_automodsumm']
# We use \n instead of os.linesep because even on Windows, the generated files
# use \n as the newline character.
SPACE_NEWLINE = ' \n'
SINGLE_NEWLINE = '\n'
DOUBLE_NEWLINE = '\n\n'
TRIPLE_NEWLINE = '\n\n\n'
def cleanup_whitespace(text):
"""
Make sure there are never more than two consecutive newlines, and that
there are no trailing whitespaces.
"""
# Get rid of overall leading/trailing whitespace
text = text.strip() + '\n'
# Get rid of trailing whitespace on each line
while SPACE_NEWLINE in text:
text = text.replace(SPACE_NEWLINE, SINGLE_NEWLINE)
# Avoid too many consecutive newlines
while TRIPLE_NEWLINE in text:
text = text.replace(TRIPLE_NEWLINE, DOUBLE_NEWLINE)
return text
def find_mod_objs(modname, onlylocals=False, sort=False):
""" Returns all the public attributes of a module referenced by name.
.. note::
The returned list *not* include subpackages or modules of
`modname`,nor does it include private attributes (those that
beginwith '_' or are not in `__all__`).
Parameters
----------
modname : str
The name of the module to search.
onlylocals : bool or list
If `True`, only attributes that are either members of `modname` OR one of
its modules or subpackages will be included. If a list, only members
of packages in the list are included. If `False`, selection is done.
This option is ignored if a module defines __all__ - in that case, __all__
is used to determine whether objects are public.
Returns
-------
localnames : list of str
A list of the names of the attributes as they are named in the
module `modname` .
fqnames : list of str
A list of the full qualified names of the attributes (e.g.,
``astropy.utils.misc.find_mod_objs``). For attributes that are
simple variables, this is based on the local name, but for
functions or classes it can be different if they are actually
defined elsewhere and just referenced in `modname`.
objs : list of objects
A list of the actual attributes themselves (in the same order as
the other arguments)
"""
__import__(modname)
mod = sys.modules[modname]
# Note: use getattr instead of mod.__dict__[k] for modules that
# define their own __getattr__ and __dir__.
if hasattr(mod, '__all__'):
pkgitems = [(k, getattr(mod, k)) for k in mod.__all__]
# Optionally sort the items alphabetically
if sort:
pkgitems.sort()
onlylocals = False
else:
pkgitems = [(k, getattr(mod, k)) for k in dir(mod) if k[0] != "_"]
# filter out modules and pull the names and objs out
localnames = [k for k, v in pkgitems if not ismodule(v)]
objs = [v for k, v in pkgitems if not ismodule(v)]
# fully qualified names can be determined from the object's module
fqnames = []
for obj, lnm in zip(objs, localnames):
if hasattr(obj, '__module__') and hasattr(obj, '__name__'):
fqnames.append(obj.__module__ + '.' + obj.__name__)
else:
fqnames.append(modname + '.' + lnm)
if onlylocals:
if isinstance(onlylocals, (tuple, list)):
modname = tuple(onlylocals)
valids = [fqn.startswith(modname) for fqn in fqnames]
localnames = [e for i, e in enumerate(localnames) if valids[i]]
fqnames = [e for i, e in enumerate(fqnames) if valids[i]]
objs = [e for i, e in enumerate(objs) if valids[i]]
return localnames, fqnames, objs
def find_autosummary_in_lines_for_automodsumm(lines, module=None, filename=None):
"""Find out what items appear in autosummary:: directives in the
given lines.
Returns a list of (name, toctree, template, inherited_members, noindex)
where *name* is a name
of an object and *toctree* the :toctree: path of the corresponding
autosummary directive (relative to the root of the file name),
*template* the value of the :template: option, and *inherited_members*
is the value of the :inherited-members: option.
*toctree*, *template*, and *inherited_members* are ``None`` if the
directive does not have the corresponding options set.
.. note::
This is a slightly modified version of
``sphinx.ext.autosummary.generate.find_autosummary_in_lines``
which recognizes the ``inherited-members`` option.
"""
autosummary_re = re.compile(r'^(\s*)\.\.\s+autosummary::\s*')
automodule_re = re.compile(
r'^\s*\.\.\s+automodule::\s*([A-Za-zäüöÄÜÖßő0-9_.]+)\s*$')
module_re = re.compile(
r'^\s*\.\.\s+(current)?module::\s*([a-zA-ZäüöÄÜÖßő0-9_.]+)\s*$')
autosummary_item_re = re.compile(r'^\s+(~?[_a-zA-ZäüöÄÜÖßő][a-zA-ZäüöÄÜÖßő0-9_.]*)\s*.*?')
toctree_arg_re = re.compile(r'^\s+:toctree:\s*(.*?)\s*$')
template_arg_re = re.compile(r'^\s+:template:\s*(.*?)\s*$')
inherited_members_arg_re = re.compile(r'^\s+:inherited-members:\s*$')
no_inherited_members_arg_re = re.compile(r'^\s+:no-inherited-members:\s*$')
noindex_arg_re = re.compile(r'^\s+:noindex:\s*$')
other_options_re = re.compile(r'^\s+:nosignatures:\s*$')
documented = []
toctree = None
template = None
inherited_members = None
noindex = None
current_module = module
in_autosummary = False
base_indent = ""
for line in lines:
if in_autosummary:
m = toctree_arg_re.match(line)
if m:
toctree = m.group(1)
if filename:
toctree = os.path.join(os.path.dirname(filename),
toctree)
continue
m = template_arg_re.match(line)
if m:
template = m.group(1).strip()
continue
m = inherited_members_arg_re.match(line)
if m:
inherited_members = True
continue
m = no_inherited_members_arg_re.match(line)
if m:
inherited_members = False
continue
m = noindex_arg_re.match(line)
if m:
noindex = True
continue
if line.strip().startswith(':'):
if other_options_re.match(line):
continue # skip known options
else:
warn(line) # warn about unknown options
m = autosummary_item_re.match(line)
if m:
name = m.group(1).strip()
if name.startswith('~'):
name = name[1:]
if current_module and \
not name.startswith(current_module + '.'):
name = "%s.%s" % (current_module, name)
documented.append((name, toctree, template,
inherited_members, noindex))
continue
if not line.strip() or line.startswith(base_indent + " "):
continue
in_autosummary = False
m = autosummary_re.match(line)
if m:
in_autosummary = True
base_indent = m.group(1)
toctree = None
template = None
inherited_members = None
continue
m = automodule_re.search(line)
if m:
current_module = m.group(1).strip()
# recurse into the automodule docstring
documented.extend(find_autosummary_in_docstring(
current_module, filename=filename))
continue
m = module_re.match(line)
if m:
current_module = m.group(2)
continue
return documented
|