Skip to content

Commit

Permalink
Merge pull request astropy/astropy#2370 from eteq/auto-outside-pkg
Browse files Browse the repository at this point in the history
Allow automodapi to document objects not in the package being documented
  • Loading branch information
eteq committed Apr 28, 2014
2 parents eeacbdc + bb7f6d5 commit d1d71c5
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 17 deletions.
67 changes: 53 additions & 14 deletions sphinx_automodapi/automodapi.py
Expand Up @@ -40,6 +40,15 @@
* ``:no-heading:``
If specified do not create a top level heading for the section.
That is, do not create a title heading with text like "packagename
Package". The actual docstring for the package/module will still be
shown, though, unless ``:no-main-docstr:`` is given.
* ``:allowed-package-names: str``
Specifies the packages that functions/classes documented here are
allowed to be from, as comma-separated list of package names. If not
given, only objects that are actually in a subpackage of the package
currently being documented are included.
This extension also adds two sphinx configuration options:
Expand Down Expand Up @@ -84,8 +93,7 @@
.. automodsumm:: {modname}
:classes-only:
{toctree}
{skips}
{clsfuncoptions}
"""

automod_templ_funcs = """
Expand All @@ -94,8 +102,7 @@
.. automodsumm:: {modname}
:functions-only:
{toctree}
{skips}
{clsfuncoptions}
"""

automod_templ_inh = """
Expand All @@ -105,6 +112,7 @@
.. automod-diagram:: {modname}
:private-bases:
:parts: 1
{allowedpkgnms}
"""

_automodapirex = re.compile(r'^(?:\s*\.\.\s+automodapi::\s*)([A-Za-z0-9_.]+)'
Expand Down Expand Up @@ -181,6 +189,7 @@ def automodapi_replace(sourcestr, app, dotoctree=True, docname=None,
toskip = []
inhdiag = maindocstr = top_head = True
hds = '-^'
allowedpkgnms = []

# look for actual options
unknownops = []
Expand All @@ -195,9 +204,19 @@ def automodapi_replace(sourcestr, app, dotoctree=True, docname=None,
hds = args
elif opname == 'no-heading':
top_head = False
elif opname == 'allowed-package-names':
allowedpkgnms.append(args.strip())
else:
unknownops.append(opname)

#join all the allowedpkgnms
if len(allowedpkgnms) == 0:
allowedpkgnms = ''
onlylocals = True
else:
allowedpkgnms = ':allowed-package-names: ' + ','.join(allowedpkgnms)
onlylocals = allowedpkgnms

# get the two heading chars
if len(hds) < 2:
msg = 'Not enough headings (got {0}, need 2), using default -^'
Expand All @@ -213,7 +232,7 @@ def automodapi_replace(sourcestr, app, dotoctree=True, docname=None,
if warnings:
app.warn(msg, location)

ispkg, hascls, hasfuncs = _mod_info(modnm, toskip)
ispkg, hascls, hasfuncs = _mod_info(modnm, toskip, onlylocals=onlylocals)

# add automodule directive only if no-main-docstr isn't present
if maindocstr:
Expand All @@ -226,23 +245,43 @@ def automodapi_replace(sourcestr, app, dotoctree=True, docname=None,
pkgormod='Package' if ispkg else 'Module',
pkgormodhds=h1 * (8 if ispkg else 7),
automoduleline=automodline))
else:
newstrs.append(automod_templ_modheader.format(
modname='',
modhds='',
pkgormod='',
pkgormodhds='',
automoduleline=automodline))

#construct the options for the class/function sections
#start out indented at 4 spaces, but need to keep the indentation.
clsfuncoptions = []
if toctreestr:
clsfuncoptions.append(toctreestr)
if toskip:
clsfuncoptions.append(':skip: ' + ','.join(toskip))
if allowedpkgnms:
clsfuncoptions.append(allowedpkgnms)
clsfuncoptionstr = '\n '.join(clsfuncoptions)

if hasfuncs:
newstrs.append(automod_templ_funcs.format(modname=modnm,
newstrs.append(automod_templ_funcs.format(
modname=modnm,
funchds=h2 * 9,
toctree=toctreestr,
skips=':skip: ' + ','.join(toskip) if toskip else ''))
clsfuncoptions=clsfuncoptionstr))

if hascls:
newstrs.append(automod_templ_classes.format(modname=modnm,
newstrs.append(automod_templ_classes.format(
modname=modnm,
clshds=h2 * 7,
toctree=toctreestr,
skips=':skip: ' + ','.join(toskip) if toskip else ''))
clsfuncoptions=clsfuncoptionstr))

if inhdiag and hascls:
# add inheritance diagram if any classes are in the module
newstrs.append(automod_templ_inh.format(
modname=modnm, clsinhsechds=h2 * 25))
modname=modnm,
clsinhsechds=h2 * 25,
allowedpkgnms=allowedpkgnms))

newstrs.append(spl[grp * 3 + 3])

Expand All @@ -269,7 +308,7 @@ def automodapi_replace(sourcestr, app, dotoctree=True, docname=None,
return sourcestr


def _mod_info(modname, toskip=[]):
def _mod_info(modname, toskip=[], onlylocals=True):
"""
Determines if a module is a module or a package and whether or not
it has classes or functions.
Expand All @@ -279,7 +318,7 @@ def _mod_info(modname, toskip=[]):

