This commit is contained in:
Georg Brandl 2010-02-23 21:35:31 +01:00
commit f6260eb6e0
14 changed files with 186 additions and 45 deletions

10
CHANGES
View File

@ -1,11 +1,16 @@
Release 1.0 (in development)
============================
* Sphinx now requires Jinja2 version 2.2 or greater.
* Added ``needs_sphinx`` config value and ``Sphinx.require_sphinx``
application API function.
* Added single-file HTML builder.
* Added ``autodoc_default_flags`` config value, which can be used
to select default flags for all autodoc directives.
* Added ``tab-width`` option to ``literalinclude`` directive.
* The ``html_sidebars`` config value can now contain patterns as
@ -84,6 +89,11 @@ Release 1.0 (in development)
Release 0.6.5 (in development)
==============================
* In autodoc, allow customizing the signature of an object where
the built-in mechanism fails.
* #331: Fix output for enumerated lists with start values in LaTeX.
* Make the ``start-after`` and ``end-before`` options to the
``literalinclude`` directive work correctly if not used together.

View File

@ -81,6 +81,11 @@ The builder's "name" must be given to the **-b** command-line option of
details about it. For definition of the epub format, have a look at
`<http://www.idpf.org/specs.htm>`_ or `<http://en.wikipedia.org/wiki/EPUB>`_.
Some ebook readers do not show the link targets of references. Therefore
this builder adds the targets after the link when necessary. The display
of the URLs can be customized by adding CSS rules for the class
``link-target``.
Its name is ``epub``.
.. module:: sphinx.builders.latex

View File

@ -308,9 +308,8 @@ Project information
.. confval:: pygments_style
The style name to use for Pygments highlighting of source code. Default is
``'sphinx'``, which is a builtin style designed to match Sphinx' default
style.
The style name to use for Pygments highlighting of source code. The default
style is selected by the theme for HTML output, and ``'sphinx'`` otherwise.
.. versionchanged:: 0.3
If the value is a fully-qualified name of a custom Pygments style class,
@ -708,6 +707,12 @@ the `Dublin Core metadata <http://dublincore.org/>`_.
A list of files that are generated/copied in the build directory but should
not be included in the epub file. The default value is ``[]``.
.. confval:: epub_tocdepth
The depth of the table of contents in the file :file:`toc.ncx`. It should
be an integer greater than zero. The default value is 3. Note: A deeply
nested table of contents may be difficult to navigate.
.. _latex-options:

View File

@ -228,6 +228,24 @@ There are also new config values that you can set:
.. versionadded:: 0.6
.. confval:: autodoc_default_flags
This value is a list of autodoc directive flags that should be automatically
applied to all autodoc directives. The supported flags are ``'members'``,
``'undoc-members'``, ``'inherited-members'`` and ``'show-inheritance'``.
If you set one of these flags in this config value, you can use a negated
form, :samp:`'no-{flag}'`, in an autodoc directive, to disable it once.
For example, if ``autodoc_default_flags`` is set to ``['members',
'undoc-members']``, and you write a directive like this::
.. automodule:: foo
:no-undoc-members:
the directive will be interpreted as if only ``:members:`` was given.
.. versionadded:: 1.0
Docstring preprocessing
-----------------------

View File

@ -97,6 +97,8 @@ The :mod:`sphinx.ext.autosummary` extension does this in two parts:
:confval:`templates_path` to generate the pages for all entries
listed. See `Customizing templates`_ below.
.. versionadded:: 1.0
:program:`sphinx-autogen` -- generate autodoc stub pages
--------------------------------------------------------
@ -142,6 +144,8 @@ also use this new config value:
Customizing templates
---------------------
.. versionadded:: 1.0
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.)

View File

