merge with pv/sphinx-work

This commit is contained in:
Georg Brandl
2009-05-17 12:24:04 +02:00
7 changed files with 379 additions and 142 deletions

View File

@@ -73,7 +73,8 @@ The :mod:`sphinx.ext.autosummary` extension does this in two parts:
that stub pages should be generated for the entries listed in this
directive. The option accepts a directory name as an argument;
:program:`sphinx-autogen` will by default place its output in this
directory.
directory. If no argument is given, output is placed in the same directory
as the file that contains the directive.
* If you don't want the :dir:`autosummary` to show function signatures in the
listing, include the ``nosignatures`` option::
@@ -84,6 +85,18 @@ The :mod:`sphinx.ext.autosummary` extension does this in two parts:
sphinx.environment.BuildEnvironment
sphinx.util.relative_uri
* You can specify a custom template with the ``template`` option.
For example, ::
.. autosummary::
:template: mytemplate.rst
sphinx.environment.BuildEnvironment
would use the template :file:`mytemplate.rst` in your
:confval:`templates_path` to generate the pages for all entries
listed. See `Customizing templates`_ below.
:program:`sphinx-autogen` -- generate autodoc stub pages
--------------------------------------------------------
@@ -117,5 +130,98 @@ also use this new config value:
.. confval:: autosummary_generate
A list of documents for which stub pages should be generated. They will be
placed in the directories specified in the ``:toctree:`` options.
Boolean indicating whether to scan all found documents for
autosummary directives, and to generate stub pages for each.
Can also be a list of documents for which stub pages should be
generated.
The new files will be placed in the directories specified in the
``:toctree:`` options of the directives.
Customizing templates
---------------------
You can customize the stub page templates, in a similar way as the
HTML Jinja templates, see
:ref:`templating`. (:class:`~sphinx.application.TemplateBridge` is not
supported.)
.. note::
If you find yourself spending much time tailoring the stub
templates, this may indicate that it's a better idea to write
custom narrative documentation instead.
Autosummary uses the following template files:
- :file:`autosummary/base.rst` -- fallback template
- :file:`autosummary/module.rst` -- template for modules
- :file:`autosummary/class.rst` -- template for classes
- :file:`autosummary/function.rst` -- template for functions
- :file:`autosummary/attribute.rst` -- template for class attributes
- :file:`autosummary/method.rst` -- template for class methods
The following variables available in the templates:
.. data:: name
Name of the documented object, excluding the module and class parts.
.. data:: objname
Name of the documented object, excluding the module parts.
.. data:: fullname
Full name of the documented object, including module and class parts.
.. data:: module
Name of the module the documented object belongs to.
.. data:: class
Name of the class the documented object belongs to.
Only available for methods and attributes.
.. data:: underline
A string containing ``len(full_name) * '='``.
.. data:: members
List containing names of all members of the module or class.
Only available for modules and classes.
.. data:: functions
List containing names of "public" functions in the module.
Here, "public" here means that the name does not start with an
underscore. Only available for modules.
.. data:: classes
List containing names of "public" classes in the module.
Only available for modules.
.. data:: exceptions
List containing names of "public" exceptions in the module.
Only available for modules.
.. data:: methods
List containing names of "public" methods in the class.
Only available for classes.
.. data:: methods
List containing names of "public" attributes in the class.
Only available for classes.
.. note::
You can use the :dir:`autosummary` directive in the stub pages.
However, stub pages are not generated automatically recursively.

View File