hascls = hasfunc = False

for localnm, fqnm, obj in zip(*find_mod_objs(modname, onlylocals=True)):
for localnm, fqnm, obj in zip(*find_mod_objs(modname, onlylocals=onlylocals)):
if localnm not in toskip:
hascls = hascls or inspect.isclass(obj)
hasfunc = hasfunc or inspect.isfunction(obj)
Expand Down
25 changes: 22 additions & 3 deletions sphinx_automodapi/automodsumm.py
Expand Up @@ -39,6 +39,12 @@
and not have their documentation generated, nor be includded in
the summary table.
* ``:allowed-package-names: pkgormod1, [pkgormod2, pkgormod3, ...]``
Specifies the packages that functions/classes documented here are
allowed to be from, as comma-separated list of package names. If not
given, only objects that are actually in a subpackage of the package
currently being documented are included.
This extension also adds one sphinx configuration option:
* `automodsumm_writereprocessed`
Expand Down Expand Up @@ -97,6 +103,7 @@ class Automodsumm(AstropyAutosummary):
option_spec['functions-only'] = flag
option_spec['classes-only'] = flag
option_spec['skip'] = _str_list_converter
option_spec['allowed-package-names'] = _str_list_converter

def run(self):
env = self.state.document.settings.env
Expand Down Expand Up @@ -163,9 +170,16 @@ def run(self):

#<-------------------automod-diagram stuff------------------------------------>
class Automoddiagram(InheritanceDiagram):

option_spec = dict(InheritanceDiagram.option_spec)
option_spec['allowed-package-names'] = _str_list_converter

def run(self):
try:
nms, objs = find_mod_objs(self.arguments[0], onlylocals=True)[1:]
ols = self.options.get('allowed-package-names', [])
ols = True if len(ols) == 0 else ols # if none are given, assume only local

nms, objs = find_mod_objs(self.arguments[0], onlylocals=ols)[1:]
except ImportError:
self.warnings = []
self.warn("Couldn't import module " + self.arguments[0])
Expand Down Expand Up @@ -276,11 +290,12 @@ def automodsumm_to_autosummary_lines(fn, app):
#loop over all automodsumms in this document
for i, (i1, i2, modnm, ops, rem) in enumerate(zip(indent1s, indent2s, mods,
opssecs, remainders)):
allindent = i1 + i2
allindent = i1 + ('' if i2 is None else i2)

#filter out functions-only and classes-only options if present
oplines = ops.split('\n')
toskip = []
allowedpkgnms = []
funcsonly = clssonly = False
for i, ln in reversed(list(enumerate(oplines))):
if ':functions-only:' in ln:
Expand All @@ -292,6 +307,9 @@ def automodsumm_to_autosummary_lines(fn, app):
if ':skip:' in ln:
toskip.extend(_str_list_converter(ln.replace(':skip:', '')))
del oplines[i]
if ':allowed-package-names:' in ln:
allowedpkgnms.extend(_str_list_converter(ln.replace(':allowed-package-names:', '')))
del oplines[i]
if funcsonly and clssonly:
msg = ('Defined both functions-only and classes-only options. '
'Skipping this directive.')
Expand All @@ -308,7 +326,8 @@ def automodsumm_to_autosummary_lines(fn, app):
'.. autosummary::'])
newlines.extend(oplines)

for nm, fqn, obj in zip(*find_mod_objs(modnm, onlylocals=True)):
ols = True if len(allowedpkgnms) == 0 else allowedpkgnms
for nm, fqn, obj in zip(*find_mod_objs(modnm, onlylocals=ols)):
if nm in toskip:
continue
if funcsonly and not inspect.isfunction(obj):
Expand Down

0 comments on commit d1d71c5

Please sign in to comment.