@ -40,7 +40,7 @@ A development egg can be found `here
<http://bitbucket.org/birkenfeld/sphinx/get/tip.gz#egg=Sphinx-dev>`_.
'''
requires = ['Pygments>=0.8', 'Jinja2>=2.1', 'docutils>=0.4']
requires = ['Pygments>=0.8', 'Jinja2>=2.2', 'docutils>=0.4']
if sys.version_info < (2, 4):
print 'ERROR: Sphinx requires at least Python 2.4 to run.'

View File

@ -16,6 +16,7 @@ from os import path
import zipfile
from docutils import nodes
from docutils.transforms import Transform
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.util.osutil import EEXIST
@ -23,6 +24,9 @@ from sphinx.util.osutil import EEXIST
# (Fragment) templates from which the metainfo files content.opf, toc.ncx,
# mimetype, and META-INF/container.xml are created.
# This template section also defines strings that are embedded in the html
# output but that may be customized by (re-)setting module attributes,
# e.g. from conf.py.
_mimetype_template = 'application/epub+zip' # no EOL!
@ -99,6 +103,10 @@ _spine_template = u'''\
_toctree_template = u'toctree-l%d'
_link_target_template = u' [%(uri)s]'
_css_link_target_class = u'link-target'
_media_types = {
'.html': 'application/xhtml+xml',
'.css': 'text/css',
@ -112,6 +120,30 @@ _media_types = {
}
# The transform to show link targets
class VisibleLinksTransform(Transform):
"""
Add the link target of referances to the text, unless it is already
present in the description.
"""
# This transform must run after the references transforms
default_priority = 680
def apply(self):
for ref in self.document.traverse(nodes.reference):
uri = ref.get('refuri', '')
if ( uri.startswith('http:') or uri.startswith('https:') or \
uri.startswith('ftp:') ) and uri not in ref.astext():
uri = _link_target_template % {'uri': uri}
if uri:
idx = ref.parent.index(ref) + 1
link = nodes.inline(uri, uri)
link['classes'].append(_css_link_target_class)
ref.parent.insert(idx, link)
# The epub publisher
class EpubBuilder(StandaloneHTMLBuilder):
@ -138,6 +170,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
# the output files for epub must be .html only
self.out_suffix = '.html'
self.playorder = 0
self.app.add_transform(VisibleLinksTransform)
def get_theme_config(self):
return self.config.epub_theme, {}
@ -145,7 +178,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
# generic support functions
def make_id(self, name):
"""Replace all characters not allowed for (X)HTML ids."""
return name.replace('/', '_')
return name.replace('/', '_').replace(' ', '')
def esc(self, name):
"""Replace all characters not allowed in text an attribute values."""
@ -157,34 +190,20 @@ class EpubBuilder(StandaloneHTMLBuilder):
name = name.replace('\'', '&apos;')
return name
def collapse_text(self, doctree, result):
"""Remove all HTML markup and return only the text nodes."""
for c in doctree.children:
if isinstance(c, nodes.Text):
try:
# docutils 0.4 and 0.5: Text is a UserString subclass
result.append(c.data)
except AttributeError:
# docutils 0.6: Text is a unicode subclass
result.append(c)
else:
result = self.collapse_text(c, result)
return result
def get_refnodes(self, doctree, result):
"""Collect section titles, their depth in the toc and the refuri."""
# XXX: is there a better way than checking the attribute
# toctree-l[1-6] on the parent node?
# toctree-l[1-8] on the parent node?
if isinstance(doctree, nodes.reference):
classes = doctree.parent.attributes['classes']
level = 1
for l in range(5,0,-1): # or range(1,6)?
for l in range(8, 0, -1): # or range(1, 8)?
if (_toctree_template % l) in classes:
level = l
result.append({
'level': level,
'refuri': self.esc(doctree['refuri']),
'text': self.esc(''.join(self.collapse_text(doctree, [])))
'text': self.esc(doctree.astext())
})
else:
for elem in doctree.children:
@ -195,16 +214,14 @@ class EpubBuilder(StandaloneHTMLBuilder):
"""Get the total table of contents, containg the master_doc
and pre and post files not managed by sphinx.
"""
doctree = self.env.get_and_resolve_doctree(self.config.master_doc, self)
doctree = self.env.get_and_resolve_doctree(self.config.master_doc,
self, prune_toctrees=False)
self.refnodes = self.get_refnodes(doctree, [])
self.refnodes.insert(0, {
'level': 1,
'refuri': self.esc(self.config.master_doc + '.html'),
'text': self.esc(''.join(self.collapse_text(
self.env.titles[self.config.master_doc], []
))),
'text': self.esc(self.env.titles[self.config.master_doc].astext())
})
# XXX: is reversed ok?
for file, text in reversed(self.config.epub_pre_files):
self.refnodes.insert(0, {
'level': 1,
@ -290,11 +307,10 @@ class EpubBuilder(StandaloneHTMLBuilder):
for fn in files:
filename = path.join(root, fn)[olen:]
if filename in self.ignored_files:
# self.warn("ignoring %s" % filename)
continue
ext = path.splitext(filename)[-1]
if ext not in _media_types:
self.warn("unknown mimetype for %s, ignoring" % filename)
self.warn('unknown mimetype for %s, ignoring' % filename)
continue
projectfiles.append(_file_template % {
'href': self.esc(filename),
@ -338,7 +354,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
"""Insert nested navpoints for given node.
The node and subnav are already rendered to text.
"""
nlist = node.split('\n')
nlist = node.rsplit('\n', 1)
nlist.insert(-1, subnav)
return '\n'.join(nlist)
@ -356,6 +372,8 @@ class EpubBuilder(StandaloneHTMLBuilder):
file = node['refuri'].split('#')[0]
if file in self.ignored_files:
continue
if node['level'] > self.config.epub_tocdepth:
continue
if node['level'] == level:
navlist.append(self.new_navpoint(node, level))
elif node['level'] == level + 1:
@ -398,6 +416,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
navpoints = self.build_navpoints(self.refnodes)
level = max(item['level'] for item in self.refnodes)
level = min(level, self.config.epub_tocdepth)
f = codecs.open(path.join(outdir, outname), 'w', 'utf-8')
try:
f.write(_toc_template % self.toc_metadata(level, navpoints))

View File

@ -117,6 +117,7 @@ class Config(object):
epub_pre_files = ([], 'env'),
epub_post_files = ([], 'env'),
epub_exclude_files = ([], 'env'),
epub_tocdepth = (3, 'env'),
# LaTeX options
latex_documents = ([], None),

View File

@ -377,7 +377,12 @@ class Documenter(object):
args = "(%s)" % self.args
else:
# try to introspect the signature
try:
args = self.format_args()
except Exception, err:
self.directive.warn('error while formatting arguments for '
'%s: %s' % (self.fullname, err))
args = None
retann = self.retann
@ -663,12 +668,7 @@ class Documenter(object):
self.add_line(u'', '')
# format the object's signature, if any
try:
sig = self.format_signature()
except Exception, err:
self.directive.warn('error while formatting signature for '
'%s: %s' % (self.fullname, err))
sig = ''
# generate the directive header and options, if applicable
self.add_directive_header(sig)
@ -868,7 +868,6 @@ class ClassDocumenter(ModuleLevelDocumenter):
return ret
def format_args(self):
args = None
# for classes, the relevant signature is the __init__ method's
initmeth = self.get_attr(self.object, '__init__', None)
# classes without __init__ method, default __init__ or
@ -1098,6 +1097,10 @@ class AutoDirective(Directive):
# a registry of type -> getattr function
_special_attrgetters = {}
# flags that can be given in autodoc_default_flags
_default_flags = set(['members', 'undoc-members', 'inherited-members',
'show-inheritance'])
# standard docutils directive settings
has_content = True
required_arguments = 1
@ -1120,6 +1123,14 @@ class AutoDirective(Directive):
# find out what documenter to call
objtype = self.name[4:]
doc_class = self._registry[objtype]
# add default flags
for flag in self._default_flags:
if flag not in doc_class.option_spec:
continue
negated = self.options.pop('no-' + flag, 'not given') is None
if flag in self.env.config.autodoc_default_flags and \
not negated:
self.options[flag] = None
# process the options with the selected documenter's option_spec
self.genopt = Options(assemble_option_dict(
self.options.items(), doc_class.option_spec))
@ -1177,6 +1188,7 @@ def setup(app):
app.add_config_value('autoclass_content', 'class', True)
app.add_config_value('autodoc_member_order', 'alphabetic', True)
app.add_config_value('autodoc_default_flags', [], True)
app.add_event('autodoc-process-docstring')
app.add_event('autodoc-process-signature')
app.add_event('autodoc-skip-member')

View File

@ -258,6 +258,9 @@ latex_documents = [
# A list of files that should not be packed into the epub file.
#epub_exclude_files = []
# The depth of the table of contents in toc.ncx.
#epub_tocdepth = 3
'''
INTERSPHINX_CONFIG = '''

View File

@ -22,7 +22,41 @@ from sphinx.util.console import darkred, nocolor, color_terminal
class BuildDoc(Command):
"""Distutils command to build Sphinx documentation."""
"""Distutils command to build Sphinx documentation.
The Sphinx build can then be triggered from distutils, and some Sphinx
options can be set in ``setup.py`` or ``setup.cfg`` instead of Sphinx own
configuration file.
For instance, from `setup.py`::
# this is only necessary when not using setuptools/distribute
from sphinx.setup_command import BuildDoc
cmdclass = {'build_sphinx': BuildDoc}
name = 'My project'
version = 1.2
release = 1.2.0
setup(
name=name,
author='Bernard Montgomery',
version=release,
cmdclass={'build_sphinx': BuildDoc},
# these are optional and override conf.py settings
command_options={
'build_sphinx': {
'project': ('setup.py', name),
'version': ('setup.py', version),
'release': ('setup.py', release)}},
)
Or add this section in ``setup.cfg``::
[build_sphinx]
project = 'My project'
version = 1.2
release = 1.2.0
"""
description = 'Build Sphinx documentation'
user_options = [
@ -31,6 +65,11 @@ class BuildDoc(Command):
('source-dir=', 's', 'Source directory'),
('build-dir=', None, 'Build directory'),
('builder=', 'b', 'The builder to use. Defaults to "html"'),
('project=', None, 'The documented project\'s name'),
('version=', None, 'The short X.Y version'),
('release=', None, 'The full version, including alpha/beta/rc tags'),
('today=', None, 'How to format the current date, used as the '
'replacement for |today|'),
]
boolean_options = ['fresh-env', 'all-files']
@ -38,8 +77,10 @@ class BuildDoc(Command):
def initialize_options(self):
self.fresh_env = self.all_files = False
self.source_dir = self.build_dir = None
self.conf_file_name = 'conf.py'
self.builder = 'html'
self.version = ''
self.release = ''
self.today = ''
def _guess_source_dir(self):
for guess in ('doc', 'docs'):
@ -74,9 +115,16 @@ class BuildDoc(Command):
status_stream = StringIO()
else:
status_stream = sys.stdout
confoverrides = {}
if self.version:
confoverrides['version'] = self.version
if self.release:
confoverrides['release'] = self.release
if self.today:
confoverrides['today'] = self.today
app = Sphinx(self.source_dir, self.source_dir,
self.builder_target_dir, self.doctree_dir,
self.builder, {}, status_stream,
self.builder, confoverrides, status_stream,
freshenv=self.fresh_env)
try:

View File

@ -420,6 +420,19 @@ div.footer a {
text-decoration: underline;
}
/* -- link-target ----------------------------------------------------------- */
.link-target {
font-size: 80%;
}
table .link-target {
/* Do not show links in tables, there is not enough space */
display: none;
}
/* -- font-face ------------------------------------------------------------- */
@font-face {
font-family: "LiberationNarrow";
font-style: normal;

View File

@ -741,6 +741,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_enumerated_list(self, node):
self.body.append('\\begin{enumerate}\n' )
if 'start' in node:
self.body.append('\\setcounter{enumi}{%d}\n' % (node['start'] - 1))
def depart_enumerated_list(self, node):
self.body.append('\\end{enumerate}\n' )

View File

@ -169,8 +169,9 @@ def test_format_signature():
assert formatsig('method', 'H.foo', H.foo1, 'a', None) == '(a)'
assert formatsig('method', 'H.foo', H.foo2, None, None) == '(b, *c)'
# test exception handling
raises(TypeError, formatsig, 'function', 'int', int, None, None)
# test exception handling (exception is caught and args is '')
assert formatsig('function', 'int', int, None, None) == ''
del _warnings[:]
# test processing by event handler
assert formatsig('method', 'bar', H.foo1, None, None) == '42'