@@ -101,14 +101,31 @@ def autosummary_toc_visit_html(self, node):
"""Hide autosummary toctree list in HTML output."""
raise nodes.SkipNode
def autosummary_toc_visit_latex(self, node):
"""Show autosummary toctree (= put the referenced pages here) in Latex."""
pass
def autosummary_noop(self, node):
pass
# -- autosummary_table node ----------------------------------------------------
class autosummary_table(nodes.comment):
pass
def autosummary_table_visit_html(self, node):
"""Make the first column of the table non-breaking."""
try:
tbody = node[0][0][-1]
for row in tbody:
col1_entry = row[0]
par = col1_entry[0]
for j, subnode in enumerate(list(par)):
if isinstance(subnode, nodes.Text):
new_text = unicode(subnode.astext())
new_text = new_text.replace(u" ", u"\u00a0")
par[j] = nodes.Text(new_text)
except IndexError:
pass
# -- autodoc integration -------------------------------------------------------
def get_documenter(obj):
@@ -150,6 +167,7 @@ class Autosummary(Directive):
option_spec = {
'toctree': directives.unchanged,
'nosignatures': directives.flag,
'template': directives.unchanged,
}
def warn(self, msg):
@@ -162,9 +180,9 @@ class Autosummary(Directive):
self.warnings = []
names = [x.strip().split()[0] for x in self.content
if x.strip() and re.search(r'^[a-zA-Z_]', x.strip()[0])]
if x.strip() and re.search(r'^[~a-zA-Z_]', x.strip()[0])]
items = self.get_items(names)
nodes = [self.get_table(items)]
nodes = self.get_table(items)
if 'toctree' in self.options:
suffix = env.config.source_suffix
@@ -199,12 +217,22 @@ class Autosummary(Directive):
Try to import the given names, and return a list of
``[(name, signature, summary_string, real_name), ...]``.
"""
env = self.state.document.settings.env
prefixes = ['']
prefixes.insert(0, self.state.document.settings.env.currmodule)
if env.currmodule:
prefixes.insert(0, env.currmodule)
items = []
max_item_chars = 50
for name in names:
display_name = name
if name.startswith('~'):
name = name[1:]
display_name = name.split('.')[-1]
try:
obj, real_name = import_by_name(name, prefixes=prefixes)
except ImportError:
@@ -213,15 +241,15 @@ class Autosummary(Directive):
continue
# NB. using real_name here is important, since Documenters
# don't handle module prefixes slightly differently
# handle module prefixes slightly differently
documenter = get_documenter(obj)(self, real_name)
if not documenter.parse_name():
self.warn('failed to parse name %s' % real_name)
items.append((name, '', '', real_name))
items.append((display_name, '', '', real_name))
continue
if not documenter.import_object():
self.warn('failed to import object %s' % real_name)
items.append((name, '', '', real_name))
items.append((display_name, '', '', real_name))
continue
# -- Grab the signature
@@ -230,7 +258,9 @@ class Autosummary(Directive):
if not sig:
sig = ''
else:
sig = mangle_signature(sig).replace('*', r'\*')
max_chars = max(10, max_item_chars - len(display_name))
sig = mangle_signature(sig, max_chars=max_chars)
sig = sig.replace('*', r'\*')
# -- Grab the summary
@@ -246,19 +276,24 @@ class Autosummary(Directive):
else:
summary = ''
items.append((name, sig, summary, real_name))
items.append((display_name, sig, summary, real_name))
return items
def get_table(self, items):
"""
Generate a proper table node for autosummary:: directive.
Generate a proper list of table nodes for autosummary:: directive.
*items* is a list produced by :meth:`get_items`.
"""
table = nodes.table('')
table_spec = addnodes.tabular_col_spec()
table_spec['spec'] = 'LL'
table = autosummary_table('')
real_table = nodes.table('')
table.append(real_table)
group = nodes.tgroup('', cols=2)
table.append(group)
real_table.append(group)
group.append(nodes.colspec('', colwidth=10))
group.append(nodes.colspec('', colwidth=90))
body = nodes.tbody('')
@@ -288,7 +323,7 @@ class Autosummary(Directive):
col2 = summary
append_row(col1, col2)
return table
return [table_spec, table]
def mangle_signature(sig, max_chars=30):
"""Reformat a function signature to a more compact form."""
@@ -296,34 +331,42 @@ def mangle_signature(sig, max_chars=30):
r = re.compile(r"(?P<name>[a-zA-Z0-9_*]+)(?P<default>=.*?)?, ")
items = r.findall(sig)
args = []
opts = []
args = [name for name, default in items if not default]
opts = [name for name, default in items if default]
total_len = 4
for name, default in items:
if default:
opts.append(name)
else:
args.append(name)
total_len += len(name) + 2
sig = limited_join(", ", args, max_chars=max_chars-2)
if opts:
if not sig:
sig = "[%s]" % limited_join(", ", opts, max_chars=max_chars-4)
elif len(sig) < max_chars - 4 - 2 - 3:
sig += "[, %s]" % limited_join(", ", opts,
max_chars=max_chars-len(sig)-4-2)
if total_len > max_chars:
if opts:
opts.append('...')
else:
args.append('...')
break
if opts and args:
sig = ", ".join(args) + "[, " + ", ".join(opts) + "]"
elif opts and not args:
sig = "[" + ", ".join(opts) + "]"
else:
sig = ", ".join(args)
sig = unicode(sig).replace(u" ", u"\u00a0")
return u"(%s)" % sig
def limited_join(sep, items, max_chars=30, overflow_marker="..."):
"""
Join a number of strings to one, limiting the length to *max_chars*.
If the string overflows this limit, replace the last fitting item by
*overflow_marker*.
Returns: joined_string
"""
full_str = sep.join(items)
if len(full_str) < max_chars:
return full_str
n_chars = 0
n_items = 0
for j, item in enumerate(items):
n_chars += len(item) + len(sep)
if n_chars < max_chars - len(overflow_marker):
n_items += 1
else:
break
return sep.join(list(items[:n_items]) + [overflow_marker])
# -- Importing items -----------------------------------------------------------
@@ -409,15 +452,25 @@ def autolink_role(typ, rawtext, etext, lineno, inliner,
def process_generate_options(app):
genfiles = app.config.autosummary_generate
if not genfiles:
return
from sphinx.ext.autosummary.generate import generate_autosummary_docs
ext = app.config.source_suffix
if genfiles and not hasattr(genfiles, '__len__'):
env = app.builder.env
genfiles = [x + ext for x in env.found_docs
if os.path.isfile(env.doc2path(x))]
if not genfiles:
return
from sphinx.ext.autosummary.generate import generate_autosummary_docs
genfiles = [genfile + (not genfile.endswith(ext) and ext or '')
for genfile in genfiles]
generate_autosummary_docs(genfiles, warn=app.warn, info=app.info,
suffix=ext, base_path=app.srcdir)
generate_autosummary_docs(genfiles, builder=app.builder,
warn=app.warn, info=app.info, suffix=ext,
base_path=app.srcdir)
def setup(app):
@@ -425,7 +478,11 @@ def setup(app):
app.setup_extension('sphinx.ext.autodoc')
app.add_node(autosummary_toc,
html=(autosummary_toc_visit_html, autosummary_noop),
latex=(autosummary_toc_visit_latex, autosummary_noop),
latex=(autosummary_noop, autosummary_noop),
text=(autosummary_noop, autosummary_noop))
app.add_node(autosummary_table,
html=(autosummary_table_visit_html, autosummary_noop),
latex=(autosummary_noop, autosummary_noop),
text=(autosummary_noop, autosummary_noop))
app.add_directive('autosummary', Autosummary)
app.add_role('autolink', autolink_role)

View File

@@ -24,11 +24,12 @@ import optparse
import inspect
import pydoc
from jinja2 import Environment, PackageLoader
from jinja2 import FileSystemLoader, TemplateNotFound
from jinja2.sandbox import SandboxedEnvironment
from sphinx.ext.autosummary import import_by_name, get_documenter
from sphinx.util import ensuredir
from sphinx.jinja2glue import BuiltinTemplateLoader
def main(argv=sys.argv):
usage = """%prog [OPTIONS] SOURCEFILE ..."""
@@ -39,14 +40,17 @@ def main(argv=sys.argv):
p.add_option("-s", "--suffix", action="store", type="string",
dest="suffix", default="rst",
help="Default suffix for files (default: %default)")
p.add_option("-t", "--templates", action="store", type="string",
dest="templates", default=None,
help="Custom template directory (default: %default)")
options, args = p.parse_args(argv[1:])
if len(args) < 1:
p.error('no input files given')
generate_autosummary_docs(args, options.output_dir,
"." + options.suffix)
"." + options.suffix,
template_dir=options.templates)
def _simple_info(msg):
print msg
@@ -56,15 +60,15 @@ def _simple_warn(msg):
# -- Generating output ---------------------------------------------------------
# create our own templating environment, for module template only
env = Environment(loader=PackageLoader('sphinx.ext.autosummary', 'templates'))
def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
warn=_simple_warn, info=_simple_info,
base_path=None):
base_path=None, builder=None, template_dir=None):
showed_sources = list(sorted(sources))
if len(showed_sources) > 20:
showed_sources = showed_sources[:10] + ['...'] + showed_sources[-10:]
info('[autosummary] generating autosummary for: %s' %
', '.join(sorted(sources)))
', '.join(showed_sources))
if output_dir:
info('[autosummary] writing to %s' % output_dir)
@@ -72,6 +76,18 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
if base_path is not None:
sources = [os.path.join(base_path, filename) for filename in sources]
# create our own templating environment
template_dirs = [os.path.join(os.path.dirname(__file__), 'templates')]
if builder is not None:
# allow the user to override the templates
template_loader = BuiltinTemplateLoader()
template_loader.init(builder, dirs=template_dirs)
else:
if template_dir:
template_dirs.insert(0, template_dir)
template_loader = FileSystemLoader(template_dirs)
template_env = SandboxedEnvironment(loader=template_loader)
# read
items = find_autosummary_in_files(sources)
@@ -79,7 +95,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
items = dict([(item, True) for item in items]).keys()
# write
for name, path in sorted(items):
for name, path, template_name in sorted(items):
if path is None:
# The corresponding autosummary:: directive did not have
# a :toctree: option
@@ -103,52 +119,66 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
f = open(fn, 'w')
try:
if inspect.ismodule(obj):
tmpl = env.get_template('module')
doc = get_documenter(obj)
def get_items(mod, typ):
return [
getattr(mod, name).__name__ for name in dir(mod)
if get_documenter(getattr(mod, name)).objtype == typ
]
functions = get_items(obj, 'function')
classes = get_items(obj, 'class')
exceptions = get_items(obj, 'exception')
rendered = tmpl.render(name=name,
underline='='*len(name),
functions=functions,
classes=classes,
exceptions=exceptions,
len_functions=len(functions),
len_classes=len(classes),
len_exceptions=len(exceptions))
f.write(rendered)
if template_name is not None:
template = template_env.get_template(template_name)
else:
f.write('%s\n%s\n\n' % (name, '='*len(name)))
try:
template = template_env.get_template('autosummary/%s.rst'
% doc.objtype)
except TemplateNotFound:
template = template_env.get_template('autosummary/base.rst')
doc = get_documenter(obj)
if doc.objtype in ('method', 'attribute'):
f.write(format_classmember(name, 'auto%s' % doc.objtype))
else:
f.write(format_modulemember(name, 'auto%s' % doc.objtype))
def get_members(obj, typ, include_public=[]):
items = [
name for name in dir(obj)
if get_documenter(getattr(obj, name)).objtype == typ
]
public = [x for x in items
if x in include_public or not x.startswith('_')]
return public, items
info = {}
if doc.objtype == 'module':
info['members'] = dir(obj)
info['functions'], info['all_functions'] = \
get_members(obj, 'function')
info['classes'], info['all_classes'] = \
get_members(obj, 'class')
info['exceptions'], info['all_exceptions'] = \
get_members(obj, 'exception')
elif doc.objtype == 'class':
info['members'] = dir(obj)
info['methods'], info['all_methods'] = \
get_members(obj, 'method', ['__init__'])
info['attributes'], info['all_attributes'] = \
get_members(obj, 'attribute')
parts = name.split('.')
if doc.objtype in ('method', 'attribute'):
mod_name = '.'.join(parts[:-2])
cls_name = parts[-2]
obj_name = '.'.join(parts[-2:])
info['class'] = cls_name
else:
mod_name, obj_name = '.'.join(parts[:-1]), parts[-1]
info['fullname'] = name
info['module'] = mod_name
info['objname'] = obj_name
info['name'] = parts[-1]
info['objtype'] = doc.objtype
info['underline'] = len(name) * '='
rendered = template.render(**info)
f.write(rendered)
finally:
f.close()
def format_modulemember(name, directive):
parts = name.split('.')
mod, name = '.'.join(parts[:-1]), parts[-1]
return '.. currentmodule:: %s\n\n.. %s:: %s\n' % (mod, directive, name)
def format_classmember(name, directive):
parts = name.split('.')
mod, name = '.'.join(parts[:-2]), '.'.join(parts[-2:])
return '.. currentmodule:: %s\n\n.. %s:: %s\n' % (mod, directive, name)
# -- Finding documented entries in files ---------------------------------------
def find_autosummary_in_files(filenames):
@@ -183,20 +213,24 @@ def find_autosummary_in_lines(lines, module=None, filename=None):
"""
Find out what items appear in autosummary:: directives in the given lines.
Returns a list of (name, toctree) 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). *toctree* is ``None`` if
the directive does not have the :toctree: option set.
Returns a list of (name, toctree, template) 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), and
*template* the value of the :template: option. *toctree* and
*template* ``None`` if the directive does not have the
corresponding options set.
"""
autosummary_re = re.compile(r'^\.\.\s+autosummary::\s*')
automodule_re = re.compile(r'.. automodule::\s*([A-Za-z0-9_.]+)\s*$')
module_re = re.compile(r'^\.\.\s+(current)?module::\s*([a-zA-Z0-9_.]+)\s*$')
autosummary_item_re = re.compile(r'^\s+([_a-zA-Z][a-zA-Z0-9_.]*)\s*.*?')
toctree_arg_re = re.compile(r'^\s+:toctree:\s*(.*?)\s*$')
template_arg_re = re.compile(r'^\s+:template:\s*(.*?)\s*$')
documented = []
toctree = None
template = None
current_module = module
in_autosummary = False
@@ -210,6 +244,11 @@ def find_autosummary_in_lines(lines, module=None, filename=None):
toctree)
continue
m = template_arg_re.match(line)
if m:
template = m.group(1).strip()
continue
if line.strip().startswith(':'):
continue # skip options
@@ -219,7 +258,7 @@ def find_autosummary_in_lines(lines, module=None, filename=None):
if current_module and \
not name.startswith(current_module + '.'):
name = "%s.%s" % (current_module, name)
documented.append((name, toctree))
documented.append((name, toctree, template))
continue
if not line.strip():
@@ -231,6 +270,7 @@ def find_autosummary_in_lines(lines, module=None, filename=None):
if m:
in_autosummary = True
toctree = None
template = None
continue
m = automodule_re.search(line)

View File

@@ -0,0 +1,6 @@
{{ fullname }}
{{ underline }}
.. currentmodule:: {{ module }}
.. auto{{ objtype }}:: {{ objname }}

View File

@@ -0,0 +1,30 @@
{{ fullname }}
{{ underline }}
.. currentmodule:: {{ module }}
.. autoclass:: {{ objname }}
{% block methods %}
.. automethod:: __init__
{% if methods %}
.. rubric:: Methods
.. autosummary::
{% for item in methods %}
~{{ name }}.{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}
{% block attributes %}
{% if attributes %}
.. rubric:: Attributes
.. autosummary::
{% for item in attributes %}
~{{ name }}.{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,37 @@
{{ fullname }}
{{ underline }}
.. automodule:: {{ fullname }}
{% block functions %}
{% if functions %}
.. rubric:: Functions
.. autosummary::
{% for item in functions %}
{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}
{% block classes %}
{% if classes %}
.. rubric:: Classes
.. autosummary::
{% for item in classes %}
{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}
{% block exceptions %}
{% if exceptions %}
.. rubric:: Exceptions
.. autosummary::
{% for item in classes %}
{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}

View File

@@ -1,39 +0,0 @@
:mod:`{{name}}`
======{{ underline }}=
.. automodule:: {{name}}
{% if len_functions > 0 %}
Functions
----------
{% for item in functions %}
.. autofunction:: {{item}}
{% endfor %}
{% endif %}
{% if len_classes > 0 %}
Classes
--------
{% for item in classes %}
.. autoclass:: {{item}}
:show-inheritance:
:members:
:inherited-members:
:undoc-members:
{% endfor %}
{% endif %}
{% if len_exceptions > 0 %}
Exceptions
------------
{% for item in exceptions %}
.. autoclass:: {{item}}
:show-inheritance:
:members:
:inherited-members:
:undoc-members:
{% endfor %}
{% endif %}