@@ -64,7 +64,7 @@
{%- endif %}
{%- if customsidebar %}
- {{ rendertemplate(customsidebar) }}
+ {% include customsidebar %}
{%- endif %}
{%- block sidebarsearch %}
{%- if pagename != "search" %}
diff --git a/sphinx/templates/modindex.html b/sphinx/templates/modindex.html
index d6b505da..a2d2bb9a 100644
--- a/sphinx/templates/modindex.html
+++ b/sphinx/templates/modindex.html
@@ -52,7 +52,7 @@
{% if fname %}{% endif -%}
{{ modname|e }}
{%- if fname %}{% endif %}
- {%- if pform[0] %} ({{ pform|join(', ') }}){% endif -%}
+ {%- if pform and pform[0] %} ({{ pform|join(', ') }}){% endif -%}
{% if dep %}{{ _('Deprecated')}}:{% endif %}
{{ synops|e }}
{%- endif -%}
--
cgit v1.2.1
From 71355f44f63d6fba8767d08d6d3ba0c15267006d Mon Sep 17 00:00:00 2001
From: Sebastian Wiesner
Date: Thu, 20 Nov 2008 20:06:36 +0100
Subject: Fixed encoding issue in pngmath hashing
---
sphinx/ext/pngmath.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/ext/pngmath.py b/sphinx/ext/pngmath.py
index 77d08eef..aa0d0c2e 100644
--- a/sphinx/ext/pngmath.py
+++ b/sphinx/ext/pngmath.py
@@ -75,7 +75,7 @@ def render_math(self, math):
"""
use_preview = self.builder.config.pngmath_use_preview
- shasum = "%s.png" % sha(math).hexdigest()
+ shasum = "%s.png" % sha(math.encode('utf-8')).hexdigest()
relfn = posixpath.join(self.builder.imgpath, 'math', shasum)
outfn = path.join(self.builder.outdir, '_images', 'math', shasum)
if path.isfile(outfn):
--
cgit v1.2.1
From 72e0a250c7d891171e2c648ee585e6ec8b453aa7 Mon Sep 17 00:00:00 2001
From: Sebastian Wiesner
Date: Thu, 20 Nov 2008 20:12:41 +0100
Subject: Fixed syntax error in setup.py
---
setup.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/setup.py b/setup.py
index ba80ea23..c3fbcf2d 100644
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,4 @@
-0;115;0c# -*- coding: utf-8 -*-
+# -*- coding: utf-8 -*-
import ez_setup
ez_setup.use_setuptools()
--
cgit v1.2.1
From 35b9921582ff30eb1b7c343cfcf45cc41208a045 Mon Sep 17 00:00:00 2001
From: Vsevolod Solovyov
Date: Mon, 24 Nov 2008 16:55:06 +0200
Subject: Fixes #32. sphinx-quickstart adapted for windows
---
sphinx/quickstart.py | 130 +++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 125 insertions(+), 5 deletions(-)
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index 03024535..998d93fb 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -318,6 +318,109 @@ linkcheck:
\t "or in %(rbuilddir)s/linkcheck/output.txt."
'''
+BATCHFILE = '''\
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+set SPHINXBUILD=sphinx-build
+set ALLSPHINXOPTS=-d %(rbuilddir)s/doctrees %%SPHINXOPTS%% %(rsrcdir)s
+if NOT "%%PAPER%%" == "" (
+\tset ALLSPHINXOPTS=-D latex_paper_size=%%PAPER%% %%ALLSPHINXOPTS%%
+)
+
+if "%%1" == "" goto help
+
+if "%%1" == "help" (
+\t:help
+\techo.Please use `make-docs ^` where ^ is one of
+\techo. html to make standalone HTML files
+\techo. pickle to make pickle files
+\techo. json to make JSON files
+\techo. htmlhelp to make HTML files and a HTML help project
+\techo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+\techo. changes to make an overview over all changed/added/deprecated items
+\techo. linkcheck to check all external links for integrity
+\tgoto end
+)
+
+if "%%1" == "clean" (
+\tfor /d %%%%i in (%(rbuilddir)s\*) do rmdir /q /s %%%%i
+\tdel /q /s %(rbuilddir)s\*
+\tgoto end
+)
+
+if "%%1" == "html" (
+\tcall :mkdir %(rbuilddir)s\html %(rbuilddir)s\doctrees
+\t%%SPHINXBUILD%% -b html %%ALLSPHINXOPTS%% %(rbuilddir)s/html
+\techo.
+\techo.Build finished. The HTML pages are in %(rbuilddir)s/html.
+\tgoto end
+)
+
+if "%%1" == "web" goto pickle
+
+if "%%1" == "pickle" (
+\t:pickle
+\tcall :mkdir %(rbuilddir)s\pickle %(rbuilddir)s\doctrees
+\t%%SPHINXBUILD%% -b pickle %%ALLSPHINXOPTS%% %(rbuilddir)s/pickle
+\techo.
+\techo.Build finished; now you can process the pickle files.
+\tgoto end
+)
+
+if "%%1" == "json" (
+\tcall :mkdir %(rbuilddir)s\json %(rbuilddir)s\doctrees
+\t%%SPHINXBUILD%% -b json %%ALLSPHINXOPTS%% %(rbuilddir)s/json
+\techo.
+\techo.Build finished; now you can process the JSON files.
+\tgoto end
+)
+
+if "%%1" == "htmlhelp" (
+\tcall :mkdir %(rbuilddir)s\htmlhelp %(rbuilddir)s\doctrees
+\t%%SPHINXBUILD%% -b htmlhelp %%ALLSPHINXOPTS%% %(rbuilddir)s/htmlhelp
+\techo.
+\techo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %(rbuilddir)s/htmlhelp.
+\tgoto end
+)
+
+if "%%1" == "latex" (
+\tcall :mkdir %(rbuilddir)s\latex %(rbuilddir)s\doctrees
+\t%%SPHINXBUILD%% -b latex %%ALLSPHINXOPTS%% %(rbuilddir)s/latex
+\techo.
+\techo.Build finished; the LaTeX files are in %(rbuilddir)s/latex.
+\tgoto end
+)
+
+if "%%1" == "changes" (
+\tcall :mkdir %(rbuilddir)s\changes %(rbuilddir)s\doctrees
+\t%%SPHINXBUILD%% -b changes %%ALLSPHINXOPTS%% %(rbuilddir)s/changes
+\techo.
+\techo.The overview file is in %(rbuilddir)s/changes.
+\tgoto end
+)
+
+if "%%1" == "linkcheck" (
+\tcall :mkdir %(rbuilddir)s\linkcheck %(rbuilddir)s\doctrees
+\t%%SPHINXBUILD%% -b linkcheck %%ALLSPHINXOPTS%% %(rbuilddir)s/linkcheck
+\techo.
+\techo.Link check complete; look for any errors in the above output ^
+or in %(rbuilddir)s/linkcheck/output.txt.
+\tgoto end
+)
+
+goto end
+
+:mkdir %%1 %%2
+\tIF NOT EXIST %%1 mkdir %%1
+\tIF NOT EXIST %%2 mkdir %%2
+\texit /b
+
+:end
+'''
+
def mkdir_p(dir):
if path.isdir(dir):
@@ -410,12 +513,18 @@ Either, you use a directory ".build" within the root path, or you separate
"source" and "build" directories within the root path.'''
do_prompt(d, 'sep', 'Separate source and build directories (y/N)', 'n',
boolean)
- print '''
+ if os.name == 'nt':
+ print '''
+Inside the root directory, two more directories will be created; "_templates"
+for custom HTML templates and "_static" for custom stylesheets and other
+static files. You can enter another prefix (such as ".") to replace the underscore.'''
+ do_prompt(d, 'dot', 'Name prefix for templates and static dir', '_', ok)
+ else:
+ print '''
Inside the root directory, two more directories will be created; ".templates"
for custom HTML templates and ".static" for custom stylesheets and other
-static files. Since the leading dot may be inconvenient for Windows users,
-you can enter another prefix (such as "_") to replace the dot.'''
- do_prompt(d, 'dot', 'Name prefix for templates and static dir', '.', ok)
+static files. You can enter another prefix (such as "_") to replace the dot.'''
+ do_prompt(d, 'dot', 'Name prefix for templates and static dir', '.', ok)
print '''
The project name will occur in several places in the built documentation.'''
@@ -454,6 +563,8 @@ only have to run e.g. `make html' instead of invoking sphinx-build
directly.'''
do_prompt(d, 'makefile', 'Create Makefile? (Y/n)',
os.name == 'posix' and 'y' or 'n', boolean)
+ do_prompt(d, 'batchfile', 'Create Windows command file? (Y/n)',
+ os.name == 'nt' and 'y' or 'n', boolean)
d['project_fn'] = make_filename(d['project'])
d['now'] = time.asctime()
@@ -505,12 +616,21 @@ directly.'''
f.write((MAKEFILE % d).encode('utf-8'))
f.close()
+ create_batch = d['batchfile'].upper() in ('Y', 'YES')
+ if create_batch:
+ d['rsrcdir'] = separate and 'source' or '.'
+ d['rbuilddir'] = separate and 'build' or d['dot'] + 'build'
+ f = open(path.join(d['path'], 'make.bat'), 'w')
+ f.write((BATCHFILE % d).encode('utf-8'))
+ f.close()
+
+
print
print bold('Finished: An initial directory structure has been created.')
print '''
You should now populate your master file %s and create other documentation
source files. Use the sphinx-build script to build the docs, like so:
-''' % masterfile + (create_makefile and '''
+''' % masterfile + ((create_makefile or create_batch) and '''
make
''' or '''
sphinx-build -b %s %s
--
cgit v1.2.1
From 61f7e12faeca94a847aa32084bc4f1933d32c89a Mon Sep 17 00:00:00 2001
From: Sebastian Wiesner
Date: Thu, 27 Nov 2008 00:31:43 +0100
Subject: Fixed markup escaping issue
---
sphinx/templates/layout.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/templates/layout.html b/sphinx/templates/layout.html
index eb691455..f2314f41 100644
--- a/sphinx/templates/layout.html
+++ b/sphinx/templates/layout.html
@@ -89,7 +89,7 @@
{{ metatags }}
{%- if builder != 'htmlhelp' %}
- {%- set titlesuffix = " — " + docstitle|e %}
+ {%- set titlesuffix = " — "|safe + docstitle|e %}
{%- endif %}
{{ title|striptags }}{{ titlesuffix }}
{%- if builder == 'web' %}
--
cgit v1.2.1
From 10c994f344a0ecee8b43e94d0190b95d8d1d4aea Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sat, 29 Nov 2008 19:56:58 +0100
Subject: Move builders and writers into new packages.
---
sphinx/__init__.py | 3 +
sphinx/_jinja.py | 3 +-
sphinx/application.py | 24 +-
sphinx/builder.py | 1274 +-----------------------------------------
sphinx/builders/__init__.py | 328 +++++++++++
sphinx/builders/changes.py | 137 +++++
sphinx/builders/html.py | 607 ++++++++++++++++++++
sphinx/builders/htmlhelp.py | 245 ++++++++
sphinx/builders/latex.py | 185 ++++++
sphinx/builders/linkcheck.py | 130 +++++
sphinx/builders/text.py | 68 +++
sphinx/ext/intersphinx.py | 2 +-
sphinx/htmlhelp.py | 220 --------
sphinx/htmlwriter.py | 457 ---------------
sphinx/latexwriter.py | 1185 ---------------------------------------
sphinx/linkcheck.py | 130 -----
sphinx/textwriter.py | 679 ----------------------
sphinx/writers/__init__.py | 10 +
sphinx/writers/html.py | 457 +++++++++++++++
sphinx/writers/latex.py | 1185 +++++++++++++++++++++++++++++++++++++++
sphinx/writers/text.py | 679 ++++++++++++++++++++++
tests/test_build.py | 2 +-
tests/test_markup.py | 4 +-
23 files changed, 4070 insertions(+), 3944 deletions(-)
create mode 100644 sphinx/builders/__init__.py
create mode 100644 sphinx/builders/changes.py
create mode 100644 sphinx/builders/html.py
create mode 100644 sphinx/builders/htmlhelp.py
create mode 100644 sphinx/builders/latex.py
create mode 100644 sphinx/builders/linkcheck.py
create mode 100644 sphinx/builders/text.py
delete mode 100644 sphinx/htmlhelp.py
delete mode 100644 sphinx/htmlwriter.py
delete mode 100644 sphinx/latexwriter.py
delete mode 100644 sphinx/linkcheck.py
delete mode 100644 sphinx/textwriter.py
create mode 100644 sphinx/writers/__init__.py
create mode 100644 sphinx/writers/html.py
create mode 100644 sphinx/writers/latex.py
create mode 100644 sphinx/writers/text.py
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index aa4398e0..2df41707 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -10,11 +10,14 @@
"""
import sys
+from os import path
__revision__ = '$Revision$'
__version__ = '0.5'
__released__ = '0.5'
+package_dir = path.abspath(path.dirname(__file__))
+
def main(argv=sys.argv):
if sys.version_info[:3] < (2, 4, 0):
diff --git a/sphinx/_jinja.py b/sphinx/_jinja.py
index d6e98b21..654e0c52 100644
--- a/sphinx/_jinja.py
+++ b/sphinx/_jinja.py
@@ -12,6 +12,7 @@
import codecs
from os import path
+from sphinx import package_dir
from sphinx.util import mtimes_of_files
from sphinx.application import TemplateBridge
@@ -88,7 +89,7 @@ class TranslatorEnvironment(Environment):
class BuiltinTemplates(TemplateBridge):
def init(self, builder):
self.templates = {}
- base_templates_path = path.join(path.dirname(__file__), 'templates')
+ base_templates_path = path.join(package_dir, 'templates')
ext_templates_path = [path.join(builder.confdir, dir)
for dir in builder.config.templates_path]
self.templates_path = [base_templates_path] + ext_templates_path
diff --git a/sphinx/application.py b/sphinx/application.py
index 888d7567..6c644bbe 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -22,7 +22,7 @@ from docutils.parsers.rst import directives, roles
import sphinx
from sphinx.roles import xfileref_role, innernodetypes
from sphinx.config import Config
-from sphinx.builder import builtin_builders, StandaloneHTMLBuilder
+from sphinx.builders import BUILTIN_BUILDERS
from sphinx.directives import desc_directive, target_directive, additional_xref_types
from sphinx.environment import SphinxStandaloneReader
from sphinx.util.console import bold
@@ -77,7 +77,7 @@ class Sphinx(object):
confoverrides, status, warning=sys.stderr, freshenv=False):
self.next_listener_id = 0
self._listeners = {}
- self.builderclasses = builtin_builders.copy()
+ self.builderclasses = BUILTIN_BUILDERS.copy()
self.builder = None
self.srcdir = srcdir
@@ -125,6 +125,11 @@ class Sphinx(object):
buildername)))
builderclass = self.builderclasses[buildername]
+ if isinstance(builderclass, tuple):
+ # builtin builder
+ mod, cls = builderclass
+ builderclass = getattr(
+ __import__('sphinx.builders.' + mod, None, None, [cls]), cls)
self.builder = builderclass(self, freshenv=freshenv)
self.emit('builder-inited')
@@ -220,8 +225,12 @@ class Sphinx(object):
if not hasattr(builder, 'name'):
raise ExtensionError('Builder class %s has no "name" attribute' % builder)
if builder.name in self.builderclasses:
- raise ExtensionError('Builder %r already exists (in module %s)' % (
- builder.name, self.builderclasses[builder.name].__module__))
+ if isinstance(self.builderclasses[builder.name], tuple):
+ raise ExtensionError('Builder %r is a builtin builder' %
+ builder.name)
+ else:
+ raise ExtensionError('Builder %r already exists (in module %s)' % (
+ builder.name, self.builderclasses[builder.name].__module__))
self.builderclasses[builder.name] = builder
def add_config_value(self, name, default, rebuild_env):
@@ -243,11 +252,11 @@ class Sphinx(object):
raise ExtensionError('Value for key %r must be a (visit, depart) '
'function tuple' % key)
if key == 'html':
- from sphinx.htmlwriter import HTMLTranslator as translator
+ from sphinx.writers.html import HTMLTranslator as translator
elif key == 'latex':
- from sphinx.latexwriter import LaTeXTranslator as translator
+ from sphinx.writers.latex import LaTeXTranslator as translator
elif key == 'text':
- from sphinx.textwriter import TextTranslator as translator
+ from sphinx.writers.text import TextTranslator as translator
else:
# ignore invalid keys for compatibility
continue
@@ -284,6 +293,7 @@ class Sphinx(object):
SphinxStandaloneReader.transforms.append(transform)
def add_javascript(self, filename):
+ from sphinx.builders.html import StandaloneHTMLBuilder
StandaloneHTMLBuilder.script_files.append(
posixpath.join('_static', filename))
diff --git a/sphinx/builder.py b/sphinx/builder.py
index 159fe803..2a19b751 100644
--- a/sphinx/builder.py
+++ b/sphinx/builder.py
@@ -3,1268 +3,20 @@
sphinx.builder
~~~~~~~~~~~~~~
- Builder classes for different output formats.
+ .. warning::
- :copyright: 2007-2008 by Georg Brandl, Sebastian Wiesner, Horst Gutmann.
+ This module is only kept for API compatibility; new code should
+ import these classes directly from the sphinx.builders package.
+
+ :copyright: 2008 by Georg Brandl.
:license: BSD.
"""
-import os
-import time
-import codecs
-import shutil
-import gettext
-import cPickle as pickle
-from os import path
-from cgi import escape
-
-from docutils import nodes
-from docutils.io import StringOutput, FileOutput, DocTreeInput
-from docutils.core import publish_parts
-from docutils.utils import new_document
-from docutils.frontend import OptionParser
-from docutils.readers.doctree import Reader as DoctreeReader
-
-from sphinx import addnodes, locale, __version__
-from sphinx.util import ensuredir, relative_uri, SEP, os_path, texescape, ustrftime
-from sphinx.htmlhelp import build_hhx
-from sphinx.htmlwriter import HTMLWriter, HTMLTranslator, SmartyPantsHTMLTranslator
-from sphinx.textwriter import TextWriter
-from sphinx.latexwriter import LaTeXWriter
-from sphinx.environment import BuildEnvironment, NoUri
-from sphinx.highlighting import PygmentsBridge
-from sphinx.util.console import bold, purple, darkgreen
-from sphinx.search import js_index
-
-try:
- import json
-except ImportError:
- try:
- import simplejson as json
- except ImportError:
- json = None
-
-# side effect: registers roles and directives
-from sphinx import roles
-from sphinx import directives
-
-ENV_PICKLE_FILENAME = 'environment.pickle'
-LAST_BUILD_FILENAME = 'last_build'
-INVENTORY_FILENAME = 'objects.inv'
-
-
-class Builder(object):
- """
- Builds target formats from the reST sources.
- """
-
- # builder's name, for the -b command line options
- name = ''
-
- def __init__(self, app, env=None, freshenv=False):
- self.srcdir = app.srcdir
- self.confdir = app.confdir
- self.outdir = app.outdir
- self.doctreedir = app.doctreedir
- if not path.isdir(self.doctreedir):
- os.makedirs(self.doctreedir)
-
- self.app = app
- self.warn = app.warn
- self.info = app.info
- self.config = app.config
-
- self.load_i18n()
-
- # images that need to be copied over (source -> dest)
- self.images = {}
-
- # if None, this is set in load_env()
- self.env = env
- self.freshenv = freshenv
-
- self.init()
- self.load_env()
-
- # helper methods
-
- def init(self):
- """Load necessary templates and perform initialization."""
- raise NotImplementedError
-
- def init_templates(self):
- # Call this from init() if you need templates.
- if self.config.template_bridge:
- self.templates = self.app.import_object(
- self.config.template_bridge, 'template_bridge setting')()
- else:
- from sphinx._jinja import BuiltinTemplates
- self.templates = BuiltinTemplates()
- self.templates.init(self)
-
- def get_target_uri(self, docname, typ=None):
- """
- Return the target URI for a document name (typ can be used to qualify
- the link characteristic for individual builders).
- """
- raise NotImplementedError
-
- def get_relative_uri(self, from_, to, typ=None):
- """
- Return a relative URI between two source filenames. May raise environment.NoUri
- if there's no way to return a sensible URI.
- """
- return relative_uri(self.get_target_uri(from_),
- self.get_target_uri(to, typ))
-
- def get_outdated_docs(self):
- """
- Return an iterable of output files that are outdated, or a string describing
- what an update build will build.
- """
- raise NotImplementedError
-
- def status_iterator(self, iterable, summary, colorfunc=darkgreen):
- l = -1
- for item in iterable:
- if l == -1:
- self.info(bold(summary), nonl=1)
- l = 0
- self.info(colorfunc(item) + ' ', nonl=1)
- yield item
- if l == 0:
- self.info()
-
- supported_image_types = []
-
- def post_process_images(self, doctree):
- """
- Pick the best candidate for all image URIs.
- """
- for node in doctree.traverse(nodes.image):
- if '?' in node['candidates']:
- # don't rewrite nonlocal image URIs
- continue
- if '*' not in node['candidates']:
- for imgtype in self.supported_image_types:
- candidate = node['candidates'].get(imgtype, None)
- if candidate:
- break
- else:
- self.warn('%s:%s: no matching candidate for image URI %r' %
- (node.source, getattr(node, 'lineno', ''), node['uri']))
- continue
- node['uri'] = candidate
- else:
- candidate = node['uri']
- if candidate not in self.env.images:
- # non-existing URI; let it alone
- continue
- self.images[candidate] = self.env.images[candidate][1]
-
- # build methods
-
- def load_i18n(self):
- """
- Load translated strings from the configured localedirs if
- enabled in the configuration.
- """
- self.translator = None
- if self.config.language is not None:
- self.info(bold('loading translations [%s]... ' % self.config.language),
- nonl=True)
- locale_dirs = [path.join(path.dirname(__file__), 'locale')] + \
- [path.join(self.srcdir, x) for x in self.config.locale_dirs]
- for dir_ in locale_dirs:
- try:
- trans = gettext.translation('sphinx', localedir=dir_,
- languages=[self.config.language])
- if self.translator is None:
- self.translator = trans
- else:
- self.translator._catalog.update(trans.catalog)
- except Exception:
- # Language couldn't be found in the specified path
- pass
- if self.translator is not None:
- self.info('done')
- else:
- self.info('locale not available')
- if self.translator is None:
- self.translator = gettext.NullTranslations()
- self.translator.install(unicode=True)
- locale.init() # translate common labels
-
- def load_env(self):
- """Set up the build environment."""
- if self.env:
- return
- if not self.freshenv:
- try:
- self.info(bold('loading pickled environment... '), nonl=True)
- self.env = BuildEnvironment.frompickle(self.config,
- path.join(self.doctreedir, ENV_PICKLE_FILENAME))
- self.info('done')
- except Exception, err:
- if type(err) is IOError and err.errno == 2:
- self.info('not found')
- else:
- self.info('failed: %s' % err)
- self.env = BuildEnvironment(self.srcdir, self.doctreedir, self.config)
- self.env.find_files(self.config)
- else:
- self.env = BuildEnvironment(self.srcdir, self.doctreedir, self.config)
- self.env.find_files(self.config)
- self.env.set_warnfunc(self.warn)
-
- def build_all(self):
- """Build all source files."""
- self.build(None, summary='all source files', method='all')
-
- def build_specific(self, filenames):
- """Only rebuild as much as needed for changes in the source_filenames."""
- # bring the filenames to the canonical format, that is,
- # relative to the source directory and without source_suffix.
- dirlen = len(self.srcdir) + 1
- to_write = []
- suffix = self.config.source_suffix
- for filename in filenames:
- filename = path.abspath(filename)[dirlen:]
- if filename.endswith(suffix):
- filename = filename[:-len(suffix)]
- filename = filename.replace(os.path.sep, SEP)
- to_write.append(filename)
- self.build(to_write, method='specific',
- summary='%d source files given on command '
- 'line' % len(to_write))
-
- def build_update(self):
- """Only rebuild files changed or added since last build."""
- to_build = self.get_outdated_docs()
- if isinstance(to_build, str):
- self.build(['__all__'], to_build)
- else:
- to_build = list(to_build)
- self.build(to_build,
- summary='targets for %d source files that are '
- 'out of date' % len(to_build))
-
- def build(self, docnames, summary=None, method='update'):
- if summary:
- self.info(bold('building [%s]: ' % self.name), nonl=1)
- self.info(summary)
-
- updated_docnames = []
- # while reading, collect all warnings from docutils
- warnings = []
- self.env.set_warnfunc(warnings.append)
- self.info(bold('updating environment: '), nonl=1)
- iterator = self.env.update(self.config, self.srcdir, self.doctreedir, self.app)
- # the first item in the iterator is a summary message
- self.info(iterator.next())
- for docname in self.status_iterator(iterator, 'reading sources... ', purple):
- updated_docnames.append(docname)
- # nothing further to do, the environment has already done the reading
- for warning in warnings:
- if warning.strip():
- self.warn(warning)
- self.env.set_warnfunc(self.warn)
-
- if updated_docnames:
- # save the environment
- self.info(bold('pickling environment... '), nonl=True)
- self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME))
- self.info('done')
-
- # global actions
- self.info(bold('checking consistency... '), nonl=True)
- self.env.check_consistency()
- self.info('done')
- else:
- if method == 'update' and not docnames:
- self.info(bold('no targets are out of date.'))
- return
-
- # another indirection to support methods which don't build files
- # individually
- self.write(docnames, updated_docnames, method)
-
- # finish (write static files etc.)
- self.finish()
- if self.app._warncount:
- self.info(bold('build succeeded, %s warning%s.' %
- (self.app._warncount,
- self.app._warncount != 1 and 's' or '')))
- else:
- self.info(bold('build succeeded.'))
-
- def write(self, build_docnames, updated_docnames, method='update'):
- if build_docnames is None or build_docnames == ['__all__']:
- # build_all
- build_docnames = self.env.found_docs
- if method == 'update':
- # build updated ones as well
- docnames = set(build_docnames) | set(updated_docnames)
- else:
- docnames = set(build_docnames)
-
- # add all toctree-containing files that may have changed
- for docname in list(docnames):
- for tocdocname in self.env.files_to_rebuild.get(docname, []):
- docnames.add(tocdocname)
- docnames.add(self.config.master_doc)
-
- self.info(bold('preparing documents... '), nonl=True)
- self.prepare_writing(docnames)
- self.info('done')
-
- # write target files
- warnings = []
- self.env.set_warnfunc(warnings.append)
- for docname in self.status_iterator(sorted(docnames),
- 'writing output... ', darkgreen):
- doctree = self.env.get_and_resolve_doctree(docname, self)
- self.write_doc(docname, doctree)
- for warning in warnings:
- if warning.strip():
- self.warn(warning)
- self.env.set_warnfunc(self.warn)
-
- def prepare_writing(self, docnames):
- raise NotImplementedError
-
- def write_doc(self, docname, doctree):
- raise NotImplementedError
-
- def finish(self):
- raise NotImplementedError
-
-
-class StandaloneHTMLBuilder(Builder):
- """
- Builds standalone HTML docs.
- """
- name = 'html'
- copysource = True
- out_suffix = '.html'
- indexer_format = js_index
- supported_image_types = ['image/svg+xml', 'image/png', 'image/gif',
- 'image/jpeg']
- searchindex_filename = 'searchindex.js'
- add_header_links = True
- add_definition_links = True
-
- # This is a class attribute because it is mutated by Sphinx.add_javascript.
- script_files = ['_static/jquery.js', '_static/doctools.js']
-
- def init(self):
- """Load templates."""
- self.init_templates()
- self.init_translator_class()
- if self.config.html_file_suffix:
- self.out_suffix = self.config.html_file_suffix
-
- if self.config.language is not None:
- jsfile = path.join(path.dirname(__file__), 'locale', self.config.language,
- 'LC_MESSAGES', 'sphinx.js')
- if path.isfile(jsfile):
- self.script_files.append('_static/translations.js')
-
- def init_translator_class(self):
- if self.config.html_translator_class:
- self.translator_class = self.app.import_object(
- self.config.html_translator_class, 'html_translator_class setting')
- elif self.config.html_use_smartypants:
- self.translator_class = SmartyPantsHTMLTranslator
- else:
- self.translator_class = HTMLTranslator
-
- def render_partial(self, node):
- """Utility: Render a lone doctree node."""
- doc = new_document('')
- doc.append(node)
- return publish_parts(
- doc,
- source_class=DocTreeInput,
- reader=DoctreeReader(),
- writer=HTMLWriter(self),
- settings_overrides={'output_encoding': 'unicode'}
- )
-
- def prepare_writing(self, docnames):
- from sphinx.search import IndexBuilder
-
- self.indexer = IndexBuilder(self.env)
- self.load_indexer(docnames)
- self.docwriter = HTMLWriter(self)
- self.docsettings = OptionParser(
- defaults=self.env.settings,
- components=(self.docwriter,)).get_default_values()
-
- # format the "last updated on" string, only once is enough since it
- # typically doesn't include the time of day
- lufmt = self.config.html_last_updated_fmt
- if lufmt is not None:
- self.last_updated = ustrftime(lufmt or _('%b %d, %Y'))
- else:
- self.last_updated = None
-
- logo = self.config.html_logo and \
- path.basename(self.config.html_logo) or ''
-
- favicon = self.config.html_favicon and \
- path.basename(self.config.html_favicon) or ''
- if favicon and os.path.splitext(favicon)[1] != '.ico':
- self.warn('html_favicon is not an .ico file')
-
- if not isinstance(self.config.html_use_opensearch, basestring):
- self.warn('html_use_opensearch config value must now be a string')
-
- self.relations = self.env.collect_relations()
-
- rellinks = []
- if self.config.html_use_index:
- rellinks.append(('genindex', _('General Index'), 'I', _('index')))
- if self.config.html_use_modindex and self.env.modules:
- rellinks.append(('modindex', _('Global Module Index'), 'M', _('modules')))
-
- self.globalcontext = dict(
- project = self.config.project,
- release = self.config.release,
- version = self.config.version,
- last_updated = self.last_updated,
- copyright = self.config.copyright,
- master_doc = self.config.master_doc,
- style = self.config.html_style,
- use_opensearch = self.config.html_use_opensearch,
- docstitle = self.config.html_title,
- shorttitle = self.config.html_short_title,
- show_sphinx = self.config.html_show_sphinx,
- file_suffix = self.out_suffix,
- script_files = self.script_files,
- sphinx_version = __version__,
- rellinks = rellinks,
- builder = self.name,
- parents = [],
- logo = logo,
- favicon = favicon,
- )
- self.globalcontext.update(self.config.html_context)
-
- def get_doc_context(self, docname, body, metatags):
- """Collect items for the template context of a page."""
- # find out relations
- prev = next = None
- parents = []
- rellinks = self.globalcontext['rellinks'][:]
- related = self.relations.get(docname)
- titles = self.env.titles
- if related and related[2]:
- try:
- next = {'link': self.get_relative_uri(docname, related[2]),
- 'title': self.render_partial(titles[related[2]])['title']}
- rellinks.append((related[2], next['title'], 'N', _('next')))
- except KeyError:
- next = None
- if related and related[1]:
- try:
- prev = {'link': self.get_relative_uri(docname, related[1]),
- 'title': self.render_partial(titles[related[1]])['title']}
- rellinks.append((related[1], prev['title'], 'P', _('previous')))
- except KeyError:
- # the relation is (somehow) not in the TOC tree, handle that gracefully
- prev = None
- while related and related[0]:
- try:
- parents.append(
- {'link': self.get_relative_uri(docname, related[0]),
- 'title': self.render_partial(titles[related[0]])['title']})
- except KeyError:
- pass
- related = self.relations.get(related[0])
- if parents:
- parents.pop() # remove link to the master file; we have a generic
- # "back to index" link already
- parents.reverse()
-
- # title rendered as HTML
- title = titles.get(docname)
- title = title and self.render_partial(title)['title'] or ''
- # the name for the copied source
- sourcename = self.config.html_copy_source and docname + '.txt' or ''
-
- # metadata for the document
- meta = self.env.metadata.get(docname)
-
- return dict(
- parents = parents,
- prev = prev,
- next = next,
- title = title,
- meta = meta,
- body = body,
- metatags = metatags,
- rellinks = rellinks,
- sourcename = sourcename,
- toc = self.render_partial(self.env.get_toc_for(docname))['fragment'],
- # only display a TOC if there's more than one item to show
- display_toc = (self.env.toc_num_entries[docname] > 1),
- )
-
- def write_doc(self, docname, doctree):
- self.post_process_images(doctree)
- destination = StringOutput(encoding='utf-8')
- doctree.settings = self.docsettings
-
- self.imgpath = relative_uri(self.get_target_uri(docname), '_images')
- self.docwriter.write(doctree, destination)
- self.docwriter.assemble_parts()
- body = self.docwriter.parts['fragment']
- metatags = self.docwriter.clean_meta
-
- ctx = self.get_doc_context(docname, body, metatags)
- self.index_page(docname, doctree, ctx.get('title', ''))
- self.handle_page(docname, ctx, event_arg=doctree)
-
- def finish(self):
- self.info(bold('writing additional files...'), nonl=1)
-
- # the global general index
-
- if self.config.html_use_index:
- # the total count of lines for each index letter, used to distribute
- # the entries into two columns
- genindex = self.env.create_index(self)
- indexcounts = []
- for _, entries in genindex:
- indexcounts.append(sum(1 + len(subitems)
- for _, (_, subitems) in entries))
-
- genindexcontext = dict(
- genindexentries = genindex,
- genindexcounts = indexcounts,
- split_index = self.config.html_split_index,
- )
- self.info(' genindex', nonl=1)
-
- if self.config.html_split_index:
- self.handle_page('genindex', genindexcontext, 'genindex-split.html')
- self.handle_page('genindex-all', genindexcontext, 'genindex.html')
- for (key, entries), count in zip(genindex, indexcounts):
- ctx = {'key': key, 'entries': entries, 'count': count,
- 'genindexentries': genindex}
- self.handle_page('genindex-' + key, ctx, 'genindex-single.html')
- else:
- self.handle_page('genindex', genindexcontext, 'genindex.html')
-
- # the global module index
-
- if self.config.html_use_modindex and self.env.modules:
- # the sorted list of all modules, for the global module index
- modules = sorted(((mn, (self.get_relative_uri('modindex', fn) +
- '#module-' + mn, sy, pl, dep))
- for (mn, (fn, sy, pl, dep)) in
- self.env.modules.iteritems()),
- key=lambda x: x[0].lower())
- # collect all platforms
- platforms = set()
- # sort out collapsable modules
- modindexentries = []
- letters = []
- pmn = ''
- num_toplevels = 0
- num_collapsables = 0
- cg = 0 # collapse group
- fl = '' # first letter
- for mn, (fn, sy, pl, dep) in modules:
- pl = pl and pl.split(', ') or []
- platforms.update(pl)
- if fl != mn[0].lower() and mn[0] != '_':
- # heading
- modindexentries.append(['', False, 0, False,
- mn[0].upper(), '', [], False])
- letters.append(mn[0].upper())
- tn = mn.split('.')[0]
- if tn != mn:
- # submodule
- if pmn == tn:
- # first submodule - make parent collapsable
- modindexentries[-1][1] = True
- num_collapsables += 1
- elif not pmn.startswith(tn):
- # submodule without parent in list, add dummy entry
- cg += 1
- modindexentries.append([tn, True, cg, False, '', '', [], False])
- else:
- num_toplevels += 1
- cg += 1
- modindexentries.append([mn, False, cg, (tn != mn), fn, sy, pl, dep])
- pmn = mn
- fl = mn[0].lower()
- platforms = sorted(platforms)
-
- # apply heuristics when to collapse modindex at page load:
- # only collapse if number of toplevel modules is larger than
- # number of submodules
- collapse = len(modules) - num_toplevels < num_toplevels
-
- modindexcontext = dict(
- modindexentries = modindexentries,
- platforms = platforms,
- letters = letters,
- collapse_modindex = collapse,
- )
- self.info(' modindex', nonl=1)
- self.handle_page('modindex', modindexcontext, 'modindex.html')
-
- # the search page
- if self.name != 'htmlhelp':
- self.info(' search', nonl=1)
- self.handle_page('search', {}, 'search.html')
-
- # additional pages from conf.py
- for pagename, template in self.config.html_additional_pages.items():
- self.info(' '+pagename, nonl=1)
- self.handle_page(pagename, {}, template)
-
- if self.config.html_use_opensearch and self.name != 'htmlhelp':
- self.info(' opensearch', nonl=1)
- fn = path.join(self.outdir, '_static', 'opensearch.xml')
- self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn)
-
- self.info()
-
- # copy image files
- if self.images:
- self.info(bold('copying images...'), nonl=True)
- ensuredir(path.join(self.outdir, '_images'))
- for src, dest in self.images.iteritems():
- self.info(' '+src, nonl=1)
- shutil.copyfile(path.join(self.srcdir, src),
- path.join(self.outdir, '_images', dest))
- self.info()
-
- # copy static files
- self.info(bold('copying static files... '), nonl=True)
- ensuredir(path.join(self.outdir, '_static'))
- # first, create pygments style file
- f = open(path.join(self.outdir, '_static', 'pygments.css'), 'w')
- f.write(PygmentsBridge('html', self.config.pygments_style).get_stylesheet())
- f.close()
- # then, copy translations JavaScript file
- if self.config.language is not None:
- jsfile = path.join(path.dirname(__file__), 'locale', self.config.language,
- 'LC_MESSAGES', 'sphinx.js')
- if path.isfile(jsfile):
- shutil.copyfile(jsfile, path.join(self.outdir, '_static',
- 'translations.js'))
- # then, copy over all user-supplied static files
- staticdirnames = [path.join(path.dirname(__file__), 'static')] + \
- [path.join(self.confdir, spath)
- for spath in self.config.html_static_path]
- for staticdirname in staticdirnames:
- for filename in os.listdir(staticdirname):
- if filename.startswith('.'):
- continue
- fullname = path.join(staticdirname, filename)
- targetname = path.join(self.outdir, '_static', filename)
- if path.isfile(fullname):
- shutil.copyfile(fullname, targetname)
- elif path.isdir(fullname):
- if filename in self.config.exclude_dirnames:
- continue
- if path.exists(targetname):
- shutil.rmtree(targetname)
- shutil.copytree(fullname, targetname)
- # last, copy logo file (handled differently)
- if self.config.html_logo:
- logobase = path.basename(self.config.html_logo)
- shutil.copyfile(path.join(self.confdir, self.config.html_logo),
- path.join(self.outdir, '_static', logobase))
- self.info('done')
-
- # dump the search index
- self.handle_finish()
-
- def get_outdated_docs(self):
- if self.templates:
- template_mtime = self.templates.newest_template_mtime()
- else:
- template_mtime = 0
- for docname in self.env.found_docs:
- if docname not in self.env.all_docs:
- yield docname
- continue
- targetname = self.env.doc2path(docname, self.outdir, self.out_suffix)
- try:
- targetmtime = path.getmtime(targetname)
- except Exception:
- targetmtime = 0
- try:
- srcmtime = max(path.getmtime(self.env.doc2path(docname)),
- template_mtime)
- if srcmtime > targetmtime:
- yield docname
- except EnvironmentError:
- # source doesn't exist anymore
- pass
-
- def load_indexer(self, docnames):
- keep = set(self.env.all_docs) - set(docnames)
- try:
- f = open(path.join(self.outdir, self.searchindex_filename), 'rb')
- try:
- self.indexer.load(f, self.indexer_format)
- finally:
- f.close()
- except (IOError, OSError, ValueError):
- if keep:
- self.warn("search index couldn't be loaded, but not all documents "
- "will be built: the index will be incomplete.")
- # delete all entries for files that will be rebuilt
- self.indexer.prune(keep)
-
- def index_page(self, pagename, doctree, title):
- # only index pages with title
- if self.indexer is not None and title:
- self.indexer.feed(pagename, title, doctree)
-
- # --------- these are overwritten by the serialization builder
-
- def get_target_uri(self, docname, typ=None):
- return docname + self.out_suffix
-
- def handle_page(self, pagename, addctx, templatename='page.html',
- outfilename=None, event_arg=None):
- ctx = self.globalcontext.copy()
- # current_page_name is backwards compatibility
- ctx['pagename'] = ctx['current_page_name'] = pagename
-
- def pathto(otheruri, resource=False,
- baseuri=self.get_target_uri(pagename)):
- if not resource:
- otheruri = self.get_target_uri(otheruri)
- return relative_uri(baseuri, otheruri)
- ctx['pathto'] = pathto
- ctx['hasdoc'] = lambda name: name in self.env.all_docs
- ctx['customsidebar'] = self.config.html_sidebars.get(pagename)
- ctx.update(addctx)
-
- self.app.emit('html-page-context', pagename, templatename, ctx, event_arg)
-
- output = self.templates.render(templatename, ctx)
- if not outfilename:
- outfilename = path.join(self.outdir, os_path(pagename) + self.out_suffix)
- ensuredir(path.dirname(outfilename)) # normally different from self.outdir
- try:
- f = codecs.open(outfilename, 'w', 'utf-8')
- try:
- f.write(output)
- finally:
- f.close()
- except (IOError, OSError), err:
- self.warn("Error writing file %s: %s" % (outfilename, err))
- if self.copysource and ctx.get('sourcename'):
- # copy the source file for the "show source" link
- source_name = path.join(self.outdir, '_sources', os_path(ctx['sourcename']))
- ensuredir(path.dirname(source_name))
- shutil.copyfile(self.env.doc2path(pagename), source_name)
-
- def handle_finish(self):
- self.info(bold('dumping search index... '), nonl=True)
- self.indexer.prune(self.env.all_docs)
- f = open(path.join(self.outdir, self.searchindex_filename), 'wb')
- try:
- self.indexer.dump(f, self.indexer_format)
- finally:
- f.close()
- self.info('done')
-
- self.info(bold('dumping object inventory... '), nonl=True)
- f = open(path.join(self.outdir, INVENTORY_FILENAME), 'w')
- try:
- f.write('# Sphinx inventory version 1\n')
- f.write('# Project: %s\n' % self.config.project.encode('utf-8'))
- f.write('# Version: %s\n' % self.config.version)
- for modname, info in self.env.modules.iteritems():
- f.write('%s mod %s\n' % (modname, self.get_target_uri(info[0])))
- for refname, (docname, desctype) in self.env.descrefs.iteritems():
- f.write('%s %s %s\n' % (refname, desctype, self.get_target_uri(docname)))
- finally:
- f.close()
- self.info('done')
-
-
-class SerializingHTMLBuilder(StandaloneHTMLBuilder):
- """
- An abstract builder that serializes the HTML generated.
- """
- #: the serializing implementation to use. Set this to a module that
- #: implements a `dump`, `load`, `dumps` and `loads` functions
- #: (pickle, simplejson etc.)
- implementation = None
-
- #: the filename for the global context file
- globalcontext_filename = None
-
- supported_image_types = ('image/svg+xml', 'image/png', 'image/gif',
- 'image/jpeg')
-
- def init(self):
- self.init_translator_class()
- self.templates = None # no template bridge necessary
-
- def get_target_uri(self, docname, typ=None):
- if docname == 'index':
- return ''
- if docname.endswith(SEP + 'index'):
- return docname[:-5] # up to sep
- return docname + SEP
-
- def handle_page(self, pagename, ctx, templatename='page.html',
- outfilename=None, event_arg=None):
- ctx['current_page_name'] = pagename
- sidebarfile = self.config.html_sidebars.get(pagename)
- if sidebarfile:
- ctx['customsidebar'] = sidebarfile
-
- if not outfilename:
- outfilename = path.join(self.outdir, os_path(pagename) + self.out_suffix)
-
- self.app.emit('html-page-context', pagename, templatename, ctx, event_arg)
-
- ensuredir(path.dirname(outfilename))
- f = open(outfilename, 'wb')
- try:
- self.implementation.dump(ctx, f, 2)
- finally:
- f.close()
-
- # if there is a source file, copy the source file for the
- # "show source" link
- if ctx.get('sourcename'):
- source_name = path.join(self.outdir, '_sources',
- os_path(ctx['sourcename']))
- ensuredir(path.dirname(source_name))
- shutil.copyfile(self.env.doc2path(pagename), source_name)
-
- def handle_finish(self):
- # dump the global context
- outfilename = path.join(self.outdir, self.globalcontext_filename)
- f = open(outfilename, 'wb')
- try:
- self.implementation.dump(self.globalcontext, f, 2)
- finally:
- f.close()
-
- # super here to dump the search index
- StandaloneHTMLBuilder.handle_finish(self)
-
- # copy the environment file from the doctree dir to the output dir
- # as needed by the web app
- shutil.copyfile(path.join(self.doctreedir, ENV_PICKLE_FILENAME),
- path.join(self.outdir, ENV_PICKLE_FILENAME))
-
- # touch 'last build' file, used by the web application to determine
- # when to reload its environment and clear the cache
- open(path.join(self.outdir, LAST_BUILD_FILENAME), 'w').close()
-
-
-class PickleHTMLBuilder(SerializingHTMLBuilder):
- """
- A Builder that dumps the generated HTML into pickle files.
- """
- implementation = pickle
- indexer_format = pickle
- name = 'pickle'
- out_suffix = '.fpickle'
- globalcontext_filename = 'globalcontext.pickle'
- searchindex_filename = 'searchindex.pickle'
-
-
-class JSONHTMLBuilder(SerializingHTMLBuilder):
- """
- A builder that dumps the generated HTML into JSON files.
- """
- implementation = json
- indexer_format = json
- name = 'json'
- out_suffix = '.fjson'
- globalcontext_filename = 'globalcontext.json'
- searchindex_filename = 'searchindex.json'
-
- def init(self):
- if json is None:
- from sphinx.application import SphinxError
- raise SphinxError('The module simplejson (or json in Python >= 2.6) '
- 'is not available. The JSONHTMLBuilder builder '
- 'will not work.')
- SerializingHTMLBuilder.init(self)
-
-
-class HTMLHelpBuilder(StandaloneHTMLBuilder):
- """
- Builder that also outputs Windows HTML help project, contents and index files.
- Adapted from the original Doc/tools/prechm.py.
- """
- name = 'htmlhelp'
-
- # don't copy the reST source
- copysource = False
- supported_image_types = ['image/png', 'image/gif', 'image/jpeg']
-
- # don't add links
- add_header_links = False
- add_definition_links = False
-
- def init(self):
- StandaloneHTMLBuilder.init(self)
- # the output files for HTML help must be .html only
- self.out_suffix = '.html'
-
- def handle_finish(self):
- build_hhx(self, self.outdir, self.config.htmlhelp_basename)
-
-
-class LaTeXBuilder(Builder):
- """
- Builds LaTeX output to create PDF.
- """
- name = 'latex'
- supported_image_types = ['application/pdf', 'image/png', 'image/gif',
- 'image/jpeg']
-
- def init(self):
- self.docnames = []
- self.document_data = []
- texescape.init()
-
- def get_outdated_docs(self):
- return 'all documents' # for now
-
- def get_target_uri(self, docname, typ=None):
- if typ == 'token':
- # token references are always inside production lists and must be
- # replaced by \token{} in LaTeX
- return '@token'
- if docname not in self.docnames:
- raise NoUri
- else:
- return ''
-
- def init_document_data(self):
- preliminary_document_data = map(list, self.config.latex_documents)
- if not preliminary_document_data:
- self.warn('No "latex_documents" config value found; no documents '
- 'will be written.')
- return
- # assign subdirs to titles
- self.titles = []
- for entry in preliminary_document_data:
- docname = entry[0]
- if docname not in self.env.all_docs:
- self.warn('"latex_documents" config value references unknown '
- 'document %s' % docname)
- continue
- self.document_data.append(entry)
- if docname.endswith(SEP+'index'):
- docname = docname[:-5]
- self.titles.append((docname, entry[2]))
-
- def write(self, *ignored):
- # first, assemble the "appendix" docs that are in every PDF
- appendices = []
- for fname in self.config.latex_appendices:
- appendices.append(self.env.get_doctree(fname))
-
- docwriter = LaTeXWriter(self)
- docsettings = OptionParser(
- defaults=self.env.settings,
- components=(docwriter,)).get_default_values()
-
- self.init_document_data()
-
- for entry in self.document_data:
- docname, targetname, title, author, docclass = entry[:5]
- toctree_only = False
- if len(entry) > 5:
- toctree_only = entry[5]
- destination = FileOutput(
- destination_path=path.join(self.outdir, targetname),
- encoding='utf-8')
- self.info("processing " + targetname + "... ", nonl=1)
- doctree = self.assemble_doctree(docname, toctree_only,
- appendices=(docclass == 'manual') and appendices or [])
- self.post_process_images(doctree)
- self.info("writing... ", nonl=1)
- doctree.settings = docsettings
- doctree.settings.author = author
- doctree.settings.title = title
- doctree.settings.docname = docname
- doctree.settings.docclass = docclass
- docwriter.write(doctree, destination)
- self.info("done")
-
- def assemble_doctree(self, indexfile, toctree_only, appendices):
- self.docnames = set([indexfile] + appendices)
- self.info(darkgreen(indexfile) + " ", nonl=1)
- def process_tree(docname, tree):
- tree = tree.deepcopy()
- for toctreenode in tree.traverse(addnodes.toctree):
- newnodes = []
- includefiles = map(str, toctreenode['includefiles'])
- for includefile in includefiles:
- try:
- self.info(darkgreen(includefile) + " ", nonl=1)
- subtree = process_tree(includefile,
- self.env.get_doctree(includefile))
- self.docnames.add(includefile)
- except Exception:
- self.warn('%s: toctree contains ref to nonexisting file %r' %
- (docname, includefile))
- else:
- sof = addnodes.start_of_file()
- sof.children = subtree.children
- newnodes.append(sof)
- toctreenode.parent.replace(toctreenode, newnodes)
- return tree
- tree = self.env.get_doctree(indexfile)
- if toctree_only:
- # extract toctree nodes from the tree and put them in a fresh document
- new_tree = new_document('')
- new_sect = nodes.section()
- new_sect += nodes.title(u'', u'')
- new_tree += new_sect
- for node in tree.traverse(addnodes.toctree):
- new_sect += node
- tree = new_tree
- largetree = process_tree(indexfile, tree)
- largetree.extend(appendices)
- self.info()
- self.info("resolving references...")
- self.env.resolve_references(largetree, indexfile, self)
- # resolve :ref:s to distant tex files -- we can't add a cross-reference,
- # but append the document name
- for pendingnode in largetree.traverse(addnodes.pending_xref):
- docname = pendingnode['refdocname']
- sectname = pendingnode['refsectname']
- newnodes = [nodes.emphasis(sectname, sectname)]
- for subdir, title in self.titles:
- if docname.startswith(subdir):
- newnodes.append(nodes.Text(_(' (in '), _(' (in ')))
- newnodes.append(nodes.emphasis(title, title))
- newnodes.append(nodes.Text(')', ')'))
- break
- else:
- pass
- pendingnode.replace_self(newnodes)
- return largetree
-
- def finish(self):
- # copy image files
- if self.images:
- self.info(bold('copying images...'), nonl=1)
- for src, dest in self.images.iteritems():
- self.info(' '+src, nonl=1)
- shutil.copyfile(path.join(self.srcdir, src),
- path.join(self.outdir, dest))
- self.info()
-
- # the logo is handled differently
- if self.config.latex_logo:
- logobase = path.basename(self.config.latex_logo)
- shutil.copyfile(path.join(self.confdir, self.config.latex_logo),
- path.join(self.outdir, logobase))
-
- self.info(bold('copying TeX support files... '), nonl=True)
- staticdirname = path.join(path.dirname(__file__), 'texinputs')
- for filename in os.listdir(staticdirname):
- if not filename.startswith('.'):
- shutil.copyfile(path.join(staticdirname, filename),
- path.join(self.outdir, filename))
- self.info('done')
-
-
-class ChangesBuilder(Builder):
- """
- Write a summary with all versionadded/changed directives.
- """
- name = 'changes'
-
- def init(self):
- self.init_templates()
-
- def get_outdated_docs(self):
- return self.outdir
-
- typemap = {
- 'versionadded': 'added',
- 'versionchanged': 'changed',
- 'deprecated': 'deprecated',
- }
-
- def write(self, *ignored):
- version = self.config.version
- libchanges = {}
- apichanges = []
- otherchanges = {}
- if version not in self.env.versionchanges:
- self.info(bold('no changes in this version.'))
- return
- self.info(bold('writing summary file...'))
- for type, docname, lineno, module, descname, content in \
- self.env.versionchanges[version]:
- ttext = self.typemap[type]
- context = content.replace('\n', ' ')
- if descname and docname.startswith('c-api'):
- if not descname:
- continue
- if context:
- entry = '%s: %s: %s' % (descname, ttext, context)
- else:
- entry = '%s: %s.' % (descname, ttext)
- apichanges.append((entry, docname, lineno))
- elif descname or module:
- if not module:
- module = _('Builtins')
- if not descname:
- descname = _('Module level')
- if context:
- entry = '%s: %s: %s' % (descname, ttext, context)
- else:
- entry = '%s: %s.' % (descname, ttext)
- libchanges.setdefault(module, []).append((entry, docname, lineno))
- else:
- if not context:
- continue
- entry = '%s: %s' % (ttext.capitalize(), context)
- title = self.env.titles[docname].astext()
- otherchanges.setdefault((docname, title), []).append(
- (entry, docname, lineno))
-
- ctx = {
- 'project': self.config.project,
- 'version': version,
- 'docstitle': self.config.html_title,
- 'shorttitle': self.config.html_short_title,
- 'libchanges': sorted(libchanges.iteritems()),
- 'apichanges': sorted(apichanges),
- 'otherchanges': sorted(otherchanges.iteritems()),
- 'show_sphinx': self.config.html_show_sphinx,
- }
- f = open(path.join(self.outdir, 'index.html'), 'w')
- try:
- f.write(self.templates.render('changes/frameset.html', ctx))
- finally:
- f.close()
- f = open(path.join(self.outdir, 'changes.html'), 'w')
- try:
- f.write(self.templates.render('changes/versionchanges.html', ctx))
- finally:
- f.close()
-
- hltext = ['.. versionadded:: %s' % version,
- '.. versionchanged:: %s' % version,
- '.. deprecated:: %s' % version]
-
- def hl(no, line):
- line = '' % no + escape(line)
- for x in hltext:
- if x in line:
- line = '%s' % line
- break
- return line
-
- self.info(bold('copying source files...'))
- for docname in self.env.all_docs:
- f = open(self.env.doc2path(docname))
- lines = f.readlines()
- targetfn = path.join(self.outdir, 'rst', os_path(docname)) + '.html'
- ensuredir(path.dirname(targetfn))
- f = codecs.open(targetfn, 'w', 'utf8')
- try:
- text = ''.join(hl(i+1, line) for (i, line) in enumerate(lines))
- ctx = {'filename': self.env.doc2path(docname, None), 'text': text}
- f.write(self.templates.render('changes/rstsource.html', ctx))
- finally:
- f.close()
- shutil.copyfile(path.join(path.dirname(__file__), 'static', 'default.css'),
- path.join(self.outdir, 'default.css'))
-
- def hl(self, text, version):
- text = escape(text)
- for directive in ['versionchanged', 'versionadded', 'deprecated']:
- text = text.replace('.. %s:: %s' % (directive, version),
- '.. %s:: %s' % (directive, version))
- return text
-
- def finish(self):
- pass
-
-
-class TextBuilder(Builder):
- name = 'text'
- out_suffix = '.txt'
-
- def init(self):
- pass
-
- def get_outdated_docs(self):
- for docname in self.env.found_docs:
- if docname not in self.env.all_docs:
- yield docname
- continue
- targetname = self.env.doc2path(docname, self.outdir, self.out_suffix)
- try:
- targetmtime = path.getmtime(targetname)
- except Exception:
- targetmtime = 0
- try:
- srcmtime = path.getmtime(self.env.doc2path(docname))
- if srcmtime > targetmtime:
- yield docname
- except EnvironmentError:
- # source doesn't exist anymore
- pass
-
- def get_target_uri(self, docname, typ=None):
- return ''
-
- def prepare_writing(self, docnames):
- self.writer = TextWriter(self)
-
- def write_doc(self, docname, doctree):
- destination = StringOutput(encoding='utf-8')
- self.writer.write(doctree, destination)
- outfilename = path.join(self.outdir, os_path(docname) + self.out_suffix)
- ensuredir(path.dirname(outfilename)) # normally different from self.outdir
- try:
- f = codecs.open(outfilename, 'w', 'utf-8')
- try:
- f.write(self.writer.output)
- finally:
- f.close()
- except (IOError, OSError), err:
- self.warn("Error writing file %s: %s" % (outfilename, err))
-
- def finish(self):
- pass
-
-
-# compatibility alias
-WebHTMLBuilder = PickleHTMLBuilder
-
-
-from sphinx.linkcheck import CheckExternalLinksBuilder
-
-builtin_builders = {
- 'html': StandaloneHTMLBuilder,
- 'pickle': PickleHTMLBuilder,
- 'json': JSONHTMLBuilder,
- 'web': PickleHTMLBuilder,
- 'htmlhelp': HTMLHelpBuilder,
- 'latex': LaTeXBuilder,
- 'text': TextBuilder,
- 'changes': ChangesBuilder,
- 'linkcheck': CheckExternalLinksBuilder,
-}
+from sphinx.builders import Builder
+from sphinx.builders.text import TextBuilder
+from sphinx.builders.html import StandaloneHTMLBuilder, WebHTMLBuilder, \
+ PickleHTMLBuilder, JSONHTMLBuilder
+from sphinx.builders.latex import LaTeXBuilder
+from sphinx.builders.changes import ChangesBuilder
+from sphinx.builders.htmlhelp import HTMLHelpBuilder
+from sphinx.builders.linkcheck import CheckExternalLinksBuilder
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
new file mode 100644
index 00000000..09d37de2
--- /dev/null
+++ b/sphinx/builders/__init__.py
@@ -0,0 +1,328 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders
+ ~~~~~~~~~~~~~~~
+
+ Builder superclass for all builders.
+
+ :copyright: 2007-2008 by Georg Brandl, Sebastian Wiesner, Horst Gutmann.
+ :license: BSD.
+"""
+
+import os
+import gettext
+from os import path
+
+from docutils import nodes
+
+from sphinx import package_dir, locale
+from sphinx.util import SEP, relative_uri
+from sphinx.environment import BuildEnvironment
+from sphinx.util.console import bold, purple, darkgreen
+
+# side effect: registers roles and directives
+from sphinx import roles
+from sphinx import directives
+
+
+ENV_PICKLE_FILENAME = 'environment.pickle'
+
+
+class Builder(object):
+ """
+ Builds target formats from the reST sources.
+ """
+
+ # builder's name, for the -b command line options
+ name = ''
+
+ def __init__(self, app, env=None, freshenv=False):
+ self.srcdir = app.srcdir
+ self.confdir = app.confdir
+ self.outdir = app.outdir
+ self.doctreedir = app.doctreedir
+ if not path.isdir(self.doctreedir):
+ os.makedirs(self.doctreedir)
+
+ self.app = app
+ self.warn = app.warn
+ self.info = app.info
+ self.config = app.config
+
+ self.load_i18n()
+
+ # images that need to be copied over (source -> dest)
+ self.images = {}
+
+ # if None, this is set in load_env()
+ self.env = env
+ self.freshenv = freshenv
+
+ self.init()
+ self.load_env()
+
+ # helper methods
+
+ def init(self):
+ """Load necessary templates and perform initialization."""
+ raise NotImplementedError
+
+ def init_templates(self):
+ # Call this from init() if you need templates.
+ if self.config.template_bridge:
+ self.templates = self.app.import_object(
+ self.config.template_bridge, 'template_bridge setting')()
+ else:
+ from sphinx._jinja import BuiltinTemplates
+ self.templates = BuiltinTemplates()
+ self.templates.init(self)
+
+ def get_target_uri(self, docname, typ=None):
+ """
+ Return the target URI for a document name (typ can be used to qualify
+ the link characteristic for individual builders).
+ """
+ raise NotImplementedError
+
+ def get_relative_uri(self, from_, to, typ=None):
+ """
+ Return a relative URI between two source filenames. May raise environment.NoUri
+ if there's no way to return a sensible URI.
+ """
+ return relative_uri(self.get_target_uri(from_),
+ self.get_target_uri(to, typ))
+
+ def get_outdated_docs(self):
+ """
+ Return an iterable of output files that are outdated, or a string describing
+ what an update build will build.
+ """
+ raise NotImplementedError
+
+ def status_iterator(self, iterable, summary, colorfunc=darkgreen):
+ l = -1
+ for item in iterable:
+ if l == -1:
+ self.info(bold(summary), nonl=1)
+ l = 0
+ self.info(colorfunc(item) + ' ', nonl=1)
+ yield item
+ if l == 0:
+ self.info()
+
+ supported_image_types = []
+
+ def post_process_images(self, doctree):
+ """
+ Pick the best candidate for all image URIs.
+ """
+ for node in doctree.traverse(nodes.image):
+ if '?' in node['candidates']:
+ # don't rewrite nonlocal image URIs
+ continue
+ if '*' not in node['candidates']:
+ for imgtype in self.supported_image_types:
+ candidate = node['candidates'].get(imgtype, None)
+ if candidate:
+ break
+ else:
+ self.warn('%s:%s: no matching candidate for image URI %r' %
+ (node.source, getattr(node, 'lineno', ''), node['uri']))
+ continue
+ node['uri'] = candidate
+ else:
+ candidate = node['uri']
+ if candidate not in self.env.images:
+ # non-existing URI; let it alone
+ continue
+ self.images[candidate] = self.env.images[candidate][1]
+
+ # build methods
+
+ def load_i18n(self):
+ """
+ Load translated strings from the configured localedirs if
+ enabled in the configuration.
+ """
+ self.translator = None
+ if self.config.language is not None:
+ self.info(bold('loading translations [%s]... ' % self.config.language),
+ nonl=True)
+ locale_dirs = [path.join(package_dir, 'locale')] + \
+ [path.join(self.srcdir, x) for x in self.config.locale_dirs]
+ for dir_ in locale_dirs:
+ try:
+ trans = gettext.translation('sphinx', localedir=dir_,
+ languages=[self.config.language])
+ if self.translator is None:
+ self.translator = trans
+ else:
+ self.translator._catalog.update(trans.catalog)
+ except Exception:
+ # Language couldn't be found in the specified path
+ pass
+ if self.translator is not None:
+ self.info('done')
+ else:
+ self.info('locale not available')
+ if self.translator is None:
+ self.translator = gettext.NullTranslations()
+ self.translator.install(unicode=True)
+ locale.init() # translate common labels
+
+ def load_env(self):
+ """Set up the build environment."""
+ if self.env:
+ return
+ if not self.freshenv:
+ try:
+ self.info(bold('loading pickled environment... '), nonl=True)
+ self.env = BuildEnvironment.frompickle(self.config,
+ path.join(self.doctreedir, ENV_PICKLE_FILENAME))
+ self.info('done')
+ except Exception, err:
+ if type(err) is IOError and err.errno == 2:
+ self.info('not found')
+ else:
+ self.info('failed: %s' % err)
+ self.env = BuildEnvironment(self.srcdir, self.doctreedir, self.config)
+ self.env.find_files(self.config)
+ else:
+ self.env = BuildEnvironment(self.srcdir, self.doctreedir, self.config)
+ self.env.find_files(self.config)
+ self.env.set_warnfunc(self.warn)
+
+ def build_all(self):
+ """Build all source files."""
+ self.build(None, summary='all source files', method='all')
+
+ def build_specific(self, filenames):
+ """Only rebuild as much as needed for changes in the source_filenames."""
+ # bring the filenames to the canonical format, that is,
+ # relative to the source directory and without source_suffix.
+ dirlen = len(self.srcdir) + 1
+ to_write = []
+ suffix = self.config.source_suffix
+ for filename in filenames:
+ filename = path.abspath(filename)[dirlen:]
+ if filename.endswith(suffix):
+ filename = filename[:-len(suffix)]
+ filename = filename.replace(os.path.sep, SEP)
+ to_write.append(filename)
+ self.build(to_write, method='specific',
+ summary='%d source files given on command '
+ 'line' % len(to_write))
+
+ def build_update(self):
+ """Only rebuild files changed or added since last build."""
+ to_build = self.get_outdated_docs()
+ if isinstance(to_build, str):
+ self.build(['__all__'], to_build)
+ else:
+ to_build = list(to_build)
+ self.build(to_build,
+ summary='targets for %d source files that are '
+ 'out of date' % len(to_build))
+
+ def build(self, docnames, summary=None, method='update'):
+ if summary:
+ self.info(bold('building [%s]: ' % self.name), nonl=1)
+ self.info(summary)
+
+ updated_docnames = []
+ # while reading, collect all warnings from docutils
+ warnings = []
+ self.env.set_warnfunc(warnings.append)
+ self.info(bold('updating environment: '), nonl=1)
+ iterator = self.env.update(self.config, self.srcdir, self.doctreedir, self.app)
+ # the first item in the iterator is a summary message
+ self.info(iterator.next())
+ for docname in self.status_iterator(iterator, 'reading sources... ', purple):
+ updated_docnames.append(docname)
+ # nothing further to do, the environment has already done the reading
+ for warning in warnings:
+ if warning.strip():
+ self.warn(warning)
+ self.env.set_warnfunc(self.warn)
+
+ if updated_docnames:
+ # save the environment
+ self.info(bold('pickling environment... '), nonl=True)
+ self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME))
+ self.info('done')
+
+ # global actions
+ self.info(bold('checking consistency... '), nonl=True)
+ self.env.check_consistency()
+ self.info('done')
+ else:
+ if method == 'update' and not docnames:
+ self.info(bold('no targets are out of date.'))
+ return
+
+ # another indirection to support methods which don't build files
+ # individually
+ self.write(docnames, updated_docnames, method)
+
+ # finish (write static files etc.)
+ self.finish()
+ if self.app._warncount:
+ self.info(bold('build succeeded, %s warning%s.' %
+ (self.app._warncount,
+ self.app._warncount != 1 and 's' or '')))
+ else:
+ self.info(bold('build succeeded.'))
+
+ def write(self, build_docnames, updated_docnames, method='update'):
+ if build_docnames is None or build_docnames == ['__all__']:
+ # build_all
+ build_docnames = self.env.found_docs
+ if method == 'update':
+ # build updated ones as well
+ docnames = set(build_docnames) | set(updated_docnames)
+ else:
+ docnames = set(build_docnames)
+
+ # add all toctree-containing files that may have changed
+ for docname in list(docnames):
+ for tocdocname in self.env.files_to_rebuild.get(docname, []):
+ docnames.add(tocdocname)
+ docnames.add(self.config.master_doc)
+
+ self.info(bold('preparing documents... '), nonl=True)
+ self.prepare_writing(docnames)
+ self.info('done')
+
+ # write target files
+ warnings = []
+ self.env.set_warnfunc(warnings.append)
+ for docname in self.status_iterator(sorted(docnames),
+ 'writing output... ', darkgreen):
+ doctree = self.env.get_and_resolve_doctree(docname, self)
+ self.write_doc(docname, doctree)
+ for warning in warnings:
+ if warning.strip():
+ self.warn(warning)
+ self.env.set_warnfunc(self.warn)
+
+ def prepare_writing(self, docnames):
+ raise NotImplementedError
+
+ def write_doc(self, docname, doctree):
+ raise NotImplementedError
+
+ def finish(self):
+ raise NotImplementedError
+
+
+BUILTIN_BUILDERS = {
+ 'html': ('html', 'StandaloneHTMLBuilder'),
+ 'pickle': ('html', 'PickleHTMLBuilder'),
+ 'json': ('html', 'JSONHTMLBuilder'),
+ 'web': ('html', 'PickleHTMLBuilder'),
+ 'htmlhelp': ('htmlhelp', 'HTMLHelpBuilder'),
+ 'latex': ('latex', 'LaTeXBuilder'),
+ 'text': ('text', 'TextBuilder'),
+ 'changes': ('changes', 'ChangesBuilder'),
+ 'linkcheck': ('linkcheck', 'CheckExternalLinksBuilder'),
+}
diff --git a/sphinx/builders/changes.py b/sphinx/builders/changes.py
new file mode 100644
index 00000000..28805738
--- /dev/null
+++ b/sphinx/builders/changes.py
@@ -0,0 +1,137 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders.changes
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Changelog builder.
+
+ :copyright: 2008 by Georg Brandl.
+ :license: BSD.
+"""
+
+import codecs
+import shutil
+from os import path
+from cgi import escape
+
+from sphinx import package_dir
+from sphinx.util import ensuredir, os_path
+from sphinx.builders import Builder
+from sphinx.util.console import bold
+
+
+class ChangesBuilder(Builder):
+ """
+ Write a summary with all versionadded/changed directives.
+ """
+ name = 'changes'
+
+ def init(self):
+ self.init_templates()
+
+ def get_outdated_docs(self):
+ return self.outdir
+
+ typemap = {
+ 'versionadded': 'added',
+ 'versionchanged': 'changed',
+ 'deprecated': 'deprecated',
+ }
+
+ def write(self, *ignored):
+ version = self.config.version
+ libchanges = {}
+ apichanges = []
+ otherchanges = {}
+ if version not in self.env.versionchanges:
+ self.info(bold('no changes in this version.'))
+ return
+ self.info(bold('writing summary file...'))
+ for type, docname, lineno, module, descname, content in \
+ self.env.versionchanges[version]:
+ ttext = self.typemap[type]
+ context = content.replace('\n', ' ')
+ if descname and docname.startswith('c-api'):
+ if not descname:
+ continue
+ if context:
+ entry = '%s: %s: %s' % (descname, ttext, context)
+ else:
+ entry = '%s: %s.' % (descname, ttext)
+ apichanges.append((entry, docname, lineno))
+ elif descname or module:
+ if not module:
+ module = _('Builtins')
+ if not descname:
+ descname = _('Module level')
+ if context:
+ entry = '%s: %s: %s' % (descname, ttext, context)
+ else:
+ entry = '%s: %s.' % (descname, ttext)
+ libchanges.setdefault(module, []).append((entry, docname, lineno))
+ else:
+ if not context:
+ continue
+ entry = '%s: %s' % (ttext.capitalize(), context)
+ title = self.env.titles[docname].astext()
+ otherchanges.setdefault((docname, title), []).append(
+ (entry, docname, lineno))
+
+ ctx = {
+ 'project': self.config.project,
+ 'version': version,
+ 'docstitle': self.config.html_title,
+ 'shorttitle': self.config.html_short_title,
+ 'libchanges': sorted(libchanges.iteritems()),
+ 'apichanges': sorted(apichanges),
+ 'otherchanges': sorted(otherchanges.iteritems()),
+ 'show_sphinx': self.config.html_show_sphinx,
+ }
+ f = open(path.join(self.outdir, 'index.html'), 'w')
+ try:
+ f.write(self.templates.render('changes/frameset.html', ctx))
+ finally:
+ f.close()
+ f = open(path.join(self.outdir, 'changes.html'), 'w')
+ try:
+ f.write(self.templates.render('changes/versionchanges.html', ctx))
+ finally:
+ f.close()
+
+ hltext = ['.. versionadded:: %s' % version,
+ '.. versionchanged:: %s' % version,
+ '.. deprecated:: %s' % version]
+
+ def hl(no, line):
+ line = '' % no + escape(line)
+ for x in hltext:
+ if x in line:
+ line = '%s' % line
+ break
+ return line
+
+ self.info(bold('copying source files...'))
+ for docname in self.env.all_docs:
+ f = open(self.env.doc2path(docname))
+ lines = f.readlines()
+ targetfn = path.join(self.outdir, 'rst', os_path(docname)) + '.html'
+ ensuredir(path.dirname(targetfn))
+ f = codecs.open(targetfn, 'w', 'utf8')
+ try:
+ text = ''.join(hl(i+1, line) for (i, line) in enumerate(lines))
+ ctx = {'filename': self.env.doc2path(docname, None), 'text': text}
+ f.write(self.templates.render('changes/rstsource.html', ctx))
+ finally:
+ f.close()
+ shutil.copyfile(path.join(package_dir, 'static', 'default.css'),
+ path.join(self.outdir, 'default.css'))
+
+ def hl(self, text, version):
+ text = escape(text)
+ for directive in ['versionchanged', 'versionadded', 'deprecated']:
+ text = text.replace('.. %s:: %s' % (directive, version),
+ '.. %s:: %s' % (directive, version))
+ return text
+
+ def finish(self):
+ pass
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
new file mode 100644
index 00000000..adf58be1
--- /dev/null
+++ b/sphinx/builders/html.py
@@ -0,0 +1,607 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders.html
+ ~~~~~~~~~~~~~~~~~~~~
+
+ Several HTML builders.
+
+ :copyright: 2007-2008 by Georg Brandl, Armin Ronacher.
+ :license: BSD.
+"""
+
+import os
+import codecs
+import shutil
+import cPickle as pickle
+from os import path
+
+from docutils.io import DocTreeInput, StringOutput
+from docutils.core import publish_parts
+from docutils.utils import new_document
+from docutils.frontend import OptionParser
+from docutils.readers.doctree import Reader as DoctreeReader
+
+from sphinx import package_dir, __version__
+from sphinx.util import SEP, os_path, relative_uri, ensuredir, ustrftime
+from sphinx.search import js_index
+from sphinx.builders import Builder, ENV_PICKLE_FILENAME
+from sphinx.highlighting import PygmentsBridge
+from sphinx.util.console import bold
+from sphinx.writers.html import HTMLWriter, HTMLTranslator, SmartyPantsHTMLTranslator
+
+try:
+ import json
+except ImportError:
+ try:
+ import simplejson as json
+ except ImportError:
+ json = None
+
+
+INVENTORY_FILENAME = 'objects.inv'
+LAST_BUILD_FILENAME = 'last_build'
+
+
+class StandaloneHTMLBuilder(Builder):
+ """
+ Builds standalone HTML docs.
+ """
+ name = 'html'
+ copysource = True
+ out_suffix = '.html'
+ indexer_format = js_index
+ supported_image_types = ['image/svg+xml', 'image/png', 'image/gif',
+ 'image/jpeg']
+ searchindex_filename = 'searchindex.js'
+ add_header_links = True
+ add_definition_links = True
+
+ # This is a class attribute because it is mutated by Sphinx.add_javascript.
+ script_files = ['_static/jquery.js', '_static/doctools.js']
+
+ def init(self):
+ """Load templates."""
+ self.init_templates()
+ self.init_translator_class()
+ if self.config.html_file_suffix:
+ self.out_suffix = self.config.html_file_suffix
+
+ if self.config.language is not None:
+ jsfile = path.join(package_dir, 'locale', self.config.language,
+ 'LC_MESSAGES', 'sphinx.js')
+ if path.isfile(jsfile):
+ self.script_files.append('_static/translations.js')
+
+ def init_translator_class(self):
+ if self.config.html_translator_class:
+ self.translator_class = self.app.import_object(
+ self.config.html_translator_class, 'html_translator_class setting')
+ elif self.config.html_use_smartypants:
+ self.translator_class = SmartyPantsHTMLTranslator
+ else:
+ self.translator_class = HTMLTranslator
+
+ def render_partial(self, node):
+ """Utility: Render a lone doctree node."""
+ doc = new_document('')
+ doc.append(node)
+ return publish_parts(
+ doc,
+ source_class=DocTreeInput,
+ reader=DoctreeReader(),
+ writer=HTMLWriter(self),
+ settings_overrides={'output_encoding': 'unicode'}
+ )
+
+ def prepare_writing(self, docnames):
+ from sphinx.search import IndexBuilder
+
+ self.indexer = IndexBuilder(self.env)
+ self.load_indexer(docnames)
+ self.docwriter = HTMLWriter(self)
+ self.docsettings = OptionParser(
+ defaults=self.env.settings,
+ components=(self.docwriter,)).get_default_values()
+
+ # format the "last updated on" string, only once is enough since it
+ # typically doesn't include the time of day
+ lufmt = self.config.html_last_updated_fmt
+ if lufmt is not None:
+ self.last_updated = ustrftime(lufmt or _('%b %d, %Y'))
+ else:
+ self.last_updated = None
+
+ logo = self.config.html_logo and \
+ path.basename(self.config.html_logo) or ''
+
+ favicon = self.config.html_favicon and \
+ path.basename(self.config.html_favicon) or ''
+ if favicon and os.path.splitext(favicon)[1] != '.ico':
+ self.warn('html_favicon is not an .ico file')
+
+ if not isinstance(self.config.html_use_opensearch, basestring):
+ self.warn('html_use_opensearch config value must now be a string')
+
+ self.relations = self.env.collect_relations()
+
+ rellinks = []
+ if self.config.html_use_index:
+ rellinks.append(('genindex', _('General Index'), 'I', _('index')))
+ if self.config.html_use_modindex and self.env.modules:
+ rellinks.append(('modindex', _('Global Module Index'), 'M', _('modules')))
+
+ self.globalcontext = dict(
+ project = self.config.project,
+ release = self.config.release,
+ version = self.config.version,
+ last_updated = self.last_updated,
+ copyright = self.config.copyright,
+ master_doc = self.config.master_doc,
+ style = self.config.html_style,
+ use_opensearch = self.config.html_use_opensearch,
+ docstitle = self.config.html_title,
+ shorttitle = self.config.html_short_title,
+ show_sphinx = self.config.html_show_sphinx,
+ file_suffix = self.out_suffix,
+ script_files = self.script_files,
+ sphinx_version = __version__,
+ rellinks = rellinks,
+ builder = self.name,
+ parents = [],
+ logo = logo,
+ favicon = favicon,
+ )
+ self.globalcontext.update(self.config.html_context)
+
+ def get_doc_context(self, docname, body, metatags):
+ """Collect items for the template context of a page."""
+ # find out relations
+ prev = next = None
+ parents = []
+ rellinks = self.globalcontext['rellinks'][:]
+ related = self.relations.get(docname)
+ titles = self.env.titles
+ if related and related[2]:
+ try:
+ next = {'link': self.get_relative_uri(docname, related[2]),
+ 'title': self.render_partial(titles[related[2]])['title']}
+ rellinks.append((related[2], next['title'], 'N', _('next')))
+ except KeyError:
+ next = None
+ if related and related[1]:
+ try:
+ prev = {'link': self.get_relative_uri(docname, related[1]),
+ 'title': self.render_partial(titles[related[1]])['title']}
+ rellinks.append((related[1], prev['title'], 'P', _('previous')))
+ except KeyError:
+ # the relation is (somehow) not in the TOC tree, handle that gracefully
+ prev = None
+ while related and related[0]:
+ try:
+ parents.append(
+ {'link': self.get_relative_uri(docname, related[0]),
+ 'title': self.render_partial(titles[related[0]])['title']})
+ except KeyError:
+ pass
+ related = self.relations.get(related[0])
+ if parents:
+ parents.pop() # remove link to the master file; we have a generic
+ # "back to index" link already
+ parents.reverse()
+
+ # title rendered as HTML
+ title = titles.get(docname)
+ title = title and self.render_partial(title)['title'] or ''
+ # the name for the copied source
+ sourcename = self.config.html_copy_source and docname + '.txt' or ''
+
+ # metadata for the document
+ meta = self.env.metadata.get(docname)
+
+ return dict(
+ parents = parents,
+ prev = prev,
+ next = next,
+ title = title,
+ meta = meta,
+ body = body,
+ metatags = metatags,
+ rellinks = rellinks,
+ sourcename = sourcename,
+ toc = self.render_partial(self.env.get_toc_for(docname))['fragment'],
+ # only display a TOC if there's more than one item to show
+ display_toc = (self.env.toc_num_entries[docname] > 1),
+ )
+
+ def write_doc(self, docname, doctree):
+ self.post_process_images(doctree)
+ destination = StringOutput(encoding='utf-8')
+ doctree.settings = self.docsettings
+
+ self.imgpath = relative_uri(self.get_target_uri(docname), '_images')
+ self.docwriter.write(doctree, destination)
+ self.docwriter.assemble_parts()
+ body = self.docwriter.parts['fragment']
+ metatags = self.docwriter.clean_meta
+
+ ctx = self.get_doc_context(docname, body, metatags)
+ self.index_page(docname, doctree, ctx.get('title', ''))
+ self.handle_page(docname, ctx, event_arg=doctree)
+
+ def finish(self):
+ self.info(bold('writing additional files...'), nonl=1)
+
+ # the global general index
+
+ if self.config.html_use_index:
+ # the total count of lines for each index letter, used to distribute
+ # the entries into two columns
+ genindex = self.env.create_index(self)
+ indexcounts = []
+ for _, entries in genindex:
+ indexcounts.append(sum(1 + len(subitems)
+ for _, (_, subitems) in entries))
+
+ genindexcontext = dict(
+ genindexentries = genindex,
+ genindexcounts = indexcounts,
+ split_index = self.config.html_split_index,
+ )
+ self.info(' genindex', nonl=1)
+
+ if self.config.html_split_index:
+ self.handle_page('genindex', genindexcontext, 'genindex-split.html')
+ self.handle_page('genindex-all', genindexcontext, 'genindex.html')
+ for (key, entries), count in zip(genindex, indexcounts):
+ ctx = {'key': key, 'entries': entries, 'count': count,
+ 'genindexentries': genindex}
+ self.handle_page('genindex-' + key, ctx, 'genindex-single.html')
+ else:
+ self.handle_page('genindex', genindexcontext, 'genindex.html')
+
+ # the global module index
+
+ if self.config.html_use_modindex and self.env.modules:
+ # the sorted list of all modules, for the global module index
+ modules = sorted(((mn, (self.get_relative_uri('modindex', fn) +
+ '#module-' + mn, sy, pl, dep))
+ for (mn, (fn, sy, pl, dep)) in
+ self.env.modules.iteritems()),
+ key=lambda x: x[0].lower())
+ # collect all platforms
+ platforms = set()
+ # sort out collapsable modules
+ modindexentries = []
+ letters = []
+ pmn = ''
+ num_toplevels = 0
+ num_collapsables = 0
+ cg = 0 # collapse group
+ fl = '' # first letter
+ for mn, (fn, sy, pl, dep) in modules:
+ pl = pl and pl.split(', ') or []
+ platforms.update(pl)
+ if fl != mn[0].lower() and mn[0] != '_':
+ # heading
+ modindexentries.append(['', False, 0, False,
+ mn[0].upper(), '', [], False])
+ letters.append(mn[0].upper())
+ tn = mn.split('.')[0]
+ if tn != mn:
+ # submodule
+ if pmn == tn:
+ # first submodule - make parent collapsable
+ modindexentries[-1][1] = True
+ num_collapsables += 1
+ elif not pmn.startswith(tn):
+ # submodule without parent in list, add dummy entry
+ cg += 1
+ modindexentries.append([tn, True, cg, False, '', '', [], False])
+ else:
+ num_toplevels += 1
+ cg += 1
+ modindexentries.append([mn, False, cg, (tn != mn), fn, sy, pl, dep])
+ pmn = mn
+ fl = mn[0].lower()
+ platforms = sorted(platforms)
+
+ # apply heuristics when to collapse modindex at page load:
+ # only collapse if number of toplevel modules is larger than
+ # number of submodules
+ collapse = len(modules) - num_toplevels < num_toplevels
+
+ modindexcontext = dict(
+ modindexentries = modindexentries,
+ platforms = platforms,
+ letters = letters,
+ collapse_modindex = collapse,
+ )
+ self.info(' modindex', nonl=1)
+ self.handle_page('modindex', modindexcontext, 'modindex.html')
+
+ # the search page
+ if self.name != 'htmlhelp':
+ self.info(' search', nonl=1)
+ self.handle_page('search', {}, 'search.html')
+
+ # additional pages from conf.py
+ for pagename, template in self.config.html_additional_pages.items():
+ self.info(' '+pagename, nonl=1)
+ self.handle_page(pagename, {}, template)
+
+ if self.config.html_use_opensearch and self.name != 'htmlhelp':
+ self.info(' opensearch', nonl=1)
+ fn = path.join(self.outdir, '_static', 'opensearch.xml')
+ self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn)
+
+ self.info()
+
+ # copy image files
+ if self.images:
+ self.info(bold('copying images...'), nonl=True)
+ ensuredir(path.join(self.outdir, '_images'))
+ for src, dest in self.images.iteritems():
+ self.info(' '+src, nonl=1)
+ shutil.copyfile(path.join(self.srcdir, src),
+ path.join(self.outdir, '_images', dest))
+ self.info()
+
+ # copy static files
+ self.info(bold('copying static files... '), nonl=True)
+ ensuredir(path.join(self.outdir, '_static'))
+ # first, create pygments style file
+ f = open(path.join(self.outdir, '_static', 'pygments.css'), 'w')
+ f.write(PygmentsBridge('html', self.config.pygments_style).get_stylesheet())
+ f.close()
+ # then, copy translations JavaScript file
+ if self.config.language is not None:
+ jsfile = path.join(package_dir, 'locale', self.config.language,
+ 'LC_MESSAGES', 'sphinx.js')
+ if path.isfile(jsfile):
+ shutil.copyfile(jsfile, path.join(self.outdir, '_static',
+ 'translations.js'))
+ # then, copy over all user-supplied static files
+ staticdirnames = [path.join(package_dir, 'static')] + \
+ [path.join(self.confdir, spath)
+ for spath in self.config.html_static_path]
+ for staticdirname in staticdirnames:
+ for filename in os.listdir(staticdirname):
+ if filename.startswith('.'):
+ continue
+ fullname = path.join(staticdirname, filename)
+ targetname = path.join(self.outdir, '_static', filename)
+ if path.isfile(fullname):
+ shutil.copyfile(fullname, targetname)
+ elif path.isdir(fullname):
+ if filename in self.config.exclude_dirnames:
+ continue
+ if path.exists(targetname):
+ shutil.rmtree(targetname)
+ shutil.copytree(fullname, targetname)
+ # last, copy logo file (handled differently)
+ if self.config.html_logo:
+ logobase = path.basename(self.config.html_logo)
+ shutil.copyfile(path.join(self.confdir, self.config.html_logo),
+ path.join(self.outdir, '_static', logobase))
+ self.info('done')
+
+ # dump the search index
+ self.handle_finish()
+
+ def get_outdated_docs(self):
+ if self.templates:
+ template_mtime = self.templates.newest_template_mtime()
+ else:
+ template_mtime = 0
+ for docname in self.env.found_docs:
+ if docname not in self.env.all_docs:
+ yield docname
+ continue
+ targetname = self.env.doc2path(docname, self.outdir, self.out_suffix)
+ try:
+ targetmtime = path.getmtime(targetname)
+ except Exception:
+ targetmtime = 0
+ try:
+ srcmtime = max(path.getmtime(self.env.doc2path(docname)),
+ template_mtime)
+ if srcmtime > targetmtime:
+ yield docname
+ except EnvironmentError:
+ # source doesn't exist anymore
+ pass
+
+ def load_indexer(self, docnames):
+ keep = set(self.env.all_docs) - set(docnames)
+ try:
+ f = open(path.join(self.outdir, self.searchindex_filename), 'rb')
+ try:
+ self.indexer.load(f, self.indexer_format)
+ finally:
+ f.close()
+ except (IOError, OSError, ValueError):
+ if keep:
+ self.warn("search index couldn't be loaded, but not all documents "
+ "will be built: the index will be incomplete.")
+ # delete all entries for files that will be rebuilt
+ self.indexer.prune(keep)
+
+ def index_page(self, pagename, doctree, title):
+ # only index pages with title
+ if self.indexer is not None and title:
+ self.indexer.feed(pagename, title, doctree)
+
+ # --------- these are overwritten by the serialization builder
+
+ def get_target_uri(self, docname, typ=None):
+ return docname + self.out_suffix
+
+ def handle_page(self, pagename, addctx, templatename='page.html',
+ outfilename=None, event_arg=None):
+ ctx = self.globalcontext.copy()
+ # current_page_name is backwards compatibility
+ ctx['pagename'] = ctx['current_page_name'] = pagename
+
+ def pathto(otheruri, resource=False,
+ baseuri=self.get_target_uri(pagename)):
+ if not resource:
+ otheruri = self.get_target_uri(otheruri)
+ return relative_uri(baseuri, otheruri)
+ ctx['pathto'] = pathto
+ ctx['hasdoc'] = lambda name: name in self.env.all_docs
+ ctx['customsidebar'] = self.config.html_sidebars.get(pagename)
+ ctx.update(addctx)
+
+ self.app.emit('html-page-context', pagename, templatename, ctx, event_arg)
+
+ output = self.templates.render(templatename, ctx)
+ if not outfilename:
+ outfilename = path.join(self.outdir, os_path(pagename) + self.out_suffix)
+ ensuredir(path.dirname(outfilename)) # normally different from self.outdir
+ try:
+ f = codecs.open(outfilename, 'w', 'utf-8')
+ try:
+ f.write(output)
+ finally:
+ f.close()
+ except (IOError, OSError), err:
+ self.warn("Error writing file %s: %s" % (outfilename, err))
+ if self.copysource and ctx.get('sourcename'):
+ # copy the source file for the "show source" link
+ source_name = path.join(self.outdir, '_sources', os_path(ctx['sourcename']))
+ ensuredir(path.dirname(source_name))
+ shutil.copyfile(self.env.doc2path(pagename), source_name)
+
+ def handle_finish(self):
+ self.info(bold('dumping search index... '), nonl=True)
+ self.indexer.prune(self.env.all_docs)
+ f = open(path.join(self.outdir, self.searchindex_filename), 'wb')
+ try:
+ self.indexer.dump(f, self.indexer_format)
+ finally:
+ f.close()
+ self.info('done')
+
+ self.info(bold('dumping object inventory... '), nonl=True)
+ f = open(path.join(self.outdir, INVENTORY_FILENAME), 'w')
+ try:
+ f.write('# Sphinx inventory version 1\n')
+ f.write('# Project: %s\n' % self.config.project.encode('utf-8'))
+ f.write('# Version: %s\n' % self.config.version)
+ for modname, info in self.env.modules.iteritems():
+ f.write('%s mod %s\n' % (modname, self.get_target_uri(info[0])))
+ for refname, (docname, desctype) in self.env.descrefs.iteritems():
+ f.write('%s %s %s\n' % (refname, desctype, self.get_target_uri(docname)))
+ finally:
+ f.close()
+ self.info('done')
+
+
+class SerializingHTMLBuilder(StandaloneHTMLBuilder):
+ """
+ An abstract builder that serializes the HTML generated.
+ """
+ #: the serializing implementation to use. Set this to a module that
+ #: implements a `dump`, `load`, `dumps` and `loads` functions
+ #: (pickle, simplejson etc.)
+ implementation = None
+
+ #: the filename for the global context file
+ globalcontext_filename = None
+
+ supported_image_types = ('image/svg+xml', 'image/png', 'image/gif',
+ 'image/jpeg')
+
+ def init(self):
+ self.init_translator_class()
+ self.templates = None # no template bridge necessary
+
+ def get_target_uri(self, docname, typ=None):
+ if docname == 'index':
+ return ''
+ if docname.endswith(SEP + 'index'):
+ return docname[:-5] # up to sep
+ return docname + SEP
+
+ def handle_page(self, pagename, ctx, templatename='page.html',
+ outfilename=None, event_arg=None):
+ ctx['current_page_name'] = pagename
+ sidebarfile = self.config.html_sidebars.get(pagename)
+ if sidebarfile:
+ ctx['customsidebar'] = sidebarfile
+
+ if not outfilename:
+ outfilename = path.join(self.outdir, os_path(pagename) + self.out_suffix)
+
+ self.app.emit('html-page-context', pagename, templatename, ctx, event_arg)
+
+ ensuredir(path.dirname(outfilename))
+ f = open(outfilename, 'wb')
+ try:
+ self.implementation.dump(ctx, f, 2)
+ finally:
+ f.close()
+
+ # if there is a source file, copy the source file for the
+ # "show source" link
+ if ctx.get('sourcename'):
+ source_name = path.join(self.outdir, '_sources',
+ os_path(ctx['sourcename']))
+ ensuredir(path.dirname(source_name))
+ shutil.copyfile(self.env.doc2path(pagename), source_name)
+
+ def handle_finish(self):
+ # dump the global context
+ outfilename = path.join(self.outdir, self.globalcontext_filename)
+ f = open(outfilename, 'wb')
+ try:
+ self.implementation.dump(self.globalcontext, f, 2)
+ finally:
+ f.close()
+
+ # super here to dump the search index
+ StandaloneHTMLBuilder.handle_finish(self)
+
+ # copy the environment file from the doctree dir to the output dir
+ # as needed by the web app
+ shutil.copyfile(path.join(self.doctreedir, ENV_PICKLE_FILENAME),
+ path.join(self.outdir, ENV_PICKLE_FILENAME))
+
+ # touch 'last build' file, used by the web application to determine
+ # when to reload its environment and clear the cache
+ open(path.join(self.outdir, LAST_BUILD_FILENAME), 'w').close()
+
+
+class PickleHTMLBuilder(SerializingHTMLBuilder):
+ """
+ A Builder that dumps the generated HTML into pickle files.
+ """
+ implementation = pickle
+ indexer_format = pickle
+ name = 'pickle'
+ out_suffix = '.fpickle'
+ globalcontext_filename = 'globalcontext.pickle'
+ searchindex_filename = 'searchindex.pickle'
+
+# compatibility alias
+WebHTMLBuilder = PickleHTMLBuilder
+
+
+class JSONHTMLBuilder(SerializingHTMLBuilder):
+ """
+ A builder that dumps the generated HTML into JSON files.
+ """
+ implementation = json
+ indexer_format = json
+ name = 'json'
+ out_suffix = '.fjson'
+ globalcontext_filename = 'globalcontext.json'
+ searchindex_filename = 'searchindex.json'
+
+ def init(self):
+ if json is None:
+ from sphinx.application import SphinxError
+ raise SphinxError('The module simplejson (or json in Python >= 2.6) '
+ 'is not available. The JSONHTMLBuilder builder '
+ 'will not work.')
+ SerializingHTMLBuilder.init(self)
diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py
new file mode 100644
index 00000000..23900f36
--- /dev/null
+++ b/sphinx/builders/htmlhelp.py
@@ -0,0 +1,245 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders.htmlhelp
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Build HTML help support files.
+ Parts adapted from Python's Doc/tools/prechm.py.
+
+ :copyright: 2007-2008 by Georg Brandl.
+ :license: BSD.
+"""
+
+import os
+import cgi
+from os import path
+
+from docutils import nodes
+
+from sphinx import addnodes
+from sphinx.builders.html import StandaloneHTMLBuilder
+
+
+# Project file (*.hhp) template. 'outname' is the file basename (like
+# the pythlp in pythlp.hhp); 'version' is the doc version number (like
+# the 2.2 in Python 2.2).
+# The magical numbers in the long line under [WINDOWS] set most of the
+# user-visible features (visible buttons, tabs, etc).
+# About 0x10384e: This defines the buttons in the help viewer. The
+# following defns are taken from htmlhelp.h. Not all possibilities
+# actually work, and not all those that work are available from the Help
+# Workshop GUI. In particular, the Zoom/Font button works and is not
+# available from the GUI. The ones we're using are marked with 'x':
+#
+# 0x000002 Hide/Show x
+# 0x000004 Back x
+# 0x000008 Forward x
+# 0x000010 Stop
+# 0x000020 Refresh
+# 0x000040 Home x
+# 0x000080 Forward
+# 0x000100 Back
+# 0x000200 Notes
+# 0x000400 Contents
+# 0x000800 Locate x
+# 0x001000 Options x
+# 0x002000 Print x
+# 0x004000 Index
+# 0x008000 Search
+# 0x010000 History
+# 0x020000 Favorites
+# 0x040000 Jump 1
+# 0x080000 Jump 2
+# 0x100000 Zoom/Font x
+# 0x200000 TOC Next
+# 0x400000 TOC Prev
+
+project_template = '''\
+[OPTIONS]
+Binary TOC=Yes
+Binary Index=No
+Compiled file=%(outname)s.chm
+Contents file=%(outname)s.hhc
+Default Window=%(outname)s
+Default topic=index.html
+Display compile progress=No
+Full text search stop list file=%(outname)s.stp
+Full-text search=Yes
+Index file=%(outname)s.hhk
+Language=0x409
+Title=%(title)s
+
+[WINDOWS]
+%(outname)s="%(title)s","%(outname)s.hhc","%(outname)s.hhk",\
+"index.html","index.html",,,,,0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0
+
+[FILES]
+'''
+
+contents_header = '''\
+
+
+
+
+
+
+
+
+'''
+
+contents_footer = '''\
+
+'''
+
+object_sitemap = '''\
+
+'''
+
+# List of words the full text search facility shouldn't index. This
+# becomes file outname.stp. Note that this list must be pretty small!
+# Different versions of the MS docs claim the file has a maximum size of
+# 256 or 512 bytes (including \r\n at the end of each line).
+# Note that "and", "or", "not" and "near" are operators in the search
+# language, so no point indexing them even if we wanted to.
+stopwords = """
+a and are as at
+be but by
+for
+if in into is it
+near no not
+of on or
+such
+that the their then there these they this to
+was will with
+""".split()
+
+
+class HTMLHelpBuilder(StandaloneHTMLBuilder):
+ """
+ Builder that also outputs Windows HTML help project, contents and index files.
+ Adapted from the original Doc/tools/prechm.py.
+ """
+ name = 'htmlhelp'
+
+ # don't copy the reST source
+ copysource = False
+ supported_image_types = ['image/png', 'image/gif', 'image/jpeg']
+
+ # don't add links
+ add_header_links = False
+ add_definition_links = False
+
+ def init(self):
+ StandaloneHTMLBuilder.init(self)
+ # the output files for HTML help must be .html only
+ self.out_suffix = '.html'
+
+ def handle_finish(self):
+ self.build_hhx(self, self.outdir, self.config.htmlhelp_basename)
+
+ def build_hhx(self, outdir, outname):
+ self.info('dumping stopword list...')
+ f = open(path.join(outdir, outname+'.stp'), 'w')
+ try:
+ for word in sorted(stopwords):
+ print >>f, word
+ finally:
+ f.close()
+
+ self.info('writing project file...')
+ f = open(path.join(outdir, outname+'.hhp'), 'w')
+ try:
+ f.write(project_template % {'outname': outname,
+ 'title': self.config.html_title,
+ 'version': self.config.version,
+ 'project': self.config.project})
+ if not outdir.endswith(os.sep):
+ outdir += os.sep
+ olen = len(outdir)
+ for root, dirs, files in os.walk(outdir):
+ staticdir = (root == path.join(outdir, '_static'))
+ for fn in files:
+ if (staticdir and not fn.endswith('.js')) or fn.endswith('.html'):
+ print >>f, path.join(root, fn)[olen:].replace(os.sep, '\\')
+ finally:
+ f.close()
+
+ self.info('writing TOC file...')
+ f = open(path.join(outdir, outname+'.hhc'), 'w')
+ try:
+ f.write(contents_header)
+ # special books
+ f.write('
')
+ for subitem in subitems:
+ write_index(subitem[0], subitem[1], [])
+ f.write('
')
+ for (key, group) in index:
+ for title, (refs, subitems) in group:
+ write_index(title, refs, subitems)
+ f.write('
\n')
+ finally:
+ f.close()
diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py
new file mode 100644
index 00000000..916430db
--- /dev/null
+++ b/sphinx/builders/latex.py
@@ -0,0 +1,185 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders.latex
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ LaTeX builder.
+
+ :copyright: 2008 by Georg Brandl.
+ :license: BSD.
+"""
+
+import os
+import shutil
+from os import path
+
+from docutils import nodes
+from docutils.io import FileOutput
+from docutils.utils import new_document
+from docutils.frontend import OptionParser
+
+from sphinx import package_dir, addnodes
+from sphinx.util import SEP, texescape
+from sphinx.builders import Builder
+from sphinx.environment import NoUri
+from sphinx.util.console import bold, darkgreen
+from sphinx.writers.latex import LaTeXWriter
+
+
+class LaTeXBuilder(Builder):
+ """
+ Builds LaTeX output to create PDF.
+ """
+ name = 'latex'
+ supported_image_types = ['application/pdf', 'image/png', 'image/gif',
+ 'image/jpeg']
+
+ def init(self):
+ self.docnames = []
+ self.document_data = []
+ texescape.init()
+
+ def get_outdated_docs(self):
+ return 'all documents' # for now
+
+ def get_target_uri(self, docname, typ=None):
+ if typ == 'token':
+ # token references are always inside production lists and must be
+ # replaced by \token{} in LaTeX
+ return '@token'
+ if docname not in self.docnames:
+ raise NoUri
+ else:
+ return ''
+
+ def init_document_data(self):
+ preliminary_document_data = map(list, self.config.latex_documents)
+ if not preliminary_document_data:
+ self.warn('No "latex_documents" config value found; no documents '
+ 'will be written.')
+ return
+ # assign subdirs to titles
+ self.titles = []
+ for entry in preliminary_document_data:
+ docname = entry[0]
+ if docname not in self.env.all_docs:
+ self.warn('"latex_documents" config value references unknown '
+ 'document %s' % docname)
+ continue
+ self.document_data.append(entry)
+ if docname.endswith(SEP+'index'):
+ docname = docname[:-5]
+ self.titles.append((docname, entry[2]))
+
+ def write(self, *ignored):
+ # first, assemble the "appendix" docs that are in every PDF
+ appendices = []
+ for fname in self.config.latex_appendices:
+ appendices.append(self.env.get_doctree(fname))
+
+ docwriter = LaTeXWriter(self)
+ docsettings = OptionParser(
+ defaults=self.env.settings,
+ components=(docwriter,)).get_default_values()
+
+ self.init_document_data()
+
+ for entry in self.document_data:
+ docname, targetname, title, author, docclass = entry[:5]
+ toctree_only = False
+ if len(entry) > 5:
+ toctree_only = entry[5]
+ destination = FileOutput(
+ destination_path=path.join(self.outdir, targetname),
+ encoding='utf-8')
+ self.info("processing " + targetname + "... ", nonl=1)
+ doctree = self.assemble_doctree(docname, toctree_only,
+ appendices=(docclass == 'manual') and appendices or [])
+ self.post_process_images(doctree)
+ self.info("writing... ", nonl=1)
+ doctree.settings = docsettings
+ doctree.settings.author = author
+ doctree.settings.title = title
+ doctree.settings.docname = docname
+ doctree.settings.docclass = docclass
+ docwriter.write(doctree, destination)
+ self.info("done")
+
+ def assemble_doctree(self, indexfile, toctree_only, appendices):
+ self.docnames = set([indexfile] + appendices)
+ self.info(darkgreen(indexfile) + " ", nonl=1)
+ def process_tree(docname, tree):
+ tree = tree.deepcopy()
+ for toctreenode in tree.traverse(addnodes.toctree):
+ newnodes = []
+ includefiles = map(str, toctreenode['includefiles'])
+ for includefile in includefiles:
+ try:
+ self.info(darkgreen(includefile) + " ", nonl=1)
+ subtree = process_tree(includefile,
+ self.env.get_doctree(includefile))
+ self.docnames.add(includefile)
+ except Exception:
+ self.warn('%s: toctree contains ref to nonexisting file %r' %
+ (docname, includefile))
+ else:
+ sof = addnodes.start_of_file()
+ sof.children = subtree.children
+ newnodes.append(sof)
+ toctreenode.parent.replace(toctreenode, newnodes)
+ return tree
+ tree = self.env.get_doctree(indexfile)
+ if toctree_only:
+ # extract toctree nodes from the tree and put them in a fresh document
+ new_tree = new_document('')
+ new_sect = nodes.section()
+ new_sect += nodes.title(u'', u'')
+ new_tree += new_sect
+ for node in tree.traverse(addnodes.toctree):
+ new_sect += node
+ tree = new_tree
+ largetree = process_tree(indexfile, tree)
+ largetree.extend(appendices)
+ self.info()
+ self.info("resolving references...")
+ self.env.resolve_references(largetree, indexfile, self)
+ # resolve :ref:s to distant tex files -- we can't add a cross-reference,
+ # but append the document name
+ for pendingnode in largetree.traverse(addnodes.pending_xref):
+ docname = pendingnode['refdocname']
+ sectname = pendingnode['refsectname']
+ newnodes = [nodes.emphasis(sectname, sectname)]
+ for subdir, title in self.titles:
+ if docname.startswith(subdir):
+ newnodes.append(nodes.Text(_(' (in '), _(' (in ')))
+ newnodes.append(nodes.emphasis(title, title))
+ newnodes.append(nodes.Text(')', ')'))
+ break
+ else:
+ pass
+ pendingnode.replace_self(newnodes)
+ return largetree
+
+ def finish(self):
+ # copy image files
+ if self.images:
+ self.info(bold('copying images...'), nonl=1)
+ for src, dest in self.images.iteritems():
+ self.info(' '+src, nonl=1)
+ shutil.copyfile(path.join(self.srcdir, src),
+ path.join(self.outdir, dest))
+ self.info()
+
+ # the logo is handled differently
+ if self.config.latex_logo:
+ logobase = path.basename(self.config.latex_logo)
+ shutil.copyfile(path.join(self.confdir, self.config.latex_logo),
+ path.join(self.outdir, logobase))
+
+ self.info(bold('copying TeX support files... '), nonl=True)
+ staticdirname = path.join(package_dir, 'texinputs')
+ for filename in os.listdir(staticdirname):
+ if not filename.startswith('.'):
+ shutil.copyfile(path.join(staticdirname, filename),
+ path.join(self.outdir, filename))
+ self.info('done')
diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py
new file mode 100644
index 00000000..9dbfc913
--- /dev/null
+++ b/sphinx/builders/linkcheck.py
@@ -0,0 +1,130 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders.linkcheck
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ The CheckExternalLinksBuilder class.
+
+ :copyright: 2008 by Georg Brandl, Thomas Lamb.
+ :license: BSD.
+"""
+
+import socket
+from os import path
+from urllib2 import build_opener, HTTPError
+
+from docutils import nodes
+
+from sphinx.builders import Builder
+from sphinx.util.console import purple, red, darkgreen
+
+# create an opener that will simulate a browser user-agent
+opener = build_opener()
+opener.addheaders = [('User-agent', 'Mozilla/5.0')]
+
+
+class CheckExternalLinksBuilder(Builder):
+ """
+ Checks for broken external links.
+ """
+ name = 'linkcheck'
+
+ def init(self):
+ self.good = set()
+ self.broken = {}
+ self.redirected = {}
+ # set a timeout for non-responding servers
+ socket.setdefaulttimeout(5.0)
+ # create output file
+ open(path.join(self.outdir, 'output.txt'), 'w').close()
+
+ def get_target_uri(self, docname, typ=None):
+ return ''
+
+ def get_outdated_docs(self):
+ return self.env.found_docs
+
+ def prepare_writing(self, docnames):
+ return
+
+ def write_doc(self, docname, doctree):
+ self.info()
+ for node in doctree.traverse(nodes.reference):
+ try:
+ self.check(node, docname)
+ except KeyError:
+ continue
+
+ def check(self, node, docname):
+ uri = node['refuri']
+
+ if '#' in uri:
+ uri = uri.split('#')[0]
+
+ if uri in self.good:
+ return
+
+ lineno = None
+ while lineno is None and node:
+ node = node.parent
+ lineno = node.line
+
+ if uri[0:5] == 'http:' or uri[0:6] == 'https:':
+ self.info(uri, nonl=1)
+
+ if uri in self.broken:
+ (r, s) = self.broken[uri]
+ elif uri in self.redirected:
+ (r, s) = self.redirected[uri]
+ else:
+ (r, s) = self.resolve(uri)
+
+ if r == 0:
+ self.info(' - ' + darkgreen('working'))
+ self.good.add(uri)
+ elif r == 2:
+ self.info(' - ' + red('broken: ') + s)
+ self.write_entry('broken', docname, lineno, uri + ': ' + s)
+ self.broken[uri] = (r, s)
+ if self.app.quiet:
+ self.warn('%s:%s: broken link: %s' % (docname, lineno, uri))
+ else:
+ self.info(' - ' + purple('redirected') + ' to ' + s)
+ self.write_entry('redirected', docname, lineno, uri + ' to ' + s)
+ self.redirected[uri] = (r, s)
+ elif len(uri) == 0 or uri[0:7] == 'mailto:' or uri[0:4] == 'ftp:':
+ return
+ else:
+ self.warn(uri + ' - ' + red('malformed!'))
+ self.write_entry('malformed', docname, lineno, uri)
+ if self.app.quiet:
+ self.warn('%s:%s: malformed link: %s' % (docname, lineno, uri))
+ self.app.statuscode = 1
+
+ if self.broken:
+ self.app.statuscode = 1
+
+ def write_entry(self, what, docname, line, uri):
+ output = open(path.join(self.outdir, 'output.txt'), 'a')
+ output.write("%s:%s: [%s] %s\n" % (self.env.doc2path(docname, None),
+ line, what, uri))
+ output.close()
+
+ def resolve(self, uri):
+ try:
+ f = opener.open(uri)
+ f.close()
+ except HTTPError, err:
+ #if err.code == 403 and uri.startswith('http://en.wikipedia.org/'):
+ # # Wikipedia blocks requests from urllib User-Agent
+ # return (0, 0)
+ return (2, str(err))
+ except Exception, err:
+ return (2, str(err))
+ if f.url.rstrip('/') == uri.rstrip('/'):
+ return (0, 0)
+ else:
+ return (1, f.url)
+
+ def finish(self):
+ return
diff --git a/sphinx/builders/text.py b/sphinx/builders/text.py
new file mode 100644
index 00000000..c6f232e8
--- /dev/null
+++ b/sphinx/builders/text.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders.text
+ ~~~~~~~~~~~~~~~~~~~~
+
+ Plain-text Sphinx builder.
+
+ :copyright: 2008 by Georg Brandl.
+ :license: BSD.
+"""
+
+import codecs
+from os import path
+
+from docutils.io import StringOutput
+
+from sphinx.util import ensuredir, os_path
+from sphinx.builders import Builder
+from sphinx.writers.text import TextWriter
+
+
+class TextBuilder(Builder):
+ name = 'text'
+ out_suffix = '.txt'
+
+ def init(self):
+ pass
+
+ def get_outdated_docs(self):
+ for docname in self.env.found_docs:
+ if docname not in self.env.all_docs:
+ yield docname
+ continue
+ targetname = self.env.doc2path(docname, self.outdir, self.out_suffix)
+ try:
+ targetmtime = path.getmtime(targetname)
+ except Exception:
+ targetmtime = 0
+ try:
+ srcmtime = path.getmtime(self.env.doc2path(docname))
+ if srcmtime > targetmtime:
+ yield docname
+ except EnvironmentError:
+ # source doesn't exist anymore
+ pass
+
+ def get_target_uri(self, docname, typ=None):
+ return ''
+
+ def prepare_writing(self, docnames):
+ self.writer = TextWriter(self)
+
+ def write_doc(self, docname, doctree):
+ destination = StringOutput(encoding='utf-8')
+ self.writer.write(doctree, destination)
+ outfilename = path.join(self.outdir, os_path(docname) + self.out_suffix)
+ ensuredir(path.dirname(outfilename)) # normally different from self.outdir
+ try:
+ f = codecs.open(outfilename, 'w', 'utf-8')
+ try:
+ f.write(self.writer.output)
+ finally:
+ f.close()
+ except (IOError, OSError), err:
+ self.warn("Error writing file %s: %s" % (outfilename, err))
+
+ def finish(self):
+ pass
diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py
index 0c034e0d..5d9607c9 100644
--- a/sphinx/ext/intersphinx.py
+++ b/sphinx/ext/intersphinx.py
@@ -31,7 +31,7 @@ from os import path
from docutils import nodes
-from sphinx.builder import INVENTORY_FILENAME
+from sphinx.builders import INVENTORY_FILENAME
def fetch_inventory(app, uri, inv):
diff --git a/sphinx/htmlhelp.py b/sphinx/htmlhelp.py
deleted file mode 100644
index 4cc68bc9..00000000
--- a/sphinx/htmlhelp.py
+++ /dev/null
@@ -1,220 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- sphinx.htmlhelp
- ~~~~~~~~~~~~~~~
-
- Build HTML help support files.
- Adapted from the original Doc/tools/prechm.py.
-
- :copyright: 2007-2008 by Georg Brandl.
- :license: BSD.
-"""
-
-import os
-import cgi
-from os import path
-
-from docutils import nodes
-
-from sphinx import addnodes
-
-# Project file (*.hhp) template. 'outname' is the file basename (like
-# the pythlp in pythlp.hhp); 'version' is the doc version number (like
-# the 2.2 in Python 2.2).
-# The magical numbers in the long line under [WINDOWS] set most of the
-# user-visible features (visible buttons, tabs, etc).
-# About 0x10384e: This defines the buttons in the help viewer. The
-# following defns are taken from htmlhelp.h. Not all possibilities
-# actually work, and not all those that work are available from the Help
-# Workshop GUI. In particular, the Zoom/Font button works and is not
-# available from the GUI. The ones we're using are marked with 'x':
-#
-# 0x000002 Hide/Show x
-# 0x000004 Back x
-# 0x000008 Forward x
-# 0x000010 Stop
-# 0x000020 Refresh
-# 0x000040 Home x
-# 0x000080 Forward
-# 0x000100 Back
-# 0x000200 Notes
-# 0x000400 Contents
-# 0x000800 Locate x
-# 0x001000 Options x
-# 0x002000 Print x
-# 0x004000 Index
-# 0x008000 Search
-# 0x010000 History
-# 0x020000 Favorites
-# 0x040000 Jump 1
-# 0x080000 Jump 2
-# 0x100000 Zoom/Font x
-# 0x200000 TOC Next
-# 0x400000 TOC Prev
-
-project_template = '''\
-[OPTIONS]
-Binary TOC=Yes
-Binary Index=No
-Compiled file=%(outname)s.chm
-Contents file=%(outname)s.hhc
-Default Window=%(outname)s
-Default topic=index.html
-Display compile progress=No
-Full text search stop list file=%(outname)s.stp
-Full-text search=Yes
-Index file=%(outname)s.hhk
-Language=0x409
-Title=%(title)s
-
-[WINDOWS]
-%(outname)s="%(title)s","%(outname)s.hhc","%(outname)s.hhk",\
-"index.html","index.html",,,,,0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0
-
-[FILES]
-'''
-
-contents_header = '''\
-
-
-
-
-
-
-
-
-'''
-
-contents_footer = '''\
-
-'''
-
-object_sitemap = '''\
-
-'''
-
-# List of words the full text search facility shouldn't index. This
-# becomes file outname.stp. Note that this list must be pretty small!
-# Different versions of the MS docs claim the file has a maximum size of
-# 256 or 512 bytes (including \r\n at the end of each line).
-# Note that "and", "or", "not" and "near" are operators in the search
-# language, so no point indexing them even if we wanted to.
-stopwords = """
-a and are as at
-be but by
-for
-if in into is it
-near no not
-of on or
-such
-that the their then there these they this to
-was will with
-""".split()
-
-
-def build_hhx(builder, outdir, outname):
- builder.info('dumping stopword list...')
- f = open(path.join(outdir, outname+'.stp'), 'w')
- try:
- for word in sorted(stopwords):
- print >>f, word
- finally:
- f.close()
-
- builder.info('writing project file...')
- f = open(path.join(outdir, outname+'.hhp'), 'w')
- try:
- f.write(project_template % {'outname': outname,
- 'title': builder.config.html_title,
- 'version': builder.config.version,
- 'project': builder.config.project})
- if not outdir.endswith(os.sep):
- outdir += os.sep
- olen = len(outdir)
- for root, dirs, files in os.walk(outdir):
- staticdir = (root == path.join(outdir, '_static'))
- for fn in files:
- if (staticdir and not fn.endswith('.js')) or fn.endswith('.html'):
- print >>f, path.join(root, fn)[olen:].replace(os.sep, '\\')
- finally:
- f.close()
-
- builder.info('writing TOC file...')
- f = open(path.join(outdir, outname+'.hhc'), 'w')
- try:
- f.write(contents_header)
- # special books
- f.write('
')
+
+ def visit_compact_paragraph(self, node):
+ pass
+ def depart_compact_paragraph(self, node):
+ pass
+
+ def visit_highlightlang(self, node):
+ self.highlightlang = node['lang']
+ self.highlightlinenothreshold = node['linenothreshold']
+ def depart_highlightlang(self, node):
+ pass
+
+ # overwritten
+ def visit_image(self, node):
+ olduri = node['uri']
+ # rewrite the URI if the environment knows about it
+ if olduri in self.builder.images:
+ node['uri'] = posixpath.join(self.builder.imgpath,
+ self.builder.images[olduri])
+
+ if node.has_key('scale'):
+ if Image and not (node.has_key('width')
+ and node.has_key('height')):
+ try:
+ im = Image.open(os.path.join(self.builder.srcdir,
+ olduri))
+ except (IOError, # Source image can't be found or opened
+ UnicodeError): # PIL doesn't like Unicode paths.
+ print olduri
+ pass
+ else:
+ if not node.has_key('width'):
+ node['width'] = str(im.size[0])
+ if not node.has_key('height'):
+ node['height'] = str(im.size[1])
+ del im
+ BaseTranslator.visit_image(self, node)
+
+ def visit_toctree(self, node):
+ # this only happens when formatting a toc from env.tocs -- in this
+ # case we don't want to include the subtree
+ raise nodes.SkipNode
+
+ def visit_index(self, node):
+ raise nodes.SkipNode
+
+ def visit_tabular_col_spec(self, node):
+ raise nodes.SkipNode
+
+ def visit_glossary(self, node):
+ pass
+ def depart_glossary(self, node):
+ pass
+
+ def visit_acks(self, node):
+ pass
+ def depart_acks(self, node):
+ pass
+
+ def visit_module(self, node):
+ pass
+ def depart_module(self, node):
+ pass
+
+ def bulk_text_processor(self, text):
+ return text
+
+ # overwritten
+ def visit_Text(self, node):
+ text = node.astext()
+ encoded = self.encode(text)
+ if self.protect_literal_text:
+ # moved here from base class's visit_literal to support
+ # more formatting in literal nodes
+ for token in self.words_and_spaces.findall(encoded):
+ if token.strip():
+ # protect literal text from line wrapping
+ self.body.append('%s' % token)
+ elif token in ' \n':
+ # allow breaks at whitespace
+ self.body.append(token)
+ else:
+ # protect runs of multiple spaces; the last one can wrap
+ self.body.append(' ' * (len(token)-1) + ' ')
+ else:
+ if self.in_mailto and self.settings.cloak_email_addresses:
+ encoded = self.cloak_email(encoded)
+ else:
+ encoded = self.bulk_text_processor(encoded)
+ self.body.append(encoded)
+
+ # these are all for docutils 0.5 compatibility
+
+ def visit_note(self, node):
+ self.visit_admonition(node, 'note')
+ def depart_note(self, node):
+ self.depart_admonition(node)
+
+ def visit_warning(self, node):
+ self.visit_admonition(node, 'warning')
+ def depart_warning(self, node):
+ self.depart_admonition(node)
+
+ def visit_attention(self, node):
+ self.visit_admonition(node, 'attention')
+
+ def depart_attention(self, node):
+ self.depart_admonition()
+
+ def visit_caution(self, node):
+ self.visit_admonition(node, 'caution')
+ def depart_caution(self, node):
+ self.depart_admonition()
+
+ def visit_danger(self, node):
+ self.visit_admonition(node, 'danger')
+ def depart_danger(self, node):
+ self.depart_admonition()
+
+ def visit_error(self, node):
+ self.visit_admonition(node, 'error')
+ def depart_error(self, node):
+ self.depart_admonition()
+
+ def visit_hint(self, node):
+ self.visit_admonition(node, 'hint')
+ def depart_hint(self, node):
+ self.depart_admonition()
+
+ def visit_important(self, node):
+ self.visit_admonition(node, 'important')
+ def depart_important(self, node):
+ self.depart_admonition()
+
+ def visit_tip(self, node):
+ self.visit_admonition(node, 'tip')
+ def depart_tip(self, node):
+ self.depart_admonition()
+
+ # these are only handled specially in the SmartyPantsHTMLTranslator
+ def visit_literal_emphasis(self, node):
+ return self.visit_emphasis(node)
+ def depart_literal_emphasis(self, node):
+ return self.depart_emphasis(node)
+
+ def depart_title(self, node):
+ close_tag = self.context[-1]
+ if self.builder.add_header_links and \
+ (close_tag.startswith('\u00B6' %
+ _('Permalink to this headline'))
+ BaseTranslator.depart_title(self, node)
+
+ def unknown_visit(self, node):
+ raise NotImplementedError('Unknown node: ' + node.__class__.__name__)
+
+
+class SmartyPantsHTMLTranslator(HTMLTranslator):
+ """
+ Handle ordinary text via smartypants, converting quotes and dashes
+ to the correct entities.
+ """
+
+ def __init__(self, *args, **kwds):
+ self.no_smarty = 0
+ HTMLTranslator.__init__(self, *args, **kwds)
+
+ def visit_literal(self, node):
+ self.no_smarty += 1
+ try:
+ # this raises SkipNode
+ HTMLTranslator.visit_literal(self, node)
+ finally:
+ self.no_smarty -= 1
+
+ def visit_literal_emphasis(self, node):
+ self.no_smarty += 1
+ self.visit_emphasis(node)
+
+ def depart_literal_emphasis(self, node):
+ self.depart_emphasis(node)
+ self.no_smarty -= 1
+
+ def visit_desc_signature(self, node):
+ self.no_smarty += 1
+ HTMLTranslator.visit_desc_signature(self, node)
+
+ def depart_desc_signature(self, node):
+ self.no_smarty -= 1
+ HTMLTranslator.depart_desc_signature(self, node)
+
+ def visit_productionlist(self, node):
+ self.no_smarty += 1
+ try:
+ HTMLTranslator.visit_productionlist(self, node)
+ finally:
+ self.no_smarty -= 1
+
+ def visit_option(self, node):
+ self.no_smarty += 1
+ HTMLTranslator.visit_option(self, node)
+ def depart_option(self, node):
+ self.no_smarty -= 1
+ HTMLTranslator.depart_option(self, node)
+
+ def bulk_text_processor(self, text):
+ if self.no_smarty <= 0:
+ return sphinx_smarty_pants(text)
+ return text
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
new file mode 100644
index 00000000..bfa9c120
--- /dev/null
+++ b/sphinx/writers/latex.py
@@ -0,0 +1,1185 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.writers.latex
+ ~~~~~~~~~~~~~~~~~~~~
+
+ Custom docutils writer for LaTeX.
+
+ Much of this code is adapted from Dave Kuhlman's "docpy" writer from his
+ docutils sandbox.
+
+ :copyright: 2007-2008 by Georg Brandl, Dave Kuhlman.
+ :license: BSD.
+"""
+
+import re
+import sys
+from os import path
+
+from docutils import nodes, writers
+from docutils.writers.latex2e import Babel
+
+from sphinx import addnodes
+from sphinx import highlighting
+from sphinx.locale import admonitionlabels, versionlabels
+from sphinx.util import ustrftime
+from sphinx.util.texescape import tex_escape_map
+from sphinx.util.smartypants import educateQuotesLatex
+
+HEADER = r'''%% Generated by Sphinx.
+\documentclass[%(papersize)s,%(pointsize)s%(classoptions)s]{%(docclass)s}
+%(inputenc)s
+%(fontenc)s
+%(babel)s
+%(fontpkg)s
+%(fncychap)s
+\usepackage{sphinx}
+%(preamble)s
+
+\title{%(title)s}
+\date{%(date)s}
+\release{%(release)s}
+\author{%(author)s}
+\newcommand{\sphinxlogo}{%(logo)s}
+\renewcommand{\releasename}{%(releasename)s}
+%(makeindex)s
+%(makemodindex)s
+'''
+
+BEGIN_DOC = r'''
+\begin{document}
+%(shorthandoff)s
+%(maketitle)s
+%(tableofcontents)s
+'''
+
+FOOTER = r'''
+%(footer)s
+\renewcommand{\indexname}{%(modindexname)s}
+%(printmodindex)s
+\renewcommand{\indexname}{%(indexname)s}
+%(printindex)s
+\end{document}
+'''
+
+
+class LaTeXWriter(writers.Writer):
+
+ supported = ('sphinxlatex',)
+
+ settings_spec = ('LaTeX writer options', '', (
+ ('Document name', ['--docname'], {'default': ''}),
+ ('Document class', ['--docclass'], {'default': 'manual'}),
+ ('Author', ['--author'], {'default': ''}),
+ ))
+ settings_defaults = {}
+
+ output = None
+
+ def __init__(self, builder):
+ writers.Writer.__init__(self)
+ self.builder = builder
+
+ def translate(self):
+ visitor = LaTeXTranslator(self.document, self.builder)
+ self.document.walkabout(visitor)
+ self.output = visitor.astext()
+
+
+# Helper classes
+
+class ExtBabel(Babel):
+ def get_shorthandoff(self):
+ shortlang = self.language.split('_')[0]
+ if shortlang in ('de', 'sl', 'pt', 'es', 'nl', 'pl'):
+ return '\\shorthandoff{"}'
+ return ''
+
+ _ISO639_TO_BABEL = Babel._ISO639_TO_BABEL.copy()
+ _ISO639_TO_BABEL['sl'] = 'slovene'
+
+
+class Table(object):
+ def __init__(self):
+ self.col = 0
+ self.colcount = 0
+ self.colspec = None
+ self.had_head = False
+ self.has_verbatim = False
+ self.caption = None
+
+
+class Desc(object):
+ def __init__(self, node):
+ self.env = LaTeXTranslator.desc_map.get(node['desctype'], 'describe')
+ self.type = self.cls = self.name = self.params = self.annotation = ''
+ self.count = 0
+
+
+class LaTeXTranslator(nodes.NodeVisitor):
+ sectionnames = ["part", "chapter", "section", "subsection",
+ "subsubsection", "paragraph", "subparagraph"]
+
+ ignore_missing_images = False
+
+ default_elements = {
+ 'docclass': 'manual',
+ 'papersize': 'letterpaper',
+ 'pointsize': '10pt',
+ 'classoptions': '',
+ 'inputenc': '\\usepackage[utf8]{inputenc}',
+ 'fontenc': '\\usepackage[T1]{fontenc}',
+ 'babel': '\\usepackage{babel}',
+ 'fontpkg': '\\usepackage{times}',
+ 'fncychap': '\\usepackage[Bjarne]{fncychap}',
+ 'preamble': '',
+ 'title': '',
+ 'date': '',
+ 'release': '',
+ 'author': '',
+ 'logo': '',
+ 'releasename': 'Release',
+ 'makeindex': '\\makeindex',
+ 'makemodindex': '\\makemodindex',
+ 'shorthandoff': '',
+ 'maketitle': '\\maketitle',
+ 'tableofcontents': '\\tableofcontents',
+ 'footer': '',
+ 'printmodindex': '\\printmodindex',
+ 'printindex': '\\printindex',
+ }
+
+ def __init__(self, document, builder):
+ nodes.NodeVisitor.__init__(self, document)
+ self.builder = builder
+ self.body = []
+
+ # sort out some elements
+ papersize = builder.config.latex_paper_size + 'paper'
+ if papersize == 'paper': # e.g. command line "-D latex_paper_size="
+ papersize = 'letterpaper'
+
+ self.elements = self.default_elements.copy()
+ self.elements.update({
+ 'docclass': document.settings.docclass,
+ 'papersize': papersize,
+ 'pointsize': builder.config.latex_font_size,
+ # if empty, the title is set to the first section title
+ 'title': document.settings.title,
+ 'date': ustrftime(builder.config.today_fmt or _('%B %d, %Y')),
+ 'release': builder.config.release,
+ 'author': document.settings.author,
+ 'releasename': _('Release'),
+ 'preamble': builder.config.latex_preamble,
+ 'modindexname': _('Module Index'),
+ 'indexname': _('Index'),
+ })
+ if builder.config.latex_logo:
+ self.elements['logo'] = '\\includegraphics{%s}\\par' % \
+ path.basename(builder.config.latex_logo)
+ if builder.config.language:
+ babel = ExtBabel(builder.config.language)
+ lang = babel.get_language()
+ if lang:
+ self.elements['classoptions'] += ',' + babel.get_language()
+ else:
+ self.builder.warn('no Babel option known for language %r' %
+ builder.config.language)
+ self.elements['shorthandoff'] = babel.get_shorthandoff()
+ self.elements['fncychap'] = '\\usepackage[Sonny]{fncychap}'
+ else:
+ self.elements['classoptions'] += ',english'
+ if not builder.config.latex_use_modindex:
+ self.elements['makemodindex'] = ''
+ self.elements['printmodindex'] = ''
+ # allow the user to override them all
+ self.elements.update(builder.config.latex_elements)
+
+ self.highlighter = highlighting.PygmentsBridge(
+ 'latex', builder.config.pygments_style)
+ self.context = []
+ self.descstack = []
+ self.bibitems = []
+ self.table = None
+ self.next_table_colspec = None
+ self.highlightlang = builder.config.highlight_language
+ self.highlightlinenothreshold = sys.maxint
+ self.written_ids = set()
+ self.footnotestack = []
+ if self.elements['docclass'] == 'manual':
+ if builder.config.latex_use_parts:
+ self.top_sectionlevel = 0
+ else:
+ self.top_sectionlevel = 1
+ else:
+ self.top_sectionlevel = 2
+ self.next_section_target = None
+ # flags
+ self.verbatim = None
+ self.in_title = 0
+ self.in_production_list = 0
+ self.first_document = 1
+ self.this_is_the_title = 1
+ self.literal_whitespace = 0
+ self.no_contractions = 0
+
+ def astext(self):
+ return (HEADER % self.elements + self.highlighter.get_stylesheet() +
+ u''.join(self.body) + FOOTER % self.elements)
+
+ def visit_document(self, node):
+ self.footnotestack.append(self.collect_footnotes(node))
+ if self.first_document == 1:
+ # the first document is all the regular content ...
+ self.body.append(BEGIN_DOC % self.elements)
+ self.first_document = 0
+ elif self.first_document == 0:
+ # ... and all others are the appendices
+ self.body.append('\n\\appendix\n')
+ self.first_document = -1
+ # "- 1" because the level is increased before the title is visited
+ self.sectionlevel = self.top_sectionlevel - 1
+ def depart_document(self, node):
+ if self.bibitems:
+ widest_label = ""
+ for bi in self.bibitems:
+ if len(widest_label) < len(bi[0]):
+ widest_label = bi[0]
+ self.body.append('\n\\begin{thebibliography}{%s}\n' % widest_label)
+ for bi in self.bibitems:
+ # cite_key: underscores must not be escaped
+ cite_key = bi[0].replace(r"\_", "_")
+ self.body.append('\\bibitem[%s]{%s}{%s}\n' % (bi[0], cite_key, bi[1]))
+ self.body.append('\\end{thebibliography}\n')
+ self.bibitems = []
+
+ def visit_start_of_file(self, node):
+ # This marks the begin of a new file; therefore the current module and
+ # class must be reset
+ self.body.append('\n\\resetcurrentobjects\n')
+ # and also, new footnotes
+ self.footnotestack.append(self.collect_footnotes(node))
+
+ def collect_footnotes(self, node):
+ fnotes = {}
+ def footnotes_under(n):
+ if isinstance(n, nodes.footnote):
+ yield n
+ else:
+ for c in n.children:
+ if isinstance(c, addnodes.start_of_file):
+ continue
+ for k in footnotes_under(c):
+ yield k
+ for fn in footnotes_under(node):
+ num = fn.children[0].astext().strip()
+ fnotes[num] = fn
+ fn.parent.remove(fn)
+ return fnotes
+
+ def depart_start_of_file(self, node):
+ self.footnotestack.pop()
+
+ def visit_highlightlang(self, node):
+ self.highlightlang = node['lang']
+ self.highlightlinenothreshold = node['linenothreshold']
+ raise nodes.SkipNode
+
+ def visit_section(self, node):
+ if not self.this_is_the_title:
+ self.sectionlevel += 1
+ self.body.append('\n\n')
+ if self.next_section_target:
+ self.body.append(r'\hypertarget{%s}{}' % self.next_section_target)
+ self.next_section_target = None
+ #if node.get('ids'):
+ # for id in node['ids']:
+ # if id not in self.written_ids:
+ # self.body.append(r'\hypertarget{%s}{}' % id)
+ # self.written_ids.add(id)
+ def depart_section(self, node):
+ self.sectionlevel = max(self.sectionlevel - 1, self.top_sectionlevel - 1)
+
+ def visit_problematic(self, node):
+ self.body.append(r'{\color{red}\bfseries{}')
+ def depart_problematic(self, node):
+ self.body.append('}')
+
+ def visit_topic(self, node):
+ self.body.append('\\setbox0\\vbox{\n'
+ '\\begin{minipage}{0.95\\textwidth}\n')
+ def depart_topic(self, node):
+ self.body.append('\\end{minipage}}\n'
+ '\\begin{center}\\setlength{\\fboxsep}{5pt}'
+ '\\shadowbox{\\box0}\\end{center}\n')
+ visit_sidebar = visit_topic
+ depart_sidebar = depart_topic
+
+ def visit_glossary(self, node):
+ pass
+ def depart_glossary(self, node):
+ pass
+
+ def visit_productionlist(self, node):
+ self.body.append('\n\n\\begin{productionlist}\n')
+ self.in_production_list = 1
+ def depart_productionlist(self, node):
+ self.body.append('\\end{productionlist}\n\n')
+ self.in_production_list = 0
+
+ def visit_production(self, node):
+ if node['tokenname']:
+ self.body.append('\\production{%s}{' % self.encode(node['tokenname']))
+ else:
+ self.body.append('\\productioncont{')
+ def depart_production(self, node):
+ self.body.append('}\n')
+
+ def visit_transition(self, node):
+ self.body.append('\n\n\\bigskip\\hrule{}\\bigskip\n\n')
+ def depart_transition(self, node):
+ pass
+
+ def visit_title(self, node):
+ parent = node.parent
+ if isinstance(parent, addnodes.seealso):
+ # the environment already handles this
+ raise nodes.SkipNode
+ elif self.this_is_the_title:
+ if len(node.children) != 1 and not isinstance(node.children[0], nodes.Text):
+ self.builder.warn('document title is not a single Text node')
+ if not self.elements['title']:
+ # text needs to be escaped since it is inserted into
+ # the output literally
+ self.elements['title'] = node.astext().translate(tex_escape_map)
+ self.this_is_the_title = 0
+ raise nodes.SkipNode
+ elif isinstance(parent, nodes.section):
+ try:
+ self.body.append(r'\%s{' % self.sectionnames[self.sectionlevel])
+ except IndexError:
+ from sphinx.application import SphinxError
+ raise SphinxError('too many nesting section levels for LaTeX, '
+ 'at heading: %s' % node.astext())
+ self.context.append('}\n')
+ elif isinstance(parent, (nodes.topic, nodes.sidebar)):
+ self.body.append(r'\textbf{')
+ self.context.append('}\n\n\medskip\n\n')
+ elif isinstance(parent, nodes.Admonition):
+ self.body.append('{')
+ self.context.append('}\n')
+ elif isinstance(parent, nodes.table):
+ self.table.caption = self.encode(node.astext())
+ raise nodes.SkipNode
+ else:
+ self.builder.warn('encountered title node not in section, topic, '
+ 'table, admonition or sidebar')
+ self.body.append('\\textbf{')
+ self.context.append('}\n')
+ self.in_title = 1
+ def depart_title(self, node):
+ self.in_title = 0
+ self.body.append(self.context.pop())
+
+ def visit_subtitle(self, node):
+ if isinstance(node.parent, nodes.sidebar):
+ self.body.append('~\\\\\n\\textbf{')
+ self.context.append('}\n\\smallskip\n')
+ else:
+ self.context.append('')
+ def depart_subtitle(self, node):
+ self.body.append(self.context.pop())
+
+ desc_map = {
+ 'function' : 'funcdesc',
+ 'class': 'classdesc',
+ 'method': 'methoddesc',
+ 'staticmethod': 'staticmethoddesc',
+ 'exception': 'excdesc',
+ 'data': 'datadesc',
+ 'attribute': 'memberdesc',
+ 'opcode': 'opcodedesc',
+
+ 'cfunction': 'cfuncdesc',
+ 'cmember': 'cmemberdesc',
+ 'cmacro': 'csimplemacrodesc',
+ 'ctype': 'ctypedesc',
+ 'cvar': 'cvardesc',
+
+ 'describe': 'describe',
+ # and all others are 'describe' too
+ }
+
+ def visit_desc(self, node):
+ self.descstack.append(Desc(node))
+ def depart_desc(self, node):
+ d = self.descstack.pop()
+ self.body.append("\\end{%s}\n" % d.env)
+
+ def visit_desc_signature(self, node):
+ d = self.descstack[-1]
+ # reset these for every signature
+ d.type = d.cls = d.name = d.params = ''
+ def depart_desc_signature(self, node):
+ d = self.descstack[-1]
+ d.cls = d.cls.rstrip('.')
+ if node.parent['desctype'] != 'describe' and node['ids']:
+ hyper = '\\hypertarget{%s}{}' % node['ids'][0]
+ else:
+ hyper = ''
+ if d.count == 0:
+ t1 = "\n\n%s\\begin{%s}" % (hyper, d.env)
+ else:
+ t1 = "\n%s\\%sline" % (hyper, d.env[:-4])
+ d.count += 1
+ if d.env in ('funcdesc', 'classdesc', 'excclassdesc'):
+ t2 = "{%s}{%s}" % (d.name, d.params)
+ elif d.env in ('datadesc', 'excdesc', 'csimplemacrodesc'):
+ t2 = "{%s}" % (d.name)
+ elif d.env in ('methoddesc', 'staticmethoddesc'):
+ if d.cls:
+ t2 = "[%s]{%s}{%s}" % (d.cls, d.name, d.params)
+ else:
+ t2 = "{%s}{%s}" % (d.name, d.params)
+ elif d.env == 'memberdesc':
+ if d.cls:
+ t2 = "[%s]{%s}" % (d.cls, d.name)
+ else:
+ t2 = "{%s}" % d.name
+ elif d.env == 'cfuncdesc':
+ if d.cls:
+ # C++ class names
+ d.name = '%s::%s' % (d.cls, d.name)
+ t2 = "{%s}{%s}{%s}" % (d.type, d.name, d.params)
+ elif d.env == 'cmemberdesc':
+ try:
+ type, container = d.type.rsplit(' ', 1)
+ container = container.rstrip('.')
+ except ValueError:
+ container = ''
+ type = d.type
+ t2 = "{%s}{%s}{%s}" % (container, type, d.name)
+ elif d.env == 'cvardesc':
+ t2 = "{%s}{%s}" % (d.type, d.name)
+ elif d.env == 'ctypedesc':
+ t2 = "{%s}" % (d.name)
+ elif d.env == 'opcodedesc':
+ t2 = "{%s}{%s}" % (d.name, d.params)
+ elif d.env == 'describe':
+ t2 = "{%s}" % d.name
+ self.body.append(t1 + t2)
+
+ def visit_desc_type(self, node):
+ d = self.descstack[-1]
+ if d.env == 'describe':
+ d.name += self.encode(node.astext())
+ else:
+ self.descstack[-1].type = self.encode(node.astext().strip())
+ raise nodes.SkipNode
+
+ def visit_desc_name(self, node):
+ d = self.descstack[-1]
+ if d.env == 'describe':
+ d.name += self.encode(node.astext())
+ else:
+ self.descstack[-1].name = self.encode(node.astext().strip())
+ raise nodes.SkipNode
+
+ def visit_desc_addname(self, node):
+ d = self.descstack[-1]
+ if d.env == 'describe':
+ d.name += self.encode(node.astext())
+ else:
+ self.descstack[-1].cls = self.encode(node.astext().strip())
+ raise nodes.SkipNode
+
+ def visit_desc_parameterlist(self, node):
+ d = self.descstack[-1]
+ if d.env == 'describe':
+ d.name += self.encode(node.astext())
+ else:
+ self.descstack[-1].params = self.encode(node.astext().strip())
+ raise nodes.SkipNode
+
+ def visit_desc_annotation(self, node):
+ d = self.descstack[-1]
+ if d.env == 'describe':
+ d.name += self.encode(node.astext())
+ else:
+ self.descstack[-1].annotation = self.encode(node.astext().strip())
+ raise nodes.SkipNode
+
+ def visit_refcount(self, node):
+ self.body.append("\\emph{")
+ def depart_refcount(self, node):
+ self.body.append("}\\\\")
+
+ def visit_desc_content(self, node):
+ if node.children and not isinstance(node.children[0], nodes.paragraph):
+ # avoid empty desc environment which causes a formatting bug
+ self.body.append('~')
+ def depart_desc_content(self, node):
+ pass
+
+ def visit_seealso(self, node):
+ self.body.append("\n\n\\strong{%s:}\n\n" % admonitionlabels['seealso'])
+ def depart_seealso(self, node):
+ self.body.append("\n\n")
+
+ def visit_rubric(self, node):
+ if len(node.children) == 1 and node.children[0].astext() == 'Footnotes':
+ raise nodes.SkipNode
+ self.body.append('\\paragraph{')
+ self.context.append('}\n')
+ def depart_rubric(self, node):
+ self.body.append(self.context.pop())
+
+ def visit_footnote(self, node):
+ pass
+ def depart_footnote(self, node):
+ pass
+
+ def visit_label(self, node):
+ if isinstance(node.parent, nodes.citation):
+ self.bibitems[-1][0] = node.astext()
+ raise nodes.SkipNode
+
+ def visit_tabular_col_spec(self, node):
+ self.next_table_colspec = node['spec']
+ raise nodes.SkipNode
+
+ def visit_table(self, node):
+ if self.table:
+ raise NotImplementedError('Nested tables are not supported.')
+ self.table = Table()
+ self.tablebody = []
+ # Redirect body output until table is finished.
+ self._body = self.body
+ self.body = self.tablebody
+ def depart_table(self, node):
+ self.body = self._body
+ if self.table.caption is not None:
+ self.body.append('\n\\begin{threeparttable}\n'
+ '\\caption{%s}\n' % self.table.caption)
+ if self.table.has_verbatim:
+ self.body.append('\n\\begin{tabular}')
+ else:
+ self.body.append('\n\\begin{tabulary}{\\textwidth}')
+ if self.table.colspec:
+ self.body.append(self.table.colspec)
+ else:
+ if self.table.has_verbatim:
+ colwidth = 0.95 / self.table.colcount
+ colspec = ('p{%.3f\\textwidth}|' % colwidth) * self.table.colcount
+ self.body.append('{|' + colspec + '}\n')
+ else:
+ self.body.append('{|' + ('L|' * self.table.colcount) + '}\n')
+ self.body.extend(self.tablebody)
+ if self.table.has_verbatim:
+ self.body.append('\\end{tabular}\n\n')
+ else:
+ self.body.append('\\end{tabulary}\n\n')
+ if self.table.caption is not None:
+ self.body.append('\\end{threeparttable}\n\n')
+ self.table = None
+ self.tablebody = None
+
+ def visit_colspec(self, node):
+ self.table.colcount += 1
+ def depart_colspec(self, node):
+ pass
+
+ def visit_tgroup(self, node):
+ pass
+ def depart_tgroup(self, node):
+ pass
+
+ def visit_thead(self, node):
+ if self.next_table_colspec:
+ self.table.colspec = '{%s}\n' % self.next_table_colspec
+ self.next_table_colspec = None
+ self.body.append('\\hline\n')
+ self.table.had_head = True
+ def depart_thead(self, node):
+ self.body.append('\\hline\n')
+
+ def visit_tbody(self, node):
+ if not self.table.had_head:
+ self.visit_thead(node)
+ def depart_tbody(self, node):
+ self.body.append('\\hline\n')
+
+ def visit_row(self, node):
+ self.table.col = 0
+ def depart_row(self, node):
+ self.body.append('\\\\\n')
+
+ def visit_entry(self, node):
+ if node.has_key('morerows') or node.has_key('morecols'):
+ raise NotImplementedError('Column or row spanning cells are '
+ 'not implemented.')
+ if self.table.col > 0:
+ self.body.append(' & ')
+ self.table.col += 1
+ if isinstance(node.parent.parent, nodes.thead):
+ self.body.append('\\textbf{')
+ self.context.append('}')
+ else:
+ self.context.append('')
+ def depart_entry(self, node):
+ self.body.append(self.context.pop()) # header
+
+ def visit_acks(self, node):
+ # this is a list in the source, but should be rendered as a
+ # comma-separated list here
+ self.body.append('\n\n')
+ self.body.append(', '.join(n.astext() for n in node.children[0].children) + '.')
+ self.body.append('\n\n')
+ raise nodes.SkipNode
+
+ def visit_bullet_list(self, node):
+ self.body.append('\\begin{itemize}\n' )
+ def depart_bullet_list(self, node):
+ self.body.append('\\end{itemize}\n' )
+
+ def visit_enumerated_list(self, node):
+ self.body.append('\\begin{enumerate}\n' )
+ def depart_enumerated_list(self, node):
+ self.body.append('\\end{enumerate}\n' )
+
+ def visit_list_item(self, node):
+ # Append "{}" in case the next character is "[", which would break
+ # LaTeX's list environment (no numbering and the "[" is not printed).
+ self.body.append(r'\item {} ')
+ def depart_list_item(self, node):
+ self.body.append('\n')
+
+ def visit_definition_list(self, node):
+ self.body.append('\\begin{description}\n')
+ def depart_definition_list(self, node):
+ self.body.append('\\end{description}\n')
+
+ def visit_definition_list_item(self, node):
+ pass
+ def depart_definition_list_item(self, node):
+ pass
+
+ def visit_term(self, node):
+ ctx = ']'
+ if node.has_key('ids') and node['ids']:
+ ctx += '\\hypertarget{%s}{}' % node['ids'][0]
+ self.body.append('\\item[')
+ self.context.append(ctx)
+ def depart_term(self, node):
+ self.body.append(self.context.pop())
+
+ def visit_classifier(self, node):
+ self.body.append('{[}')
+ def depart_classifier(self, node):
+ self.body.append('{]}')
+
+ def visit_definition(self, node):
+ pass
+ def depart_definition(self, node):
+ self.body.append('\n')
+
+ def visit_field_list(self, node):
+ self.body.append('\\begin{quote}\\begin{description}\n')
+ def depart_field_list(self, node):
+ self.body.append('\\end{description}\\end{quote}\n')
+
+ def visit_field(self, node):
+ pass
+ def depart_field(self, node):
+ pass
+
+ visit_field_name = visit_term
+ depart_field_name = depart_term
+
+ visit_field_body = visit_definition
+ depart_field_body = depart_definition
+
+ def visit_paragraph(self, node):
+ self.body.append('\n')
+ def depart_paragraph(self, node):
+ self.body.append('\n')
+
+ def visit_centered(self, node):
+ self.body.append('\n\\begin{centering}')
+ def depart_centered(self, node):
+ self.body.append('\n\\end{centering}')
+
+ def visit_module(self, node):
+ modname = node['modname']
+ self.body.append('\n\\declaremodule[%s]{}{%s}' % (modname.replace('_', ''),
+ self.encode(modname)))
+ self.body.append('\n\\modulesynopsis{%s}' % self.encode(node['synopsis']))
+ if node.has_key('platform'):
+ self.body.append('\\platform{%s}' % self.encode(node['platform']))
+ def depart_module(self, node):
+ pass
+
+ def latex_image_length(self, width_str):
+ match = re.match('(\d*\.?\d*)\s*(\S*)', width_str)
+ if not match:
+ # fallback
+ return width_str
+ res = width_str
+ amount, unit = match.groups()[:2]
+ if not unit or unit == "px":
+ # pixels: let LaTeX alone
+ return None
+ elif unit == "%":
+ res = "%.3f\\linewidth" % (float(amount) / 100.0)
+ return res
+
+ def visit_image(self, node):
+ attrs = node.attributes
+ pre = [] # in reverse order
+ post = []
+ include_graphics_options = []
+ inline = isinstance(node.parent, nodes.TextElement)
+ if attrs.has_key('scale'):
+ # Could also be done with ``scale`` option to
+ # ``\includegraphics``; doing it this way for consistency.
+ pre.append('\\scalebox{%f}{' % (attrs['scale'] / 100.0,))
+ post.append('}')
+ if attrs.has_key('width'):
+ w = self.latex_image_length(attrs['width'])
+ if w:
+ include_graphics_options.append('width=%s' % w)
+ if attrs.has_key('height'):
+ h = self.latex_image_length(attrs['height'])
+ include_graphics_options.append('height=%s' % h)
+ if attrs.has_key('align'):
+ align_prepost = {
+ # By default latex aligns the top of an image.
+ (1, 'top'): ('', ''),
+ (1, 'middle'): ('\\raisebox{-0.5\\height}{', '}'),
+ (1, 'bottom'): ('\\raisebox{-\\height}{', '}'),
+ (0, 'center'): ('{\\hfill', '\\hfill}'),
+ # These 2 don't exactly do the right thing. The image should
+ # be floated alongside the paragraph. See
+ # http://www.w3.org/TR/html4/struct/objects.html#adef-align-IMG
+ (0, 'left'): ('{', '\\hfill}'),
+ (0, 'right'): ('{\\hfill', '}'),}
+ try:
+ pre.append(align_prepost[inline, attrs['align']][0])
+ post.append(align_prepost[inline, attrs['align']][1])
+ except KeyError:
+ pass # XXX complain here?
+ if not inline:
+ pre.append('\n')
+ post.append('\n')
+ pre.reverse()
+ if node['uri'] in self.builder.images:
+ uri = self.builder.images[node['uri']]
+ else:
+ # missing image!
+ if self.ignore_missing_images:
+ return
+ uri = node['uri']
+ if uri.find('://') != -1:
+ # ignore remote images
+ return
+ self.body.extend(pre)
+ options = ''
+ if include_graphics_options:
+ options = '[%s]' % ','.join(include_graphics_options)
+ self.body.append('\\includegraphics%s{%s}' % (options, uri))
+ self.body.extend(post)
+ def depart_image(self, node):
+ pass
+
+ def visit_figure(self, node):
+ if (not node.attributes.has_key('align') or
+ node.attributes['align'] == 'center'):
+ # centering does not add vertical space like center.
+ align = '\n\\centering'
+ align_end = ''
+ else:
+ # TODO non vertical space for other alignments.
+ align = '\\begin{flush%s}' % node.attributes['align']
+ align_end = '\\end{flush%s}' % node.attributes['align']
+ self.body.append('\\begin{figure}[htbp]%s\n' % align)
+ self.context.append('%s\\end{figure}\n' % align_end)
+ def depart_figure(self, node):
+ self.body.append(self.context.pop())
+
+ def visit_caption(self, node):
+ self.body.append('\\caption{')
+ def depart_caption(self, node):
+ self.body.append('}')
+
+ def visit_legend(self, node):
+ self.body.append('{\\small ')
+ def depart_legend(self, node):
+ self.body.append('}')
+
+ def visit_admonition(self, node):
+ self.body.append('\n\\begin{notice}{note}')
+ def depart_admonition(self, node):
+ self.body.append('\\end{notice}\n')
+
+ def _make_visit_admonition(name):
+ def visit_admonition(self, node):
+ self.body.append('\n\\begin{notice}{%s}{%s:}' %
+ (name, admonitionlabels[name]))
+ return visit_admonition
+ def _depart_named_admonition(self, node):
+ self.body.append('\\end{notice}\n')
+
+ visit_attention = _make_visit_admonition('attention')
+ depart_attention = _depart_named_admonition
+ visit_caution = _make_visit_admonition('caution')
+ depart_caution = _depart_named_admonition
+ visit_danger = _make_visit_admonition('danger')
+ depart_danger = _depart_named_admonition
+ visit_error = _make_visit_admonition('error')
+ depart_error = _depart_named_admonition
+ visit_hint = _make_visit_admonition('hint')
+ depart_hint = _depart_named_admonition
+ visit_important = _make_visit_admonition('important')
+ depart_important = _depart_named_admonition
+ visit_note = _make_visit_admonition('note')
+ depart_note = _depart_named_admonition
+ visit_tip = _make_visit_admonition('tip')
+ depart_tip = _depart_named_admonition
+ visit_warning = _make_visit_admonition('warning')
+ depart_warning = _depart_named_admonition
+
+ def visit_versionmodified(self, node):
+ intro = versionlabels[node['type']] % node['version']
+ if node.children:
+ intro += ': '
+ else:
+ intro += '.'
+ self.body.append(intro)
+ def depart_versionmodified(self, node):
+ pass
+
+ def visit_target(self, node):
+ def add_target(id):
+ # indexing uses standard LaTeX index markup, so the targets
+ # will be generated differently
+ if not id.startswith('index-'):
+ self.body.append(r'\hypertarget{%s}{}' % id)
+
+ if node.has_key('refid') and node['refid'] not in self.written_ids:
+ parindex = node.parent.index(node)
+ try:
+ next = node.parent[parindex+1]
+ if isinstance(next, nodes.section):
+ self.next_section_target = node['refid']
+ return
+ except IndexError:
+ pass
+ add_target(node['refid'])
+ self.written_ids.add(node['refid'])
+ def depart_target(self, node):
+ pass
+
+ def visit_attribution(self, node):
+ self.body.append('\n\\begin{flushright}\n')
+ self.body.append('---')
+ def depart_attribution(self, node):
+ self.body.append('\n\\end{flushright}\n')
+
+ def visit_index(self, node, scre=re.compile(r';\s*')):
+ entries = node['entries']
+ for type, string, tid, _ in entries:
+ if type == 'single':
+ self.body.append(r'\index{%s}' % scre.sub('!', self.encode(string)))
+ elif type == 'pair':
+ parts = tuple(self.encode(x.strip()) for x in string.split(';', 1))
+ self.body.append(r'\indexii{%s}{%s}' % parts)
+ elif type == 'triple':
+ parts = tuple(self.encode(x.strip()) for x in string.split(';', 2))
+ self.body.append(r'\indexiii{%s}{%s}{%s}' % parts)
+ else:
+ self.builder.warn('unknown index entry type %s found' % type)
+ raise nodes.SkipNode
+
+ def visit_raw(self, node):
+ if 'latex' in node.get('format', '').split():
+ self.body.append(node.astext())
+ raise nodes.SkipNode
+
+ def visit_reference(self, node):
+ uri = node.get('refuri', '')
+ if self.in_title or not uri:
+ self.context.append('')
+ elif uri.startswith('mailto:') or uri.startswith('http:') or \
+ uri.startswith('https:') or uri.startswith('ftp:'):
+ self.body.append('\\href{%s}{' % self.encode(uri))
+ self.context.append('}')
+ elif uri.startswith('#'):
+ self.body.append('\\hyperlink{%s}{' % uri[1:])
+ self.context.append('}')
+ elif uri.startswith('@token'):
+ if self.in_production_list:
+ self.body.append('\\token{')
+ else:
+ self.body.append('\\grammartoken{')
+ self.context.append('}')
+ else:
+ self.builder.warn('unusable reference target found: %s' % uri)
+ self.context.append('')
+ def depart_reference(self, node):
+ self.body.append(self.context.pop())
+
+ def visit_pending_xref(self, node):
+ pass
+ def depart_pending_xref(self, node):
+ pass
+
+ def visit_emphasis(self, node):
+ self.body.append(r'\emph{')
+ def depart_emphasis(self, node):
+ self.body.append('}')
+
+ def visit_literal_emphasis(self, node):
+ self.body.append(r'\emph{\texttt{')
+ self.no_contractions += 1
+ def depart_literal_emphasis(self, node):
+ self.body.append('}}')
+ self.no_contractions -= 1
+
+ def visit_strong(self, node):
+ self.body.append(r'\textbf{')
+ def depart_strong(self, node):
+ self.body.append('}')
+
+ def visit_title_reference(self, node):
+ self.body.append(r'\emph{')
+ def depart_title_reference(self, node):
+ self.body.append('}')
+
+ def visit_citation(self, node):
+ # TODO maybe use cite bibitems
+ self.bibitems.append(['', ''])
+ self.context.append(len(self.body))
+ def depart_citation(self, node):
+ size = self.context.pop()
+ text = ''.join(self.body[size:])
+ del self.body[size:]
+ self.bibitems[-1][1] = text
+
+ def visit_citation_reference(self, node):
+ citeid = node.astext()
+ self.body.append('\\cite{%s}' % citeid)
+ raise nodes.SkipNode
+
+ def visit_literal(self, node):
+ content = self.encode(node.astext().strip())
+ if self.in_title:
+ self.body.append(r'\texttt{%s}' % content)
+ elif node.has_key('role') and node['role'] == 'samp':
+ self.body.append(r'\samp{%s}' % content)
+ else:
+ self.body.append(r'\code{%s}' % content)
+ raise nodes.SkipNode
+
+ def visit_footnote_reference(self, node):
+ num = node.astext().strip()
+ try:
+ fn = self.footnotestack[-1][num]
+ except (KeyError, IndexError):
+ raise nodes.SkipNode
+ self.body.append('\\footnote{')
+ fn.walkabout(self)
+ raise nodes.SkipChildren
+ def depart_footnote_reference(self, node):
+ self.body.append('}')
+
+ def visit_literal_block(self, node):
+ self.verbatim = ''
+ def depart_literal_block(self, node):
+ code = self.verbatim.rstrip('\n')
+ lang = self.highlightlang
+ linenos = code.count('\n') >= self.highlightlinenothreshold - 1
+ if node.has_key('language'):
+ # code-block directives
+ lang = node['language']
+ if node.has_key('linenos'):
+ linenos = node['linenos']
+ hlcode = self.highlighter.highlight_block(code, lang, linenos)
+ # workaround for Unicode issue
+ hlcode = hlcode.replace(u'€', u'@texteuro[]')
+ # must use original Verbatim environment and "tabular" environment
+ if self.table:
+ hlcode = hlcode.replace('\\begin{Verbatim}',
+ '\\begin{OriginalVerbatim}')
+ self.table.has_verbatim = True
+ # get consistent trailer
+ hlcode = hlcode.rstrip()[:-14] # strip \end{Verbatim}
+ hlcode = hlcode.rstrip() + '\n'
+ self.body.append('\n' + hlcode + '\\end{%sVerbatim}\n' %
+ (self.table and 'Original' or ''))
+ self.verbatim = None
+ visit_doctest_block = visit_literal_block
+ depart_doctest_block = depart_literal_block
+
+ def visit_line_block(self, node):
+ """line-block:
+ * whitespace (including linebreaks) is significant
+ * inline markup is supported.
+ * serif typeface
+ """
+ self.body.append('{\\raggedright{}')
+ self.literal_whitespace = 1
+ def depart_line_block(self, node):
+ self.literal_whitespace = 0
+ # remove the last \\
+ del self.body[-1]
+ self.body.append('}\n')
+
+ def visit_line(self, node):
+ self._line_start = len(self.body)
+ def depart_line(self, node):
+ if self._line_start == len(self.body):
+ # no output in this line -- add a nonbreaking space, else the
+ # \\ command will give an error
+ self.body.append('~')
+ if self.table is not None:
+ self.body.append('\\newline\n')
+ else:
+ self.body.append('\\\\\n')
+
+ def visit_block_quote(self, node):
+ # If the block quote contains a single object and that object
+ # is a list, then generate a list not a block quote.
+ # This lets us indent lists.
+ done = 0
+ if len(node.children) == 1:
+ child = node.children[0]
+ if isinstance(child, nodes.bullet_list) or \
+ isinstance(child, nodes.enumerated_list):
+ done = 1
+ if not done:
+ self.body.append('\\begin{quote}\n')
+ def depart_block_quote(self, node):
+ done = 0
+ if len(node.children) == 1:
+ child = node.children[0]
+ if isinstance(child, nodes.bullet_list) or \
+ isinstance(child, nodes.enumerated_list):
+ done = 1
+ if not done:
+ self.body.append('\\end{quote}\n')
+
+ # option node handling copied from docutils' latex writer
+
+ def visit_option(self, node):
+ if self.context[-1]:
+ # this is not the first option
+ self.body.append(', ')
+ def depart_option(self, node):
+ # flag that the first option is done.
+ self.context[-1] += 1
+
+ def visit_option_argument(self, node):
+ """The delimiter betweeen an option and its argument."""
+ self.body.append(node.get('delimiter', ' '))
+ def depart_option_argument(self, node):
+ pass
+
+ def visit_option_group(self, node):
+ self.body.append('\\item [')
+ # flag for first option
+ self.context.append(0)
+ def depart_option_group(self, node):
+ self.context.pop() # the flag
+ self.body.append('] ')
+
+ def visit_option_list(self, node):
+ self.body.append('\\begin{optionlist}{3cm}\n')
+ def depart_option_list(self, node):
+ self.body.append('\\end{optionlist}\n')
+
+ def visit_option_list_item(self, node):
+ pass
+ def depart_option_list_item(self, node):
+ pass
+
+ def visit_option_string(self, node):
+ ostring = node.astext()
+ self.body.append(self.encode(ostring.replace('--', u'-{-}')))
+ raise nodes.SkipNode
+
+ def visit_description(self, node):
+ self.body.append( ' ' )
+ def depart_description(self, node):
+ pass
+
+ def visit_superscript(self, node):
+ self.body.append('$^{\\text{')
+ def depart_superscript(self, node):
+ self.body.append('}}$')
+
+ def visit_subscript(self, node):
+ self.body.append('$_{\\text{')
+ def depart_subscript(self, node):
+ self.body.append('}}$')
+
+ def visit_substitution_definition(self, node):
+ raise nodes.SkipNode
+
+ def visit_substitution_reference(self, node):
+ raise nodes.SkipNode
+
+ def visit_generated(self, node):
+ pass
+ def depart_generated(self, node):
+ pass
+
+ def visit_compound(self, node):
+ pass
+ def depart_compound(self, node):
+ pass
+
+ def visit_container(self, node):
+ pass
+ def depart_container(self, node):
+ pass
+
+ def visit_decoration(self, node):
+ pass
+ def depart_decoration(self, node):
+ pass
+
+ # text handling
+
+ def encode(self, text):
+ text = unicode(text).translate(tex_escape_map)
+ if self.literal_whitespace:
+ # Insert a blank before the newline, to avoid
+ # ! LaTeX Error: There's no line here to end.
+ text = text.replace(u'\n', u'~\\\\\n').replace(u' ', u'~')
+ if self.no_contractions:
+ text = text.replace('--', u'-{-}')
+ return text
+
+ def visit_Text(self, node):
+ if self.verbatim is not None:
+ self.verbatim += node.astext()
+ else:
+ text = self.encode(node.astext())
+ self.body.append(educateQuotesLatex(text))
+ def depart_Text(self, node):
+ pass
+
+ def visit_comment(self, node):
+ raise nodes.SkipNode
+
+ def visit_meta(self, node):
+ # only valid for HTML
+ raise nodes.SkipNode
+
+ def visit_system_message(self, node):
+ pass
+ def depart_system_message(self, node):
+ self.body.append('\n')
+
+ def unknown_visit(self, node):
+ raise NotImplementedError('Unknown node: ' + node.__class__.__name__)
diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py
new file mode 100644
index 00000000..74c637ca
--- /dev/null
+++ b/sphinx/writers/text.py
@@ -0,0 +1,679 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.writers.text
+ ~~~~~~~~~~~~~~~~~~~
+
+ Custom docutils writer for plain text.
+
+ :copyright: 2008 by Georg Brandl.
+ :license: BSD.
+"""
+
+import re
+import textwrap
+
+from docutils import nodes, writers
+
+from sphinx import addnodes
+from sphinx.locale import admonitionlabels, versionlabels
+
+
+class TextWriter(writers.Writer):
+ supported = ('text',)
+ settings_spec = ('No options here.', '', ())
+ settings_defaults = {}
+
+ output = None
+
+ def __init__(self, builder):
+ writers.Writer.__init__(self)
+ self.builder = builder
+
+ def translate(self):
+ visitor = TextTranslator(self.document, self.builder)
+ self.document.walkabout(visitor)
+ self.output = visitor.body
+
+# monkey-patch...
+new_wordsep_re = re.compile(
+ r'(\s+|' # any whitespace
+ r'(?<=\s)(?::[a-z-]+:)?`\S+|' # interpreted text start
+ r'[^\s\w]*\w+[a-zA-Z]-(?=\w+[a-zA-Z])|' # hyphenated words
+ r'(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') # em-dash
+textwrap.TextWrapper.wordsep_re = new_wordsep_re
+
+MAXWIDTH = 70
+STDINDENT = 3
+
+
+class TextTranslator(nodes.NodeVisitor):
+ sectionchars = '*=-~"+'
+
+ def __init__(self, document, builder):
+ nodes.NodeVisitor.__init__(self, document)
+
+ self.states = [[]]
+ self.stateindent = [0]
+ self.sectionlevel = 0
+ self.table = None
+
+ def add_text(self, text):
+ self.states[-1].append((-1, text))
+ def new_state(self, indent=STDINDENT):
+ self.states.append([])
+ self.stateindent.append(indent)
+ def end_state(self, wrap=True, end=[''], first=None):
+ content = self.states.pop()
+ maxindent = sum(self.stateindent)
+ indent = self.stateindent.pop()
+ result = []
+ toformat = []
+ def do_format():
+ if not toformat:
+ return
+ if wrap:
+ res = textwrap.wrap(''.join(toformat), width=MAXWIDTH-maxindent)
+ else:
+ res = ''.join(toformat).splitlines()
+ if end:
+ res += end
+ result.append((indent, res))
+ for itemindent, item in content:
+ if itemindent == -1:
+ toformat.append(item)
+ else:
+ do_format()
+ result.append((indent + itemindent, item))
+ toformat = []
+ do_format()
+ if first is not None and result:
+ itemindent, item = result[0]
+ if item:
+ result.insert(0, (itemindent - indent, [first + item[0]]))
+ result[1] = (itemindent, item[1:])
+ self.states[-1].extend(result)
+
+ def visit_document(self, node):
+ self.new_state(0)
+ def depart_document(self, node):
+ self.end_state()
+ self.body = '\n'.join(line and (' '*indent + line)
+ for indent, lines in self.states[0]
+ for line in lines)
+ # XXX header/footer?
+
+ def visit_highlightlang(self, node):
+ raise nodes.SkipNode
+
+ def visit_section(self, node):
+ self._title_char = self.sectionchars[self.sectionlevel]
+ self.sectionlevel += 1
+ def depart_section(self, node):
+ self.sectionlevel -= 1
+
+ def visit_topic(self, node):
+ self.new_state(0)
+ def depart_topic(self, node):
+ self.end_state()
+
+ visit_sidebar = visit_topic
+ depart_sidebar = depart_topic
+
+ def visit_rubric(self, node):
+ self.new_state(0)
+ self.add_text('-[ ')
+ def depart_rubric(self, node):
+ self.add_text(' ]-')
+ self.end_state()
+
+ def visit_compound(self, node):
+ pass
+ def depart_compound(self, node):
+ pass
+
+ def visit_glossary(self, node):
+ pass
+ def depart_glossary(self, node):
+ pass
+
+ def visit_title(self, node):
+ if isinstance(node.parent, nodes.Admonition):
+ self.add_text(node.astext()+': ')
+ raise nodes.SkipNode
+ self.new_state(0)
+ def depart_title(self, node):
+ if isinstance(node.parent, nodes.section):
+ char = self._title_char
+ else:
+ char = '^'
+ text = ''.join(x[1] for x in self.states.pop() if x[0] == -1)
+ self.stateindent.pop()
+ self.states[-1].append((0, ['', text, '%s' % (char * len(text)), '']))
+
+ def visit_subtitle(self, node):
+ pass
+ def depart_subtitle(self, node):
+ pass
+
+ def visit_attribution(self, node):
+ self.add_text('-- ')
+ def depart_attribution(self, node):
+ pass
+
+ def visit_module(self, node):
+ if node.has_key('platform'):
+ self.new_state(0)
+ self.add_text(_('Platform: %s') % node['platform'])
+ self.end_state()
+ raise nodes.SkipNode
+
+ def visit_desc(self, node):
+ pass
+ def depart_desc(self, node):
+ pass
+
+ def visit_desc_signature(self, node):
+ self.new_state(0)
+ if node.parent['desctype'] in ('class', 'exception'):
+ self.add_text('%s ' % node.parent['desctype'])
+ def depart_desc_signature(self, node):
+ # XXX: wrap signatures in a way that makes sense
+ self.end_state(wrap=False, end=None)
+
+ def visit_desc_name(self, node):
+ pass
+ def depart_desc_name(self, node):
+ pass
+
+ def visit_desc_addname(self, node):
+ pass
+ def depart_desc_addname(self, node):
+ pass
+
+ def visit_desc_type(self, node):
+ pass
+ def depart_desc_type(self, node):
+ pass
+
+ def visit_desc_parameterlist(self, node):
+ self.add_text('(')
+ self.first_param = 1
+ def depart_desc_parameterlist(self, node):
+ self.add_text(')')
+
+ def visit_desc_parameter(self, node):
+ if not self.first_param:
+ self.add_text(', ')
+ else:
+ self.first_param = 0
+ self.add_text(node.astext())
+ raise nodes.SkipNode
+
+ def visit_desc_optional(self, node):
+ self.add_text('[')
+ def depart_desc_optional(self, node):
+ self.add_text(']')
+
+ def visit_desc_annotation(self, node):
+ pass
+ def depart_desc_annotation(self, node):
+ pass
+
+ def visit_refcount(self, node):
+ pass
+ def depart_refcount(self, node):
+ pass
+
+ def visit_desc_content(self, node):
+ self.new_state()
+ self.add_text('\n')
+ def depart_desc_content(self, node):
+ self.end_state()
+
+ def visit_figure(self, node):
+ self.new_state()
+ def depart_figure(self, node):
+ self.end_state()
+
+ def visit_caption(self, node):
+ pass
+ def depart_caption(self, node):
+ pass
+
+ def visit_productionlist(self, node):
+ self.new_state()
+ names = []
+ for production in node:
+ names.append(production['tokenname'])
+ maxlen = max(len(name) for name in names)
+ for production in node:
+ if production['tokenname']:
+ self.add_text(production['tokenname'].ljust(maxlen) + ' ::=')
+ lastname = production['tokenname']
+ else:
+ self.add_text('%s ' % (' '*len(lastname)))
+ self.add_text(production.astext() + '\n')
+ self.end_state(wrap=False)
+ raise nodes.SkipNode
+
+ def visit_seealso(self, node):
+ self.new_state()
+ def depart_seealso(self, node):
+ self.end_state(first='')
+
+ def visit_footnote(self, node):
+ self._footnote = node.children[0].astext().strip()
+ self.new_state(len(self._footnote) + 3)
+ def depart_footnote(self, node):
+ self.end_state(first='[%s] ' % self._footnote)
+
+ def visit_citation(self, node):
+ if len(node) and isinstance(node[0], nodes.label):
+ self._citlabel = node[0].astext()
+ else:
+ self._citlabel = ''
+ self.new_state(len(self._citlabel) + 3)
+ def depart_citation(self, node):
+ self.end_state(first='[%s] ' % self._citlabel)
+
+ def visit_label(self, node):
+ raise nodes.SkipNode
+
+ # XXX: option list could use some better styling
+
+ def visit_option_list(self, node):
+ pass
+ def depart_option_list(self, node):
+ pass
+
+ def visit_option_list_item(self, node):
+ self.new_state(0)
+ def depart_option_list_item(self, node):
+ self.end_state()
+
+ def visit_option_group(self, node):
+ self._firstoption = True
+ def depart_option_group(self, node):
+ self.add_text(' ')
+
+ def visit_option(self, node):
+ if self._firstoption:
+ self._firstoption = False
+ else:
+ self.add_text(', ')
+ def depart_option(self, node):
+ pass
+
+ def visit_option_string(self, node):
+ pass
+ def depart_option_string(self, node):
+ pass
+
+ def visit_option_argument(self, node):
+ self.add_text(node['delimiter'])
+ def depart_option_argument(self, node):
+ pass
+
+ def visit_description(self, node):
+ pass
+ def depart_description(self, node):
+ pass
+
+ def visit_tabular_col_spec(self, node):
+ raise nodes.SkipNode
+
+ def visit_colspec(self, node):
+ self.table[0].append(node['colwidth'])
+ raise nodes.SkipNode
+
+ def visit_tgroup(self, node):
+ pass
+ def depart_tgroup(self, node):
+ pass
+
+ def visit_thead(self, node):
+ pass
+ def depart_thead(self, node):
+ pass
+
+ def visit_tbody(self, node):
+ self.table.append('sep')
+ def depart_tbody(self, node):
+ pass
+
+ def visit_row(self, node):
+ self.table.append([])
+ def depart_row(self, node):
+ pass
+
+ def visit_entry(self, node):
+ if node.has_key('morerows') or node.has_key('morecols'):
+ raise NotImplementedError('Column or row spanning cells are '
+ 'not implemented.')
+ self.new_state(0)
+ def depart_entry(self, node):
+ text = '\n'.join('\n'.join(x[1]) for x in self.states.pop())
+ self.stateindent.pop()
+ self.table[-1].append(text)
+
+ def visit_table(self, node):
+ if self.table:
+ raise NotImplementedError('Nested tables are not supported.')
+ self.new_state(0)
+ self.table = [[]]
+ def depart_table(self, node):
+ lines = self.table[1:]
+ fmted_rows = []
+ colwidths = self.table[0]
+ realwidths = colwidths[:]
+ separator = 0
+ # don't allow paragraphs in table cells for now
+ for line in lines:
+ if line == 'sep':
+ separator = len(fmted_rows)
+ else:
+ cells = []
+ for i, cell in enumerate(line):
+ par = textwrap.wrap(cell, width=colwidths[i])
+ if par:
+ maxwidth = max(map(len, par))
+ else:
+ maxwidth = 0
+ realwidths[i] = max(realwidths[i], maxwidth)
+ cells.append(par)
+ fmted_rows.append(cells)
+
+ def writesep(char='-'):
+ out = ['+']
+ for width in realwidths:
+ out.append(char * (width+2))
+ out.append('+')
+ self.add_text(''.join(out) + '\n')
+
+ def writerow(row):
+ lines = map(None, *row)
+ for line in lines:
+ out = ['|']
+ for i, cell in enumerate(line):
+ if cell:
+ out.append(' ' + cell.ljust(realwidths[i]+1))
+ else:
+ out.append(' ' * (realwidths[i] + 2))
+ out.append('|')
+ self.add_text(''.join(out) + '\n')
+
+ for i, row in enumerate(fmted_rows):
+ if separator and i == separator:
+ writesep('=')
+ else:
+ writesep('-')
+ writerow(row)
+ writesep('-')
+ self.table = None
+ self.end_state(wrap=False)
+
+ def visit_acks(self, node):
+ self.new_state(0)
+ self.add_text(', '.join(n.astext() for n in node.children[0].children) + '.')
+ self.end_state()
+ raise nodes.SkipNode
+
+ def visit_image(self, node):
+ self.add_text(_('[image]'))
+ raise nodes.SkipNode
+
+ def visit_transition(self, node):
+ indent = sum(self.stateindent)
+ self.new_state(0)
+ self.add_text('=' * (MAXWIDTH - indent))
+ self.end_state()
+ raise nodes.SkipNode
+
+ def visit_bullet_list(self, node):
+ self._list_counter = -1
+ def depart_bullet_list(self, node):
+ pass
+
+ def visit_enumerated_list(self, node):
+ self._list_counter = 0
+ def depart_enumerated_list(self, node):
+ pass
+
+ def visit_definition_list(self, node):
+ self._list_counter = -2
+ def depart_definition_list(self, node):
+ pass
+
+ def visit_list_item(self, node):
+ if self._list_counter == -1:
+ # bullet list
+ self.new_state(2)
+ elif self._list_counter == -2:
+ # definition list
+ pass
+ else:
+ # enumerated list
+ self._list_counter += 1
+ self.new_state(len(str(self._list_counter)) + 2)
+ def depart_list_item(self, node):
+ if self._list_counter == -1:
+ self.end_state(first='* ', end=None)
+ elif self._list_counter == -2:
+ pass
+ else:
+ self.end_state(first='%s. ' % self._list_counter, end=None)
+
+ def visit_definition_list_item(self, node):
+ self._li_has_classifier = len(node) >= 2 and \
+ isinstance(node[1], nodes.classifier)
+ def depart_definition_list_item(self, node):
+ pass
+
+ def visit_term(self, node):
+ self.new_state(0)
+ def depart_term(self, node):
+ if not self._li_has_classifier:
+ self.end_state(end=None)
+
+ def visit_classifier(self, node):
+ self.add_text(' : ')
+ def depart_classifier(self, node):
+ self.end_state(end=None)
+
+ def visit_definition(self, node):
+ self.new_state()
+ def depart_definition(self, node):
+ self.end_state()
+
+ def visit_field_list(self, node):
+ pass
+ def depart_field_list(self, node):
+ pass
+
+ def visit_field(self, node):
+ pass
+ def depart_field(self, node):
+ pass
+
+ def visit_field_name(self, node):
+ self.new_state(0)
+ def depart_field_name(self, node):
+ self.add_text(':')
+ self.end_state(end=None)
+
+ def visit_field_body(self, node):
+ self.new_state()
+ def depart_field_body(self, node):
+ self.end_state()
+
+ def visit_centered(self, node):
+ pass
+ def depart_centered(self, node):
+ pass
+
+ def visit_admonition(self, node):
+ self.new_state(0)
+ def depart_admonition(self, node):
+ self.end_state()
+
+ def _visit_admonition(self, node):
+ self.new_state(2)
+ def _make_depart_admonition(name):
+ def depart_admonition(self, node):
+ self.end_state(first=admonitionlabels[name] + ': ')
+ return depart_admonition
+
+ visit_attention = _visit_admonition
+ depart_attention = _make_depart_admonition('attention')
+ visit_caution = _visit_admonition
+ depart_caution = _make_depart_admonition('caution')
+ visit_danger = _visit_admonition
+ depart_danger = _make_depart_admonition('danger')
+ visit_error = _visit_admonition
+ depart_error = _make_depart_admonition('error')
+ visit_hint = _visit_admonition
+ depart_hint = _make_depart_admonition('hint')
+ visit_important = _visit_admonition
+ depart_important = _make_depart_admonition('important')
+ visit_note = _visit_admonition
+ depart_note = _make_depart_admonition('note')
+ visit_tip = _visit_admonition
+ depart_tip = _make_depart_admonition('tip')
+ visit_warning = _visit_admonition
+ depart_warning = _make_depart_admonition('warning')
+
+ def visit_versionmodified(self, node):
+ self.new_state(0)
+ if node.children:
+ self.add_text(versionlabels[node['type']] % node['version'] + ': ')
+ else:
+ self.add_text(versionlabels[node['type']] % node['version'] + '.')
+ def depart_versionmodified(self, node):
+ self.end_state()
+
+ def visit_literal_block(self, node):
+ self.new_state()
+ def depart_literal_block(self, node):
+ self.end_state(wrap=False)
+
+ def visit_doctest_block(self, node):
+ self.new_state(0)
+ def depart_doctest_block(self, node):
+ self.end_state(wrap=False)
+
+ def visit_line_block(self, node):
+ self.new_state(0)
+ def depart_line_block(self, node):
+ self.end_state(wrap=False)
+
+ def visit_line(self, node):
+ pass
+ def depart_line(self, node):
+ pass
+
+ def visit_block_quote(self, node):
+ self.new_state()
+ def depart_block_quote(self, node):
+ self.end_state()
+
+ def visit_compact_paragraph(self, node):
+ pass
+ def depart_compact_paragraph(self, node):
+ pass
+
+ def visit_paragraph(self, node):
+ if not isinstance(node.parent, nodes.Admonition) or \
+ isinstance(node.parent, addnodes.seealso):
+ self.new_state(0)
+ def depart_paragraph(self, node):
+ if not isinstance(node.parent, nodes.Admonition) or \
+ isinstance(node.parent, addnodes.seealso):
+ self.end_state()
+
+ def visit_target(self, node):
+ raise nodes.SkipNode
+
+ def visit_index(self, node):
+ raise nodes.SkipNode
+
+ def visit_substitution_definition(self, node):
+ raise nodes.SkipNode
+
+ def visit_pending_xref(self, node):
+ pass
+ def depart_pending_xref(self, node):
+ pass
+
+ def visit_reference(self, node):
+ pass
+ def depart_reference(self, node):
+ pass
+
+ def visit_emphasis(self, node):
+ self.add_text('*')
+ def depart_emphasis(self, node):
+ self.add_text('*')
+
+ def visit_literal_emphasis(self, node):
+ self.add_text('*')
+ def depart_literal_emphasis(self, node):
+ self.add_text('*')
+
+ def visit_strong(self, node):
+ self.add_text('**')
+ def depart_strong(self, node):
+ self.add_text('**')
+
+ def visit_title_reference(self, node):
+ self.add_text('*')
+ def depart_title_reference(self, node):
+ self.add_text('*')
+
+ def visit_literal(self, node):
+ self.add_text('``')
+ def depart_literal(self, node):
+ self.add_text('``')
+
+ def visit_subscript(self, node):
+ self.add_text('_')
+ def depart_subscript(self, node):
+ pass
+
+ def visit_superscript(self, node):
+ self.add_text('^')
+ def depart_superscript(self, node):
+ pass
+
+ def visit_footnote_reference(self, node):
+ self.add_text('[%s]' % node.astext())
+ raise nodes.SkipNode
+
+ def visit_citation_reference(self, node):
+ self.add_text('[%s]' % node.astext())
+ raise nodes.SkipNode
+
+ def visit_Text(self, node):
+ self.add_text(node.astext())
+ def depart_Text(self, node):
+ pass
+
+ def visit_problematic(self, node):
+ self.add_text('>>')
+ def depart_problematic(self, node):
+ self.add_text('<<')
+
+ def visit_system_message(self, node):
+ self.new_state(0)
+ self.add_text('' % node.astext())
+ self.end_state()
+ raise nodes.SkipNode
+
+ def visit_comment(self, node):
+ raise nodes.SkipNode
+
+ def visit_meta(self, node):
+ # only valid for HTML
+ raise nodes.SkipNode
+
+ def unknown_visit(self, node):
+ raise NotImplementedError('Unknown node: ' + node.__class__.__name__)
diff --git a/tests/test_build.py b/tests/test_build.py
index 8ca17938..0443aada 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -20,7 +20,7 @@ from util import *
from etree13 import ElementTree as ET
from sphinx.builder import StandaloneHTMLBuilder, LaTeXBuilder
-from sphinx.latexwriter import LaTeXTranslator
+from sphinx.writers.latex import LaTeXTranslator
html_warnfile = StringIO()
diff --git a/tests/test_markup.py b/tests/test_markup.py
index f1125103..a4b7cf77 100644
--- a/tests/test_markup.py
+++ b/tests/test_markup.py
@@ -17,8 +17,8 @@ from docutils import frontend, utils, nodes
from docutils.parsers import rst
from sphinx import addnodes
-from sphinx.htmlwriter import HTMLWriter, SmartyPantsHTMLTranslator
-from sphinx.latexwriter import LaTeXWriter, LaTeXTranslator
+from sphinx.writers.html import HTMLWriter, SmartyPantsHTMLTranslator
+from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator
def setup_module():
global app, settings, parser
--
cgit v1.2.1
From f8f41e1341f4aeb4458f74e4e4e47833210abdcb Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sat, 29 Nov 2008 20:03:56 +0100
Subject: This is version 0.6.
---
sphinx/__init__.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index 2df41707..9a41d4a0 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -13,8 +13,8 @@ import sys
from os import path
__revision__ = '$Revision$'
-__version__ = '0.5'
-__released__ = '0.5'
+__version__ = '0.6'
+__released__ = '0.6 (hg)'
package_dir = path.abspath(path.dirname(__file__))
--
cgit v1.2.1
From e9c79382e51d6aca7600addf51c53c4bb0344196 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sat, 29 Nov 2008 20:04:11 +0100
Subject: Some more fixes after the great renaming.
---
doc/builders.rst | 8 +++++++-
doc/config.rst | 6 +++---
doc/ext/appapi.rst | 2 +-
doc/ext/builderapi.rst | 2 +-
doc/glossary.rst | 2 +-
doc/templating.rst | 4 ++--
sphinx/ext/coverage.py | 2 +-
sphinx/ext/doctest.py | 2 +-
8 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/doc/builders.rst b/doc/builders.rst
index 508ab3c5..dd07a96a 100644
--- a/doc/builders.rst
+++ b/doc/builders.rst
@@ -3,7 +3,7 @@
Available builders
==================
-.. module:: sphinx.builder
+.. module:: sphinx.builders
:synopsis: Available built-in builder classes.
These are the built-in Sphinx builders. More builders can be added by
@@ -13,6 +13,7 @@ The builder's "name" must be given to the **-b** command-line option of
:program:`sphinx-build` to select a builder.
+.. module:: sphinx.builders.html
.. class:: StandaloneHTMLBuilder
This is the standard HTML builder. Its output is a directory with HTML
@@ -30,6 +31,7 @@ The builder's "name" must be given to the **-b** command-line option of
Its name is ``htmlhelp``.
+.. module:: sphinx.builders.latex
.. class:: LaTeXBuilder
This builder produces a bunch of LaTeX files in the output directory. You
@@ -50,6 +52,7 @@ The builder's "name" must be given to the **-b** command-line option of
Its name is ``latex``.
+.. module:: sphinx.builders.text
.. class:: TextBuilder
This builder produces a text file for each reST file -- this is almost the
@@ -60,6 +63,7 @@ The builder's "name" must be given to the **-b** command-line option of
.. versionadded:: 0.4
+.. currentmodule:: sphinx.builders.html
.. class:: SerializingHTMLBuilder
This builder uses a module that implements the Python serialization API
@@ -135,6 +139,7 @@ The builder's "name" must be given to the **-b** command-line option of
.. versionadded:: 0.5
+.. module:: sphinx.builders.changes
.. class:: ChangesBuilder
This builder produces an HTML overview of all :dir:`versionadded`,
@@ -144,6 +149,7 @@ The builder's "name" must be given to the **-b** command-line option of
Its name is ``changes``.
+.. module:: sphinx.builders.linkcheck
.. class:: CheckExternalLinksBuilder
This builder scans all documents for external links, tries to open them with
diff --git a/doc/config.rst b/doc/config.rst
index 819bf468..78671b68 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -22,8 +22,8 @@ Important points to note:
* The term "fully-qualified name" refers to a string that names an importable
Python object inside a module; for example, the FQN
- ``"sphinx.builder.Builder"`` means the ``Builder`` class in the
- ``sphinx.builder`` module.
+ ``"sphinx.builders.Builder"`` means the ``Builder`` class in the
+ ``sphinx.builders`` module.
* Remember that document names use ``/`` as the path separator and don't contain
the file name extension.
@@ -412,7 +412,7 @@ that use Sphinx' HTMLWriter class.
.. confval:: html_translator_class
A string with the fully-qualified name of a HTML Translator class, that is, a
- subclass of Sphinx' :class:`~sphinx.htmlwriter.HTMLTranslator`, that is used
+ subclass of Sphinx' :class:`~sphinx.writers.html.HTMLTranslator`, that is used
to translate document trees to HTML. Default is ``None`` (use the builtin
translator).
diff --git a/doc/ext/appapi.rst b/doc/ext/appapi.rst
index fcc29e38..355e42bd 100644
--- a/doc/ext/appapi.rst
+++ b/doc/ext/appapi.rst
@@ -13,7 +13,7 @@ the following public API:
.. method:: Sphinx.add_builder(builder)
Register a new builder. *builder* must be a class that inherits from
- :class:`~sphinx.builder.Builder`.
+ :class:`~sphinx.builders.Builder`.
.. method:: Sphinx.add_config_value(name, default, rebuild_env)
diff --git a/doc/ext/builderapi.rst b/doc/ext/builderapi.rst
index adc41016..72c388fb 100644
--- a/doc/ext/builderapi.rst
+++ b/doc/ext/builderapi.rst
@@ -5,7 +5,7 @@ Writing new builders
.. todo:: Expand this.
-.. currentmodule:: sphinx.builder
+.. currentmodule:: sphinx.builders
.. class:: Builder
diff --git a/doc/glossary.rst b/doc/glossary.rst
index 6a80ad36..7ec787ff 100644
--- a/doc/glossary.rst
+++ b/doc/glossary.rst
@@ -6,7 +6,7 @@ Glossary
.. glossary::
builder
- A class (inheriting from :class:`~sphinx.builder.Builder`) that takes
+ A class (inheriting from :class:`~sphinx.builders.Builder`) that takes
parsed documents and performs an action on them. Normally, builders
translate the documents to an output format, but it is also possible to
use the builder builders that e.g. check for broken links in the
diff --git a/doc/templating.rst b/doc/templating.rst
index 61a8a72b..fa6f2682 100644
--- a/doc/templating.rst
+++ b/doc/templating.rst
@@ -19,10 +19,10 @@ No. You have several other options:
configuration value accordingly.
* You can :ref:`write a custom builder ` that derives from
- :class:`~sphinx.builder.StandaloneHTMLBuilder` and calls your template engine
+ :class:`~sphinx.builders.StandaloneHTMLBuilder` and calls your template engine
of choice.
-* You can use the :class:`~sphinx.builder.PickleHTMLBuilder` that produces
+* You can use the :class:`~sphinx.builders.PickleHTMLBuilder` that produces
pickle files with the page contents, and postprocess them using a custom tool,
or use them in your Web application.
diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py
index f03dbc1e..dfe01419 100644
--- a/sphinx/ext/coverage.py
+++ b/sphinx/ext/coverage.py
@@ -16,7 +16,7 @@ import inspect
import cPickle as pickle
from os import path
-from sphinx.builder import Builder
+from sphinx.builders import Builder
# utility
diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py
index badd50ff..aa38bc71 100644
--- a/sphinx/ext/doctest.py
+++ b/sphinx/ext/doctest.py
@@ -21,7 +21,7 @@ doctest = __import__('doctest')
from docutils import nodes
from docutils.parsers.rst import directives
-from sphinx.builder import Builder
+from sphinx.builders import Builder
from sphinx.util.console import bold
blankline_re = re.compile(r'^\s*', re.MULTILINE)
--
cgit v1.2.1
From 229d3eb16b0c949ec87c21555345a83087317fe7 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 30 Nov 2008 16:33:56 +0100
Subject: Allow giving values for dict type config values in the overrides.
---
CHANGES | 12 ++++++++++++
doc/intro.rst | 8 ++++++--
sphinx/config.py | 6 ++++++
tests/test_config.py | 4 +++-
4 files changed, 27 insertions(+), 3 deletions(-)
diff --git a/CHANGES b/CHANGES
index 63321df1..5d9bc20c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,15 @@
+Release 0.6 (in development)
+============================
+
+New features added
+------------------
+
+* Other changes:
+
+ - Allow giving config overrides for single dict keys on the command
+ line.
+
+
Release 0.5 (Nov 23, 2008) -- Birthday release!
===============================================
diff --git a/doc/intro.rst b/doc/intro.rst
index 47e016b3..7ce9d1fa 100644
--- a/doc/intro.rst
+++ b/doc/intro.rst
@@ -111,8 +111,12 @@ The :program:`sphinx-build` script has several more options:
.. versionadded:: 0.5
**-D** *setting=value*
- Override a configuration value set in the :file:`conf.py` file. (The value
- must be a string value.)
+ Override a configuration value set in the :file:`conf.py` file. The value
+ must be a string or dictionary value. For the latter, supply the setting
+ name and key like this: ``-D latex_elements.docclass=scrartcl``.
+
+ .. versionchanged:: 0.6
+ The value can now be a dictionary value.
**-A** *name=value*
Make the *name* assigned to *value* in the HTML templates.
diff --git a/sphinx/config.py b/sphinx/config.py
index fa04ac2a..a9cae4cd 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -111,6 +111,12 @@ class Config(object):
def init_values(self):
config = self._raw_config
+ for valname, value in self.overrides.iteritems():
+ if '.' in valname:
+ realvalname, key = valname.split('.', 1)
+ config.setdefault(realvalname, {})[key] = value
+ else:
+ config[valname] = value
config.update(self.overrides)
for name in config:
if name in self.values:
diff --git a/tests/test_config.py b/tests/test_config.py
index 57d1936b..53cba59c 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -15,7 +15,8 @@ from util import *
from sphinx.application import ExtensionError
-@with_app(confoverrides={'master_doc': 'master', 'nonexisting_value': 'True'})
+@with_app(confoverrides={'master_doc': 'master', 'nonexisting_value': 'True',
+ 'latex_elements.docclass': 'scrartcl'})
def test_core_config(app):
cfg = app.config
@@ -26,6 +27,7 @@ def test_core_config(app):
# overrides
assert cfg.master_doc == 'master'
+ assert cfg.latex_elements['docclass'] == 'scrartcl'
# simple default values
assert 'exclude_dirs' not in cfg.__dict__
--
cgit v1.2.1
From 4b201975a442d45a3ae5118a38c3baa07940f9b3 Mon Sep 17 00:00:00 2001
From: mitsuhiko
Date: Sun, 30 Nov 2008 19:55:59 +0100
Subject: Changed an `__import__` call in the highlighter module because that
trigger a bug in the python importer.
---
sphinx/highlighting.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py
index 1637b2c3..3017133a 100644
--- a/sphinx/highlighting.py
+++ b/sphinx/highlighting.py
@@ -89,7 +89,7 @@ class PygmentsBridge(object):
style = SphinxStyle
elif '.' in stylename:
module, stylename = stylename.rsplit('.', 1)
- style = getattr(__import__(module, None, None, ['']), stylename)
+ style = getattr(__import__(module, None, None, ['__name__']), stylename)
else:
style = get_style_by_name(stylename)
self.hfmter = {False: HtmlFormatter(style=style),
--
cgit v1.2.1
From 0647cdecb439fa4dfd9bbdf094409e262cf33483 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 30 Nov 2008 19:58:29 +0100
Subject: Add Sphinx.add_lexer().
---
CHANGES | 4 ++++
doc/ext/appapi.rst | 7 +++++++
sphinx/application.py | 6 ++++++
sphinx/highlighting.py | 1 +
tests/test_highlighting.py | 37 +++++++++++++++++++++++++++++++++++++
5 files changed, 55 insertions(+)
create mode 100644 tests/test_highlighting.py
diff --git a/CHANGES b/CHANGES
index 5d9bc20c..b45ce66c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,10 @@ Release 0.6 (in development)
New features added
------------------
+* Extension API:
+
+ - Add Sphinx.add_lexer() to add custom Pygments lexers.
+
* Other changes:
- Allow giving config overrides for single dict keys on the command
diff --git a/doc/ext/appapi.rst b/doc/ext/appapi.rst
index 355e42bd..3dd5282b 100644
--- a/doc/ext/appapi.rst
+++ b/doc/ext/appapi.rst
@@ -167,6 +167,13 @@ the following public API:
:confval:`the docs for the config value `.
.. versionadded:: 0.5
+
+.. method:: Sphinx.add_lexer(alias, lexer)
+
+ Use *lexer*, which must be an instance of a Pygments lexer class, to
+ highlight code blocks with the given language *alias*.
+
+ .. versionadded:: 0.6
.. method:: Sphinx.connect(event, callback)
diff --git a/sphinx/application.py b/sphinx/application.py
index 6c644bbe..f7c57592 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -297,6 +297,12 @@ class Sphinx(object):
StandaloneHTMLBuilder.script_files.append(
posixpath.join('_static', filename))
+ def add_lexer(self, alias, lexer):
+ from sphinx.highlighting import lexers
+ if lexers is None:
+ return
+ lexers[alias] = lexer
+
class TemplateBridge(object):
"""
diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py
index 3017133a..d85aac23 100644
--- a/sphinx/highlighting.py
+++ b/sphinx/highlighting.py
@@ -30,6 +30,7 @@ try:
from pygments.token import Generic, Comment, Number
except ImportError:
pygments = None
+ lexers = None
else:
class SphinxStyle(Style):
"""
diff --git a/tests/test_highlighting.py b/tests/test_highlighting.py
new file mode 100644
index 00000000..5c353946
--- /dev/null
+++ b/tests/test_highlighting.py
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+"""
+ test_highlighting
+ ~~~~~~~~~~~~~~~~~
+
+ Test the Pygments highlighting bridge.
+
+ :copyright: 2008 by Georg Brandl.
+ :license: BSD.
+"""
+
+from util import *
+
+from pygments.lexer import RegexLexer
+from pygments.token import Text, Name
+
+from sphinx.highlighting import PygmentsBridge
+
+
+class MyLexer(RegexLexer):
+ name = 'testlexer'
+
+ tokens = {
+ 'root': [
+ ('a', Name),
+ ('b', Text),
+ ],
+ }
+
+
+@with_app()
+def test_add_lexer(app):
+ app.add_lexer('test', MyLexer())
+
+ bridge = PygmentsBridge('html')
+ ret = bridge.highlight_block('ab', 'test')
+ assert 'ab' in ret
--
cgit v1.2.1
From c001d82adcbb31d9c392ed5dd77a821f96a0c8db Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 30 Nov 2008 20:29:34 +0100
Subject: Allow using different Pygments formatters.
---
sphinx/highlighting.py | 28 ++++++++++++++++++----------
tests/test_highlighting.py | 14 ++++++++++++++
2 files changed, 32 insertions(+), 10 deletions(-)
diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py
index d85aac23..927b42d9 100644
--- a/sphinx/highlighting.py
+++ b/sphinx/highlighting.py
@@ -82,7 +82,12 @@ if sys.version_info < (2, 5):
class PygmentsBridge(object):
- def __init__(self, dest='html', stylename='sphinx'):
+ # Set these attributes if you want to have different Pygments formatters
+ # than the default ones.
+ html_formatter = HtmlFormatter
+ latex_formatter = LatexFormatter
+
+ def __init__(self, dest='html', stylename='sphinx', ):
self.dest = dest
if not pygments:
return
@@ -93,11 +98,14 @@ class PygmentsBridge(object):
style = getattr(__import__(module, None, None, ['__name__']), stylename)
else:
style = get_style_by_name(stylename)
- self.hfmter = {False: HtmlFormatter(style=style),
- True: HtmlFormatter(style=style, linenos=True)}
- self.lfmter = {False: LatexFormatter(style=style, commandprefix='PYG'),
- True: LatexFormatter(style=style, linenos=True,
- commandprefix='PYG')}
+ if dest == 'html':
+ self.fmter = {False: self.html_formatter(style=style),
+ True: self.html_formatter(style=style, linenos=True)}
+ else:
+ self.fmter = {False: self.latex_formatter(style=style,
+ commandprefix='PYG'),
+ True: self.latex_formatter(style=style, linenos=True,
+ commandprefix='PYG')}
def unhighlighted(self, source):
if self.dest == 'html':
@@ -171,9 +179,9 @@ class PygmentsBridge(object):
lexer.add_filter('raiseonerror')
try:
if self.dest == 'html':
- return highlight(source, lexer, self.hfmter[bool(linenos)])
+ return highlight(source, lexer, self.fmter[bool(linenos)])
else:
- hlsource = highlight(source, lexer, self.lfmter[bool(linenos)])
+ hlsource = highlight(source, lexer, self.fmter[bool(linenos)])
return hlsource.translate(tex_hl_escape_map)
except ErrorToken:
# this is most probably not the selected language,
@@ -187,9 +195,9 @@ class PygmentsBridge(object):
# no HTML styles needed
return ''
if self.dest == 'html':
- return self.hfmter[0].get_style_defs()
+ return self.fmter[0].get_style_defs()
else:
- styledefs = self.lfmter[0].get_style_defs()
+ styledefs = self.fmter[0].get_style_defs()
# workaround for Pygments < 0.12
if styledefs.startswith('\\newcommand\\at{@}'):
styledefs += _LATEX_STYLES
diff --git a/tests/test_highlighting.py b/tests/test_highlighting.py
index 5c353946..067c37cb 100644
--- a/tests/test_highlighting.py
+++ b/tests/test_highlighting.py
@@ -13,6 +13,7 @@ from util import *
from pygments.lexer import RegexLexer
from pygments.token import Text, Name
+from pygments.formatters.html import HtmlFormatter
from sphinx.highlighting import PygmentsBridge
@@ -27,6 +28,10 @@ class MyLexer(RegexLexer):
],
}
+class MyFormatter(HtmlFormatter):
+ def format(self, tokensource, outfile):
+ outfile.write('test')
+
@with_app()
def test_add_lexer(app):
@@ -35,3 +40,12 @@ def test_add_lexer(app):
bridge = PygmentsBridge('html')
ret = bridge.highlight_block('ab', 'test')
assert 'ab' in ret
+
+def test_set_formatter():
+ PygmentsBridge.html_formatter = MyFormatter
+ try:
+ bridge = PygmentsBridge('html')
+ ret = bridge.highlight_block('foo', 'python')
+ assert ret == 'test'
+ finally:
+ PygmentsBridge.html_formatter = HtmlFormatter
--
cgit v1.2.1
From f1bd7914bd3fb656b58e7bf72d64bec06f3034d7 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 30 Nov 2008 20:38:59 +0100
Subject: Add html_add_permalinks config value.
---
CHANGES | 13 ++++++++++---
doc/config.rst | 9 +++++++++
sphinx/builders/html.py | 3 +--
sphinx/builders/htmlhelp.py | 3 +--
sphinx/config.py | 1 +
sphinx/writers/html.py | 5 +++--
6 files changed, 25 insertions(+), 9 deletions(-)
diff --git a/CHANGES b/CHANGES
index b45ce66c..bf865f60 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,14 +4,21 @@ Release 0.6 (in development)
New features added
------------------
+* Configuration:
+
+ - The new ``html_add_permalinks`` config value can be used to
+ switch off the generated "paragraph sign" permalinks for each
+ heading and definition environment.
+
* Extension API:
- - Add Sphinx.add_lexer() to add custom Pygments lexers.
+ - There is now a Sphinx.add_lexer() method to add custom Pygments
+ lexers.
* Other changes:
- - Allow giving config overrides for single dict keys on the command
- line.
+ - Config overrides for single dict keys can now be given on the
+ command line.
Release 0.5 (Nov 23, 2008) -- Birthday release!
diff --git a/doc/config.rst b/doc/config.rst
index 78671b68..b6c33460 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -332,6 +332,15 @@ that use Sphinx' HTMLWriter class.
If true, *SmartyPants* will be used to convert quotes and dashes to
typographically correct entities. Default: ``True``.
+.. confval:: html_add_permalinks
+
+ If true, Sphinx will add "permalinks" for each heading and description
+ environment as paragraph signs that become visible when the mouse hovers over
+ them. Default: ``True``.
+
+ .. versionadded:: 0.6
+ Previously, this was always activated.
+
.. confval:: html_sidebars
Custom sidebar templates, must be a dictionary that maps document names to
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index adf58be1..7592dced 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -53,8 +53,7 @@ class StandaloneHTMLBuilder(Builder):
supported_image_types = ['image/svg+xml', 'image/png', 'image/gif',
'image/jpeg']
searchindex_filename = 'searchindex.js'
- add_header_links = True
- add_definition_links = True
+ add_permalinks = True
# This is a class attribute because it is mutated by Sphinx.add_javascript.
script_files = ['_static/jquery.js', '_static/doctools.js']
diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py
index 23900f36..20bb0d5c 100644
--- a/sphinx/builders/htmlhelp.py
+++ b/sphinx/builders/htmlhelp.py
@@ -132,8 +132,7 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder):
supported_image_types = ['image/png', 'image/gif', 'image/jpeg']
# don't add links
- add_header_links = False
- add_definition_links = False
+ add_permalinks = False
def init(self):
StandaloneHTMLBuilder.init(self)
diff --git a/sphinx/config.py b/sphinx/config.py
index a9cae4cd..1ea5f66f 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -65,6 +65,7 @@ class Config(object):
html_sidebars = ({}, False),
html_additional_pages = ({}, False),
html_use_modindex = (True, False),
+ html_add_permalinks = (True, False),
html_use_index = (True, False),
html_split_index = (False, False),
html_copy_source = (True, False),
diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py
index b82d7ccc..1b9205ce 100644
--- a/sphinx/writers/html.py
+++ b/sphinx/writers/html.py
@@ -58,6 +58,7 @@ class HTMLTranslator(BaseTranslator):
self.highlightlang = builder.config.highlight_language
self.highlightlinenothreshold = sys.maxint
self.protect_literal_text = 0
+ self.add_permalinks = builder.config.html_add_permalinks
def visit_desc(self, node):
self.body.append(self.starttag(node, 'dl', CLASS=node['desctype']))
@@ -73,7 +74,7 @@ class HTMLTranslator(BaseTranslator):
if node.parent['desctype'] in ('class', 'exception'):
self.body.append('%s ' % node.parent['desctype'])
def depart_desc_signature(self, node):
- if node['ids'] and self.builder.add_definition_links:
+ if node['ids'] and self.add_permalinks and self.builder.add_permalinks:
self.body.append(u'\u00B6' %
_('Permalink to this definition'))
@@ -388,7 +389,7 @@ class HTMLTranslator(BaseTranslator):
def depart_title(self, node):
close_tag = self.context[-1]
- if self.builder.add_header_links and \
+ if self.add_permalinks and self.builder.add_permalinks and \
(close_tag.startswith('
Date: Fri, 5 Dec 2008 12:26:40 +0100
Subject: Fix #69: a "self" too much.
---
sphinx/builders/htmlhelp.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py
index 20bb0d5c..b1c7fbc5 100644
--- a/sphinx/builders/htmlhelp.py
+++ b/sphinx/builders/htmlhelp.py
@@ -140,7 +140,7 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder):
self.out_suffix = '.html'
def handle_finish(self):
- self.build_hhx(self, self.outdir, self.config.htmlhelp_basename)
+ self.build_hhx(self.outdir, self.config.htmlhelp_basename)
def build_hhx(self, outdir, outname):
self.info('dumping stopword list...')
--
cgit v1.2.1
From 41cdb9e79dd86a337e8978c80d2af0938758ac0c Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Fri, 5 Dec 2008 12:27:08 +0100
Subject: Fix #64: import error in intersphinx.
---
sphinx/ext/intersphinx.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py
index 5d9607c9..85733ac4 100644
--- a/sphinx/ext/intersphinx.py
+++ b/sphinx/ext/intersphinx.py
@@ -31,7 +31,7 @@ from os import path
from docutils import nodes
-from sphinx.builders import INVENTORY_FILENAME
+from sphinx.builders.html import INVENTORY_FILENAME
def fetch_inventory(app, uri, inv):
--
cgit v1.2.1
From 2353c3dbea3b132c00fdfabbcc4c06d0decc35d6 Mon Sep 17 00:00:00 2001
From: mitsuhiko
Date: Sun, 7 Dec 2008 23:17:29 +0100
Subject: Added better error message for missing roman.py
---
sphinx/__init__.py | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index 9a41d4a0..43343ff2 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -31,17 +31,23 @@ def main(argv=sys.argv):
errstr = str(err)
if errstr.lower().startswith('no module named'):
whichmod = errstr[16:]
+ hint = ''
if whichmod.startswith('docutils'):
whichmod = 'Docutils library'
elif whichmod.startswith('jinja'):
whichmod = 'Jinja library'
elif whichmod == 'roman':
whichmod = 'roman module (which is distributed with Docutils)'
+ hint = ('This can happen if you upgraded docutils using\n'
+ 'easy_install without uninstalling the old version'
+ 'first.')
else:
whichmod += ' module'
print >>sys.stderr, \
'Error: The %s cannot be found. Did you install Sphinx '\
'and its dependencies correctly?' % whichmod
+ if hint:
+ print >> sys.stderr, hint
return 1
raise
return cmdline.main(argv)
--
cgit v1.2.1
From 03d6c8b26c27a480f9c8fe3079fa8fa2c2e7ed23 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Mon, 8 Dec 2008 08:59:51 +0100
Subject: Move again.
---
sphinx/builders/htmlhelp.py | 220 ++++++++
sphinx/builders/linkcheck.py | 130 +++++
sphinx/htmlhelp.py | 220 --------
sphinx/htmlwriter.py | 457 ----------------
sphinx/latexwriter.py | 1185 ------------------------------------------
sphinx/linkcheck.py | 130 -----
sphinx/textwriter.py | 679 ------------------------
sphinx/writers/html.py | 457 ++++++++++++++++
sphinx/writers/latex.py | 1185 ++++++++++++++++++++++++++++++++++++++++++
sphinx/writers/text.py | 679 ++++++++++++++++++++++++
10 files changed, 2671 insertions(+), 2671 deletions(-)
create mode 100644 sphinx/builders/htmlhelp.py
create mode 100644 sphinx/builders/linkcheck.py
delete mode 100644 sphinx/htmlhelp.py
delete mode 100644 sphinx/htmlwriter.py
delete mode 100644 sphinx/latexwriter.py
delete mode 100644 sphinx/linkcheck.py
delete mode 100644 sphinx/textwriter.py
create mode 100644 sphinx/writers/html.py
create mode 100644 sphinx/writers/latex.py
create mode 100644 sphinx/writers/text.py
diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py
new file mode 100644
index 00000000..4cc68bc9
--- /dev/null
+++ b/sphinx/builders/htmlhelp.py
@@ -0,0 +1,220 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.htmlhelp
+ ~~~~~~~~~~~~~~~
+
+ Build HTML help support files.
+ Adapted from the original Doc/tools/prechm.py.
+
+ :copyright: 2007-2008 by Georg Brandl.
+ :license: BSD.
+"""
+
+import os
+import cgi
+from os import path
+
+from docutils import nodes
+
+from sphinx import addnodes
+
+# Project file (*.hhp) template. 'outname' is the file basename (like
+# the pythlp in pythlp.hhp); 'version' is the doc version number (like
+# the 2.2 in Python 2.2).
+# The magical numbers in the long line under [WINDOWS] set most of the
+# user-visible features (visible buttons, tabs, etc).
+# About 0x10384e: This defines the buttons in the help viewer. The
+# following defns are taken from htmlhelp.h. Not all possibilities
+# actually work, and not all those that work are available from the Help
+# Workshop GUI. In particular, the Zoom/Font button works and is not
+# available from the GUI. The ones we're using are marked with 'x':
+#
+# 0x000002 Hide/Show x
+# 0x000004 Back x
+# 0x000008 Forward x
+# 0x000010 Stop
+# 0x000020 Refresh
+# 0x000040 Home x
+# 0x000080 Forward
+# 0x000100 Back
+# 0x000200 Notes
+# 0x000400 Contents
+# 0x000800 Locate x
+# 0x001000 Options x
+# 0x002000 Print x
+# 0x004000 Index
+# 0x008000 Search
+# 0x010000 History
+# 0x020000 Favorites
+# 0x040000 Jump 1
+# 0x080000 Jump 2
+# 0x100000 Zoom/Font x
+# 0x200000 TOC Next
+# 0x400000 TOC Prev
+
+project_template = '''\
+[OPTIONS]
+Binary TOC=Yes
+Binary Index=No
+Compiled file=%(outname)s.chm
+Contents file=%(outname)s.hhc
+Default Window=%(outname)s
+Default topic=index.html
+Display compile progress=No
+Full text search stop list file=%(outname)s.stp
+Full-text search=Yes
+Index file=%(outname)s.hhk
+Language=0x409
+Title=%(title)s
+
+[WINDOWS]
+%(outname)s="%(title)s","%(outname)s.hhc","%(outname)s.hhk",\
+"index.html","index.html",,,,,0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0
+
+[FILES]
+'''
+
+contents_header = '''\
+
+
+
+
+
+
+
+
+'''
+
+contents_footer = '''\
+
+'''
+
+object_sitemap = '''\
+
+'''
+
+# List of words the full text search facility shouldn't index. This
+# becomes file outname.stp. Note that this list must be pretty small!
+# Different versions of the MS docs claim the file has a maximum size of
+# 256 or 512 bytes (including \r\n at the end of each line).
+# Note that "and", "or", "not" and "near" are operators in the search
+# language, so no point indexing them even if we wanted to.
+stopwords = """
+a and are as at
+be but by
+for
+if in into is it
+near no not
+of on or
+such
+that the their then there these they this to
+was will with
+""".split()
+
+
+def build_hhx(builder, outdir, outname):
+ builder.info('dumping stopword list...')
+ f = open(path.join(outdir, outname+'.stp'), 'w')
+ try:
+ for word in sorted(stopwords):
+ print >>f, word
+ finally:
+ f.close()
+
+ builder.info('writing project file...')
+ f = open(path.join(outdir, outname+'.hhp'), 'w')
+ try:
+ f.write(project_template % {'outname': outname,
+ 'title': builder.config.html_title,
+ 'version': builder.config.version,
+ 'project': builder.config.project})
+ if not outdir.endswith(os.sep):
+ outdir += os.sep
+ olen = len(outdir)
+ for root, dirs, files in os.walk(outdir):
+ staticdir = (root == path.join(outdir, '_static'))
+ for fn in files:
+ if (staticdir and not fn.endswith('.js')) or fn.endswith('.html'):
+ print >>f, path.join(root, fn)[olen:].replace(os.sep, '\\')
+ finally:
+ f.close()
+
+ builder.info('writing TOC file...')
+ f = open(path.join(outdir, outname+'.hhc'), 'w')
+ try:
+ f.write(contents_header)
+ # special books
+ f.write('
')
+ for subitem in subitems:
+ write_index(subitem[0], subitem[1], [])
+ f.write('
')
+ for (key, group) in index:
+ for title, (refs, subitems) in group:
+ write_index(title, refs, subitems)
+ f.write('
\n')
+ finally:
+ f.close()
diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py
new file mode 100644
index 00000000..37aeb7a7
--- /dev/null
+++ b/sphinx/builders/linkcheck.py
@@ -0,0 +1,130 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.linkcheck
+ ~~~~~~~~~~~~~~~~
+
+ The CheckExternalLinksBuilder class.
+
+ :copyright: 2008 by Georg Brandl, Thomas Lamb.
+ :license: BSD.
+"""
+
+import socket
+from os import path
+from urllib2 import build_opener, HTTPError
+
+from docutils import nodes
+
+from sphinx.builder import Builder
+from sphinx.util.console import purple, red, darkgreen
+
+# create an opener that will simulate a browser user-agent
+opener = build_opener()
+opener.addheaders = [('User-agent', 'Mozilla/5.0')]
+
+
+class CheckExternalLinksBuilder(Builder):
+ """
+ Checks for broken external links.
+ """
+ name = 'linkcheck'
+
+ def init(self):
+ self.good = set()
+ self.broken = {}
+ self.redirected = {}
+ # set a timeout for non-responding servers
+ socket.setdefaulttimeout(5.0)
+ # create output file
+ open(path.join(self.outdir, 'output.txt'), 'w').close()
+
+ def get_target_uri(self, docname, typ=None):
+ return ''
+
+ def get_outdated_docs(self):
+ return self.env.found_docs
+
+ def prepare_writing(self, docnames):
+ return
+
+ def write_doc(self, docname, doctree):
+ self.info()
+ for node in doctree.traverse(nodes.reference):
+ try:
+ self.check(node, docname)
+ except KeyError:
+ continue
+
+ def check(self, node, docname):
+ uri = node['refuri']
+
+ if '#' in uri:
+ uri = uri.split('#')[0]
+
+ if uri in self.good:
+ return
+
+ lineno = None
+ while lineno is None and node:
+ node = node.parent
+ lineno = node.line
+
+ if uri[0:5] == 'http:' or uri[0:6] == 'https:':
+ self.info(uri, nonl=1)
+
+ if uri in self.broken:
+ (r, s) = self.broken[uri]
+ elif uri in self.redirected:
+ (r, s) = self.redirected[uri]
+ else:
+ (r, s) = self.resolve(uri)
+
+ if r == 0:
+ self.info(' - ' + darkgreen('working'))
+ self.good.add(uri)
+ elif r == 2:
+ self.info(' - ' + red('broken: ') + s)
+ self.write_entry('broken', docname, lineno, uri + ': ' + s)
+ self.broken[uri] = (r, s)
+ if self.app.quiet:
+ self.warn('%s:%s: broken link: %s' % (docname, lineno, uri))
+ else:
+ self.info(' - ' + purple('redirected') + ' to ' + s)
+ self.write_entry('redirected', docname, lineno, uri + ' to ' + s)
+ self.redirected[uri] = (r, s)
+ elif len(uri) == 0 or uri[0:7] == 'mailto:' or uri[0:4] == 'ftp:':
+ return
+ else:
+ self.warn(uri + ' - ' + red('malformed!'))
+ self.write_entry('malformed', docname, lineno, uri)
+ if self.app.quiet:
+ self.warn('%s:%s: malformed link: %s' % (docname, lineno, uri))
+ self.app.statuscode = 1
+
+ if self.broken:
+ self.app.statuscode = 1
+
+ def write_entry(self, what, docname, line, uri):
+ output = open(path.join(self.outdir, 'output.txt'), 'a')
+ output.write("%s:%s: [%s] %s\n" % (self.env.doc2path(docname, None),
+ line, what, uri))
+ output.close()
+
+ def resolve(self, uri):
+ try:
+ f = opener.open(uri)
+ f.close()
+ except HTTPError, err:
+ #if err.code == 403 and uri.startswith('http://en.wikipedia.org/'):
+ # # Wikipedia blocks requests from urllib User-Agent
+ # return (0, 0)
+ return (2, str(err))
+ except Exception, err:
+ return (2, str(err))
+ if f.url.rstrip('/') == uri.rstrip('/'):
+ return (0, 0)
+ else:
+ return (1, f.url)
+
+ def finish(self):
+ return
diff --git a/sphinx/htmlhelp.py b/sphinx/htmlhelp.py
deleted file mode 100644
index 4cc68bc9..00000000
--- a/sphinx/htmlhelp.py
+++ /dev/null
@@ -1,220 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- sphinx.htmlhelp
- ~~~~~~~~~~~~~~~
-
- Build HTML help support files.
- Adapted from the original Doc/tools/prechm.py.
-
- :copyright: 2007-2008 by Georg Brandl.
- :license: BSD.
-"""
-
-import os
-import cgi
-from os import path
-
-from docutils import nodes
-
-from sphinx import addnodes
-
-# Project file (*.hhp) template. 'outname' is the file basename (like
-# the pythlp in pythlp.hhp); 'version' is the doc version number (like
-# the 2.2 in Python 2.2).
-# The magical numbers in the long line under [WINDOWS] set most of the
-# user-visible features (visible buttons, tabs, etc).
-# About 0x10384e: This defines the buttons in the help viewer. The
-# following defns are taken from htmlhelp.h. Not all possibilities
-# actually work, and not all those that work are available from the Help
-# Workshop GUI. In particular, the Zoom/Font button works and is not
-# available from the GUI. The ones we're using are marked with 'x':
-#
-# 0x000002 Hide/Show x
-# 0x000004 Back x
-# 0x000008 Forward x
-# 0x000010 Stop
-# 0x000020 Refresh
-# 0x000040 Home x
-# 0x000080 Forward
-# 0x000100 Back
-# 0x000200 Notes
-# 0x000400 Contents
-# 0x000800 Locate x
-# 0x001000 Options x
-# 0x002000 Print x
-# 0x004000 Index
-# 0x008000 Search
-# 0x010000 History
-# 0x020000 Favorites
-# 0x040000 Jump 1
-# 0x080000 Jump 2
-# 0x100000 Zoom/Font x
-# 0x200000 TOC Next
-# 0x400000 TOC Prev
-
-project_template = '''\
-[OPTIONS]
-Binary TOC=Yes
-Binary Index=No
-Compiled file=%(outname)s.chm
-Contents file=%(outname)s.hhc
-Default Window=%(outname)s
-Default topic=index.html
-Display compile progress=No
-Full text search stop list file=%(outname)s.stp
-Full-text search=Yes
-Index file=%(outname)s.hhk
-Language=0x409
-Title=%(title)s
-
-[WINDOWS]
-%(outname)s="%(title)s","%(outname)s.hhc","%(outname)s.hhk",\
-"index.html","index.html",,,,,0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0
-
-[FILES]
-'''
-
-contents_header = '''\
-
-
-
-
-
-
-
-
-'''
-
-contents_footer = '''\
-
-'''
-
-object_sitemap = '''\
-
-'''
-
-# List of words the full text search facility shouldn't index. This
-# becomes file outname.stp. Note that this list must be pretty small!
-# Different versions of the MS docs claim the file has a maximum size of
-# 256 or 512 bytes (including \r\n at the end of each line).
-# Note that "and", "or", "not" and "near" are operators in the search
-# language, so no point indexing them even if we wanted to.
-stopwords = """
-a and are as at
-be but by
-for
-if in into is it
-near no not
-of on or
-such
-that the their then there these they this to
-was will with
-""".split()
-
-
-def build_hhx(builder, outdir, outname):
- builder.info('dumping stopword list...')
- f = open(path.join(outdir, outname+'.stp'), 'w')
- try:
- for word in sorted(stopwords):
- print >>f, word
- finally:
- f.close()
-
- builder.info('writing project file...')
- f = open(path.join(outdir, outname+'.hhp'), 'w')
- try:
- f.write(project_template % {'outname': outname,
- 'title': builder.config.html_title,
- 'version': builder.config.version,
- 'project': builder.config.project})
- if not outdir.endswith(os.sep):
- outdir += os.sep
- olen = len(outdir)
- for root, dirs, files in os.walk(outdir):
- staticdir = (root == path.join(outdir, '_static'))
- for fn in files:
- if (staticdir and not fn.endswith('.js')) or fn.endswith('.html'):
- print >>f, path.join(root, fn)[olen:].replace(os.sep, '\\')
- finally:
- f.close()
-
- builder.info('writing TOC file...')
- f = open(path.join(outdir, outname+'.hhc'), 'w')
- try:
- f.write(contents_header)
- # special books
- f.write('
')
+
def bulk_text_processor(self, text):
return text
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index c16a4271..885cac44 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -223,6 +223,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.this_is_the_title = 1
self.literal_whitespace = 0
self.no_contractions = 0
+ self.compact_list = 0
def astext(self):
return (HEADER % self.elements + self.highlighter.get_stylesheet() +
@@ -652,9 +653,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
raise nodes.SkipNode
def visit_bullet_list(self, node):
- self.body.append('\\begin{itemize}\n' )
+ if not self.compact_list:
+ self.body.append('\\begin{itemize}\n' )
def depart_bullet_list(self, node):
- self.body.append('\\end{itemize}\n' )
+ if not self.compact_list:
+ self.body.append('\\end{itemize}\n' )
def visit_enumerated_list(self, node):
self.body.append('\\begin{enumerate}\n' )
@@ -723,6 +726,21 @@ class LaTeXTranslator(nodes.NodeVisitor):
def depart_centered(self, node):
self.body.append('\n\\end{centering}')
+ def visit_hlist(self, node):
+ # for now, we don't support a more compact list format
+ # don't add individual itemize environments, but one for all columns
+ self.compact_list += 1
+ self.body.append('\\begin{itemize}\\setlength{\\itemsep}{0pt}'
+ '\\setlength{\\parskip}{0pt}\n')
+ def depart_hlist(self, node):
+ self.compact_list -= 1
+ self.body.append('\\end{itemize}\n')
+
+ def visit_hlistcol(self, node):
+ pass
+ def depart_hlistcol(self, node):
+ pass
+
def visit_module(self, node):
modname = node['modname']
self.body.append('\n\\declaremodule[%s]{}{%s}' % (modname.replace('_', ''),
diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py
index 5c8500fb..4718d41d 100644
--- a/sphinx/writers/text.py
+++ b/sphinx/writers/text.py
@@ -516,6 +516,16 @@ class TextTranslator(nodes.NodeVisitor):
def depart_centered(self, node):
pass
+ def visit_hlist(self, node):
+ pass
+ def depart_hlist(self, node):
+ pass
+
+ def visit_hlistcol(self, node):
+ pass
+ def depart_hlistcol(self, node):
+ pass
+
def visit_admonition(self, node):
self.new_state(0)
def depart_admonition(self, node):
diff --git a/tests/root/markup.txt b/tests/root/markup.txt
index 454762e3..777fbd2f 100644
--- a/tests/root/markup.txt
+++ b/tests/root/markup.txt
@@ -104,6 +104,16 @@ Reference lookup: [Ref1]_ (defined in another file).
`Google `_
For everything.
+.. hlist::
+ :columns: 4
+
+ * This
+ * is
+ * a horizontal
+ * list
+ * with several
+ * items
+
.. rubric:: Side note
This is a side note.
--
cgit v1.2.1
From c663958f6064cc325e271ca5416167716aa65e49 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 4 Jan 2009 22:34:00 +0100
Subject: Add changelog entry for #32.
---
CHANGES | 2 ++
1 file changed, 2 insertions(+)
diff --git a/CHANGES b/CHANGES
index 0d2ffbbb..47cfa672 100644
--- a/CHANGES
+++ b/CHANGES
@@ -91,6 +91,8 @@ New features added
- Source links in HTML are now generated with ``rel="nofollow"``.
+ - Quickstart can now generate a Windows ``make.bat`` file.
+
Release 0.5.2 (in development)
==============================
--
cgit v1.2.1
From 0d6fd15228d6192dcd4beb763dcdc9ac4652b185 Mon Sep 17 00:00:00 2001
From: Benoit Boissinot
Date: Sun, 4 Jan 2009 23:15:38 +0100
Subject: fix #30: disable the search box when javascript isn't available
---
sphinx/templates/layout.html | 21 +++++++++++++--------
sphinx/templates/search.html | 6 ++++++
2 files changed, 19 insertions(+), 8 deletions(-)
diff --git a/sphinx/templates/layout.html b/sphinx/templates/layout.html
index e011f643..29ddf014 100644
--- a/sphinx/templates/layout.html
+++ b/sphinx/templates/layout.html
@@ -63,14 +63,19 @@
{% include customsidebar %}
{%- endif %}
{%- block sidebarsearch %}
- {%- if pagename != "search" %}
-
{{ _('Quick search') }}
-
-
{{ _('Enter search terms or a module, class or function name.') }}
+ {%- if pagename != "search" %}
+
+
{{ _('Quick search') }}
+
+
{{ _('Enter search terms or a module, class or function name.') }}
+
+
{%- endif %}
{%- endblock %}
diff --git a/sphinx/templates/search.html b/sphinx/templates/search.html
index 545a459b..363f641f 100644
--- a/sphinx/templates/search.html
+++ b/sphinx/templates/search.html
@@ -3,6 +3,12 @@
{% set script_files = script_files + ['_static/searchtools.js'] %}
{% block body %}
{{ _('Search') }}
+
+
+
+ {% trans %}Please activate Javascript to enable the search functionality.{% endtrans %}
+
+
{% trans %}From here you can search these documents. Enter your search
words into the box below and click "search". Note that the search
--
cgit v1.2.1
From b94c9985e98ca0dca829942c3aac099685296349 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 4 Jan 2009 23:23:54 +0100
Subject: Simplify script snippet, break long lines.
---
sphinx/templates/layout.html | 16 +++++++++-------
sphinx/templates/search.html | 5 +++--
2 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/sphinx/templates/layout.html b/sphinx/templates/layout.html
index 29ddf014..fb0df9e0 100644
--- a/sphinx/templates/layout.html
+++ b/sphinx/templates/layout.html
@@ -44,18 +44,21 @@
{%- block sidebarrel %}
{%- if prev %}
- {% trans %}Please activate Javascript to enable the search functionality.{% endtrans %}
+ {% trans %}Please activate JavaScript to enable the search
+ functionality.{% endtrans %}
{% if dep %}{{ _('Deprecated')}}:{% endif %}
- {{ synops|e }}
- {%- endif -%}
- {% endfor %}
-
-
-{% endblock %}
diff --git a/sphinx/templates/opensearch.xml b/sphinx/templates/opensearch.xml
deleted file mode 100644
index 03875be4..00000000
--- a/sphinx/templates/opensearch.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- {{ project|e }}
- {% trans docstitle=docstitle|e %}Search {{ docstitle }}{% endtrans %}
- utf-8
-
- {{ docstitle|e }}
-{% block extra %} {# Put e.g. an element here. #} {% endblock %}
-
diff --git a/sphinx/templates/page.html b/sphinx/templates/page.html
deleted file mode 100644
index 17a93016..00000000
--- a/sphinx/templates/page.html
+++ /dev/null
@@ -1,4 +0,0 @@
-{% extends "layout.html" %}
-{% block body %}
- {{ body }}
-{% endblock %}
diff --git a/sphinx/templates/search.html b/sphinx/templates/search.html
deleted file mode 100644
index 224d87b8..00000000
--- a/sphinx/templates/search.html
+++ /dev/null
@@ -1,45 +0,0 @@
-{% extends "layout.html" %}
-{% set title = _('Search') %}
-{% set script_files = script_files + ['_static/searchtools.js'] %}
-{% block body %}
-
{{ _('Search') }}
-
-
-
- {% trans %}Please activate JavaScript to enable the search
- functionality.{% endtrans %}
-
-
-
- {% trans %}From here you can search these documents. Enter your search
- words into the box below and click "search". Note that the search
- function will automatically search for all of the words. Pages
- containing fewer words won't appear in the result list.{% endtrans %}
-
-
- {% if search_performed %}
-
{{ _('Search Results') }}
- {% if not search_results %}
-
{{ _('Your search did not match any results.') }}
- {% endif %}
- {% endif %}
-
- {% if search_results %}
-
- {% for href, caption, context in search_results %}
-
+{% endif %}
+ {{ super() }}
+{% endblock %}
diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html
new file mode 100644
index 00000000..fb0df9e0
--- /dev/null
+++ b/sphinx/themes/basic/layout.html
@@ -0,0 +1,187 @@
+{%- block doctype -%}
+
+{%- endblock %}
+{%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %}
+{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %}
+{%- macro relbar() %}
+
+
{{ _('Navigation') }}
+
+ {%- for rellink in rellinks %}
+
+ {{ rellink[3] }}
+ {%- if not loop.first %}{{ reldelim2 }}{% endif %}
{% if dep %}{{ _('Deprecated')}}:{% endif %}
+ {{ synops|e }}
+ {%- endif -%}
+ {% endfor %}
+
+
+{% endblock %}
diff --git a/sphinx/themes/basic/opensearch.xml b/sphinx/themes/basic/opensearch.xml
new file mode 100644
index 00000000..03875be4
--- /dev/null
+++ b/sphinx/themes/basic/opensearch.xml
@@ -0,0 +1,10 @@
+
+
+ {{ project|e }}
+ {% trans docstitle=docstitle|e %}Search {{ docstitle }}{% endtrans %}
+ utf-8
+
+ {{ docstitle|e }}
+{% block extra %} {# Put e.g. an element here. #} {% endblock %}
+
diff --git a/sphinx/themes/basic/page.html b/sphinx/themes/basic/page.html
new file mode 100644
index 00000000..17a93016
--- /dev/null
+++ b/sphinx/themes/basic/page.html
@@ -0,0 +1,4 @@
+{% extends "layout.html" %}
+{% block body %}
+ {{ body }}
+{% endblock %}
diff --git a/sphinx/themes/basic/search.html b/sphinx/themes/basic/search.html
new file mode 100644
index 00000000..224d87b8
--- /dev/null
+++ b/sphinx/themes/basic/search.html
@@ -0,0 +1,45 @@
+{% extends "layout.html" %}
+{% set title = _('Search') %}
+{% set script_files = script_files + ['_static/searchtools.js'] %}
+{% block body %}
+
{{ _('Search') }}
+
+
+
+ {% trans %}Please activate JavaScript to enable the search
+ functionality.{% endtrans %}
+
+
+
+ {% trans %}From here you can search these documents. Enter your search
+ words into the box below and click "search". Note that the search
+ function will automatically search for all of the words. Pages
+ containing fewer words won't appear in the result list.{% endtrans %}
+
+
+ {% if search_performed %}
+
{{ _('Search Results') }}
+ {% if not search_results %}
+
{{ _('Your search did not match any results.') }}
+ {% endif %}
+ {% endif %}
+
+ {% if search_results %}
+
+ {% for href, caption, context in search_results %}
+
')
@@ -204,8 +206,9 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder):
elif isinstance(node, addnodes.compact_paragraph):
for subnode in node:
write_toc(subnode, ullevel)
- istoctree = lambda node: isinstance(node, addnodes.compact_paragraph) and \
- node.has_key('toctree')
+ def istoctree(node):
+ return isinstance(node, addnodes.compact_paragraph) and \
+ node.has_key('toctree')
for node in tocdoc.traverse(istoctree):
write_toc(node)
f.write(contents_footer)
@@ -230,7 +233,8 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder):
write_param('Local', refs[0])
else:
for i, ref in enumerate(refs):
- write_param('Name', '[%d] %s' % (i, ref)) # XXX: better title?
+ # XXX: better title?
+ write_param('Name', '[%d] %s' % (i, ref))
write_param('Local', ref)
f.write('\n')
if subitems:
diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py
index 3b7b0c19..62da7491 100644
--- a/sphinx/builders/latex.py
+++ b/sphinx/builders/latex.py
@@ -116,12 +116,12 @@ class LaTeXBuilder(Builder):
for includefile in includefiles:
try:
self.info(darkgreen(includefile) + " ", nonl=1)
- subtree = process_tree(includefile,
- self.env.get_doctree(includefile))
+ subtree = process_tree(
+ includefile, self.env.get_doctree(includefile))
self.docnames.add(includefile)
except Exception:
- self.warn('%s: toctree contains ref to nonexisting file %r' %
- (docname, includefile))
+ self.warn('%s: toctree contains ref to nonexisting '
+ 'file %r' % (docname, includefile))
else:
sof = addnodes.start_of_file(docname=includefile)
sof.children = subtree.children
@@ -131,10 +131,12 @@ class LaTeXBuilder(Builder):
tree = self.env.get_doctree(indexfile)
tree['docname'] = indexfile
if toctree_only:
- # extract toctree nodes from the tree and put them in a fresh document
+ # extract toctree nodes from the tree and put them in a
+ # fresh document
new_tree = new_document('')
new_sect = nodes.section()
- new_sect += nodes.title(u'', u'')
+ new_sect += nodes.title(u'',
+ u'')
new_tree += new_sect
for node in tree.traverse(addnodes.toctree):
new_sect += node
diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py
index 5fcda231..b0fa9ba6 100644
--- a/sphinx/builders/linkcheck.py
+++ b/sphinx/builders/linkcheck.py
@@ -90,7 +90,8 @@ class CheckExternalLinksBuilder(Builder):
self.warn('%s:%s: broken link: %s' % (docname, lineno, uri))
else:
self.info(' - ' + purple('redirected') + ' to ' + s)
- self.write_entry('redirected', docname, lineno, uri + ' to ' + s)
+ self.write_entry('redirected', docname,
+ lineno, uri + ' to ' + s)
self.redirected[uri] = (r, s)
elif len(uri) == 0 or uri[0:7] == 'mailto:' or uri[0:4] == 'ftp:':
return
diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py
index 855b340e..7c2af1ef 100644
--- a/sphinx/builders/qthelp.py
+++ b/sphinx/builders/qthelp.py
@@ -19,7 +19,8 @@ from docutils import nodes
from sphinx import addnodes
from sphinx.builders.html import StandaloneHTMLBuilder
-_idpattern = re.compile('(?P.+) (\((?P[\w\.]+)( (?P\w+))?\))$')
+_idpattern = re.compile(
+ r'(?P.+) (\((?P[\w\.]+)( (?P\w+))?\))$')
# Qt Help Collection Project (.qhcp).
@@ -149,7 +150,8 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
for root, dirs, files in os.walk(outdir):
staticdir = (root == path.join(outdir, '_static'))
for fn in files:
- if (staticdir and not fn.endswith('.js')) or fn.endswith('.html'):
+ if (staticdir and not fn.endswith('.js')) or \
+ fn.endswith('.html'):
filename = path.join(root, fn)[olen:]
#filename = filename.replace(os.sep, '\\') # XXX
projectfiles.append(file_template % {'filename': filename})
diff --git a/sphinx/builders/text.py b/sphinx/builders/text.py
index aaafedb4..d243e5c7 100644
--- a/sphinx/builders/text.py
+++ b/sphinx/builders/text.py
@@ -31,7 +31,8 @@ class TextBuilder(Builder):
if docname not in self.env.all_docs:
yield docname
continue
- targetname = self.env.doc2path(docname, self.outdir, self.out_suffix)
+ targetname = self.env.doc2path(docname, self.outdir,
+ self.out_suffix)
try:
targetmtime = path.getmtime(targetname)
except Exception:
@@ -54,7 +55,7 @@ class TextBuilder(Builder):
destination = StringOutput(encoding='utf-8')
self.writer.write(doctree, destination)
outfilename = path.join(self.outdir, os_path(docname) + self.out_suffix)
- ensuredir(path.dirname(outfilename)) # normally different from self.outdir
+ ensuredir(path.dirname(outfilename))
try:
f = codecs.open(outfilename, 'w', 'utf-8')
try:
diff --git a/sphinx/cmdline.py b/sphinx/cmdline.py
index 20767f0d..5b57a7c3 100644
--- a/sphinx/cmdline.py
+++ b/sphinx/cmdline.py
@@ -31,7 +31,8 @@ def usage(argv, msg=None):
Sphinx v%s
Usage: %s [options] sourcedir outdir [filenames...]
Options: -b -- builder to use; default is html
- -a -- write all files; default is to only write new and changed files
+ -a -- write all files; default is to only write \
+new and changed files
-E -- don't use a saved environment, always read all files
-d -- path for the cached environment and doctree files
(default: outdir/.doctrees)
@@ -64,7 +65,8 @@ def main(argv):
return 1
if not path.isfile(path.join(srcdir, 'conf.py')) and \
'-c' not in allopts and '-C' not in allopts:
- print >>sys.stderr, 'Error: Source directory doesn\'t contain conf.py file.'
+ print >>sys.stderr, ('Error: Source directory doesn\'t '
+ 'contain conf.py file.')
return 1
outdir = path.abspath(args[1])
if not path.isdir(outdir):
@@ -103,8 +105,8 @@ def main(argv):
elif opt == '-c':
confdir = path.abspath(val)
if not path.isfile(path.join(confdir, 'conf.py')):
- print >>sys.stderr, \
- 'Error: Configuration directory doesn\'t contain conf.py file.'
+ print >>sys.stderr, ('Error: Configuration directory '
+ 'doesn\'t contain conf.py file.')
return 1
elif opt == '-C':
confdir = None
@@ -112,8 +114,8 @@ def main(argv):
try:
key, val = val.split('=')
except ValueError:
- print >>sys.stderr, \
- 'Error: -D option argument must be in the form name=value.'
+ print >>sys.stderr, ('Error: -D option argument must be '
+ 'in the form name=value.')
return 1
try:
val = int(val)
@@ -124,8 +126,8 @@ def main(argv):
try:
key, val = val.split('=')
except ValueError:
- print >>sys.stderr, \
- 'Error: -A option argument must be in the form name=value.'
+ print >>sys.stderr, ('Error: -A option argument must be '
+ 'in the form name=value.')
return 1
try:
val = int(val)
@@ -153,7 +155,8 @@ def main(argv):
except KeyboardInterrupt:
if use_pdb:
import pdb
- print >>sys.stderr, darkred('Interrupted while building, starting debugger:')
+ print >>sys.stderr, darkred('Interrupted while building, '
+ 'starting debugger:')
traceback.print_exc()
pdb.post_mortem(sys.exc_info()[2])
return 1
@@ -167,7 +170,8 @@ def main(argv):
else:
if isinstance(err, SystemMessage):
print >>sys.stderr, darkred('reST markup error:')
- print >>sys.stderr, err.args[0].encode('ascii', 'backslashreplace')
+ print >>sys.stderr, err.args[0].encode('ascii',
+ 'backslashreplace')
elif isinstance(err, SphinxError):
print >>sys.stderr, darkred('%s:' % err.category)
print >>sys.stderr, err
@@ -181,6 +185,6 @@ def main(argv):
print >>sys.stderr, ('Please also report this if it was a user '
'error, so that a better error message '
'can be provided next time.')
- print >>sys.stderr, ('Send reports to sphinx-dev@googlegroups.com. '
- 'Thanks!')
+ print >>sys.stderr, ('Send reports to '
+ 'sphinx-dev@googlegroups.com. Thanks!')
return 1
diff --git a/sphinx/config.py b/sphinx/config.py
index 5942fcf8..5c3b7dfe 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -63,7 +63,8 @@ class Config(object):
html_logo = (None, False),
html_favicon = (None, False),
html_static_path = ([], False),
- html_last_updated_fmt = (None, False), # the real default is locale-dependent
+ # the real default is locale-dependent
+ html_last_updated_fmt = (None, False),
html_use_smartypants = (True, False),
html_translator_class = (None, False),
html_sidebars = ({}, False),
diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py
index 74ac8e7f..da68cf2e 100644
--- a/sphinx/directives/code.py
+++ b/sphinx/directives/code.py
@@ -18,7 +18,7 @@ from sphinx import addnodes
from sphinx.util import parselinenos
-# ------ highlight directive --------------------------------------------------------
+# ------ highlight directive ---------------------------------------------------
def highlightlang_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
@@ -36,10 +36,11 @@ highlightlang_directive.content = 0
highlightlang_directive.arguments = (1, 0, 0)
highlightlang_directive.options = {'linenothreshold': directives.unchanged}
directives.register_directive('highlight', highlightlang_directive)
-directives.register_directive('highlightlang', highlightlang_directive) # old name
+# old name
+directives.register_directive('highlightlang', highlightlang_directive)
-# ------ code-block directive -------------------------------------------------------
+# ------ code-block directive --------------------------------------------------
def codeblock_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
@@ -56,13 +57,15 @@ directives.register_directive('code-block', codeblock_directive)
directives.register_directive('sourcecode', codeblock_directive)
-# ------ literalinclude directive ---------------------------------------------------
+# ------ literalinclude directive ----------------------------------------------
def literalinclude_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
- """Like .. include:: :literal:, but only warns if the include file is not found."""
+ """Like .. include:: :literal:, but only warns if the include file is
+ not found."""
if not state.document.settings.file_insertion_enabled:
- return [state.document.reporter.warning('File insertion disabled', line=lineno)]
+ return [state.document.reporter.warning('File insertion disabled',
+ line=lineno)]
env = state.document.settings.env
rel_fn = arguments[0]
source_dir = path.dirname(path.abspath(state_machine.input_lines.source(
@@ -133,14 +136,15 @@ def literalinclude_directive(name, arguments, options, content, lineno,
state.document.settings.env.note_dependency(rel_fn)
return [retnode]
-literalinclude_directive.options = {'linenos': directives.flag,
- 'language': directives.unchanged_required,
- 'encoding': directives.encoding,
- 'pyobject': directives.unchanged_required,
- 'lines': directives.unchanged_required,
- 'start-after': directives.unchanged_required,
- 'end-before': directives.unchanged_required,
- }
+literalinclude_directive.options = {
+ 'linenos': directives.flag,
+ 'language': directives.unchanged_required,
+ 'encoding': directives.encoding,
+ 'pyobject': directives.unchanged_required,
+ 'lines': directives.unchanged_required,
+ 'start-after': directives.unchanged_required,
+ 'end-before': directives.unchanged_required,
+}
literalinclude_directive.content = 0
literalinclude_directive.arguments = (1, 0, 0)
directives.register_directive('literalinclude', literalinclude_directive)
diff --git a/sphinx/directives/desc.py b/sphinx/directives/desc.py
index 3ffe048b..05df4ffa 100644
--- a/sphinx/directives/desc.py
+++ b/sphinx/directives/desc.py
@@ -17,7 +17,7 @@ from sphinx import addnodes
from sphinx.util import ws_re
-# ------ information units ---------------------------------------------------------
+# ------ information units -----------------------------------------------------
def desc_index_text(desctype, module, name, add_modules):
if desctype == 'function':
@@ -342,7 +342,8 @@ def parse_c_type(node, ctype):
tnode = nodes.Text(part, part)
if part[0] in string.ascii_letters+'_' and part not in stopwords:
pnode = addnodes.pending_xref(
- '', reftype='ctype', reftarget=part, modname=None, classname=None)
+ '', reftype='ctype', reftarget=part,
+ modname=None, classname=None)
pnode += tnode
node += pnode
else:
@@ -449,8 +450,10 @@ def desc_directive(desctype, arguments, options, content, lineno,
if desctype in ('function', 'data', 'class', 'exception',
'method', 'staticmethod', 'classmethod',
'attribute'):
- name, clsname = parse_py_signature(signode, sig, desctype, module, env)
- elif desctype in ('cfunction', 'cmember', 'cmacro', 'ctype', 'cvar'):
+ name, clsname = parse_py_signature(signode, sig,
+ desctype, module, env)
+ elif desctype in ('cfunction', 'cmember', 'cmacro',
+ 'ctype', 'cvar'):
name = parse_c_signature(signode, sig, desctype)
elif desctype == 'cmdoption':
optname = parse_option_desc(signode, sig)
@@ -463,7 +466,8 @@ def desc_directive(desctype, arguments, options, content, lineno,
state.document.note_explicit_target(signode)
inode['entries'].append(
('pair', _('%scommand line option; %s') %
- ((env.currprogram and env.currprogram + ' ' or ''), sig),
+ ((env.currprogram and env.currprogram + ' ' or ''),
+ sig),
targetname, targetname))
env.note_progoption(optname, targetname)
continue
@@ -473,7 +477,8 @@ def desc_directive(desctype, arguments, options, content, lineno,
continue
else:
# another registered generic x-ref directive
- rolename, indextemplate, parse_node = additional_xref_types[desctype]
+ rolename, indextemplate, parse_node = \
+ additional_xref_types[desctype]
if parse_node:
fullname = parse_node(env, sig, signode)
else:
@@ -502,8 +507,8 @@ def desc_directive(desctype, arguments, options, content, lineno,
signode.clear()
signode += addnodes.desc_name(sig, sig)
continue # we don't want an index entry here
- # only add target and index entry if this is the first description of the
- # function name in this desc block
+ # only add target and index entry if this is the first description
+ # of the function name in this desc block
if not noindex and name not in names:
fullname = (module and module + '.' or '') + name
# note target
@@ -583,7 +588,7 @@ additional_xref_types = {
del _
-# ------ target --------------------------------------------------------------------
+# ------ target ----------------------------------------------------------------
def target_directive(targettype, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
@@ -603,7 +608,8 @@ def target_directive(targettype, arguments, options, content, lineno,
if colon != -1:
indextype = indexentry[:colon].strip()
indexentry = indexentry[colon+1:].strip()
- inode = addnodes.index(entries=[(indextype, indexentry, targetname, targetname)])
+ inode = addnodes.index(entries=[(indextype, indexentry,
+ targetname, targetname)])
ret.insert(0, inode)
env.note_reftarget(rolename, fullname, targetname)
return ret
@@ -611,5 +617,5 @@ def target_directive(targettype, arguments, options, content, lineno,
target_directive.content = 0
target_directive.arguments = (1, 0, 1)
-# note, the target directive is not registered here, it is used by the application
-# when registering additional xref types
+# note, the target directive is not registered here, it is used by the
+# application when registering additional xref types
diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py
index cbf548be..18e18fd9 100644
--- a/sphinx/directives/other.py
+++ b/sphinx/directives/other.py
@@ -18,7 +18,7 @@ from sphinx.util import patfilter, ws_re, caption_ref_re, docname_join
from sphinx.util.compat import make_admonition
-# ------ the TOC tree ---------------------------------------------------------------
+# ------ the TOC tree ----------------------------------------------------------
def toctree_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
@@ -50,7 +50,8 @@ def toctree_directive(name, arguments, options, content, lineno,
docname = docname_join(env.docname, docname)
if docname not in env.found_docs:
ret.append(state.document.reporter.warning(
- 'toctree references unknown document %r' % docname, line=lineno))
+ 'toctree references unknown document %r' % docname,
+ line=lineno))
else:
includefiles.append(docname)
else:
@@ -61,8 +62,8 @@ def toctree_directive(name, arguments, options, content, lineno,
includefiles.append(docname)
if not docnames:
ret.append(state.document.reporter.warning(
- 'toctree glob pattern %r didn\'t match any documents' % entry,
- line=lineno))
+ 'toctree glob pattern %r didn\'t match any documents'
+ % entry, line=lineno))
subnode = addnodes.toctree()
subnode['includefiles'] = includefiles
subnode['includetitles'] = includetitles
@@ -78,7 +79,7 @@ toctree_directive.options = {'maxdepth': int, 'glob': directives.flag,
directives.register_directive('toctree', toctree_directive)
-# ------ section metadata ----------------------------------------------------------
+# ------ section metadata ------------------------------------------------------
def module_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
@@ -101,7 +102,8 @@ def module_directive(name, arguments, options, content, lineno,
node += nodes.emphasis('', _('Platforms: '))
node += nodes.Text(options['platform'], options['platform'])
ret.append(node)
- # the synopsis isn't printed; in fact, it is only used in the modindex currently
+ # the synopsis isn't printed; in fact, it is only used in the
+ # modindex currently
if not noindex:
indextext = _('%s (module)') % modname
inode = addnodes.index(entries=[('single', indextext,
@@ -172,7 +174,7 @@ program_directive.arguments = (1, 0, 1)
directives.register_directive('program', program_directive)
-# ------ index markup --------------------------------------------------------------
+# ------ index markup ----------------------------------------------------------
indextypes = [
'single', 'pair', 'triple',
@@ -214,7 +216,7 @@ def index_directive(name, arguments, options, content, lineno,
index_directive.arguments = (1, 0, 1)
directives.register_directive('index', index_directive)
-# ------ versionadded/versionchanged -----------------------------------------------
+# ------ versionadded/versionchanged -------------------------------------------
def version_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
@@ -241,7 +243,7 @@ directives.register_directive('versionadded', version_directive)
directives.register_directive('versionchanged', version_directive)
-# ------ see also ------------------------------------------------------------------
+# ------ see also --------------------------------------------------------------
def seealso_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
@@ -261,7 +263,7 @@ seealso_directive.arguments = (0, 1, 1)
directives.register_directive('seealso', seealso_directive)
-# ------ production list (for the reference) ---------------------------------------
+# ------ production list (for the reference) -----------------------------------
token_re = re.compile('`([a-z_]+)`')
@@ -317,7 +319,7 @@ productionlist_directive.arguments = (1, 0, 1)
directives.register_directive('productionlist', productionlist_directive)
-# ------ glossary directive ---------------------------------------------------------
+# ------ glossary directive ----------------------------------------------------
def glossary_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
@@ -354,7 +356,7 @@ glossary_directive.arguments = (0, 0, 0)
directives.register_directive('glossary', glossary_directive)
-# ------ miscellaneous markup -------------------------------------------------------
+# ------ miscellaneous markup --------------------------------------------------
def centered_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
@@ -373,7 +375,8 @@ def acks_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
node = addnodes.acks()
state.nested_parse(content, content_offset, node)
- if len(node.children) != 1 or not isinstance(node.children[0], nodes.bullet_list):
+ if len(node.children) != 1 or not isinstance(node.children[0],
+ nodes.bullet_list):
return [state.document.reporter.warning('.. acks content is not a list',
line=lineno)]
return [node]
@@ -388,9 +391,10 @@ def hlist_directive(name, arguments, options, content, lineno,
ncolumns = options.get('columns', 2)
node = nodes.paragraph()
state.nested_parse(content, content_offset, node)
- if len(node.children) != 1 or not isinstance(node.children[0], nodes.bullet_list):
- return [state.document.reporter.warning('.. hlist content is not a list',
- line=lineno)]
+ if len(node.children) != 1 or not isinstance(node.children[0],
+ nodes.bullet_list):
+ return [state.document.reporter.warning(
+ '.. hlist content is not a list', line=lineno)]
fulllist = node.children[0]
# create a hlist node where the items are distributed
npercol, nmore = divmod(len(fulllist), ncolumns)
diff --git a/sphinx/environment.py b/sphinx/environment.py
index fe4cba6b..d2804332 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -205,8 +205,8 @@ class BuildEnvironment:
self.set_warnfunc(None)
values = self.config.values
del self.config.values
- # first write to a temporary file, so that if dumping fails, the existing
- # environment won't be overwritten
+ # first write to a temporary file, so that if dumping fails,
+ # the existing environment won't be overwritten
picklefile = open(filename + '.tmp', 'wb')
# remove potentially pickling-problematic values from config
for key, val in vars(self.config).items():
@@ -244,13 +244,14 @@ class BuildEnvironment:
# this is to invalidate old pickles
self.version = ENV_VERSION
- # All "docnames" here are /-separated and relative and exclude the source suffix.
+ # All "docnames" here are /-separated and relative and exclude
+ # the source suffix.
self.found_docs = set() # contains all existing docnames
self.all_docs = {} # docname -> mtime at the time of build
# contains all built docnames
- self.dependencies = {} # docname -> set of dependent file names, relative to
- # documentation root
+ self.dependencies = {} # docname -> set of dependent file
+ # names, relative to documentation root
# File metadata
self.metadata = {} # docname -> dict of metadata items
@@ -259,30 +260,34 @@ class BuildEnvironment:
self.titles = {} # docname -> title node
self.tocs = {} # docname -> table of contents nodetree
self.toc_num_entries = {} # docname -> number of real entries
- # used to determine when to show the TOC in a sidebar
- # (don't show if it's only one item)
+ # used to determine when to show the TOC
+ # in a sidebar (don't show if it's only one item)
+
self.toctree_includes = {} # docname -> list of toctree includefiles
- self.files_to_rebuild = {} # docname -> set of files (containing its TOCs)
- # to rebuild too
+ self.files_to_rebuild = {} # docname -> set of files
+ # (containing its TOCs) to rebuild too
self.glob_toctrees = set() # docnames that have :glob: toctrees
# X-ref target inventory
self.descrefs = {} # fullname -> docname, desctype
self.filemodules = {} # docname -> [modules]
- self.modules = {} # modname -> docname, synopsis, platform, deprecated
+ self.modules = {} # modname -> docname, synopsis,
+ # platform, deprecated
self.labels = {} # labelname -> docname, labelid, sectionname
self.anonlabels = {} # labelname -> docname, labelid
self.progoptions = {} # (program, name) -> docname, labelid
self.reftargets = {} # (type, name) -> docname, labelid
- # where type is term, token, envvar, citation
+ # type: term, token, envvar, citation
# Other inventories
self.indexentries = {} # docname -> list of
# (type, string, target, aliasname)
- self.versionchanges = {} # version -> list of
- # (type, docname, lineno, module, descname, content)
- self.images = FilenameUniqDict() # absolute path -> (docnames, unique filename)
- self.dlfiles = FilenameUniqDict() # absolute path -> (docnames, unique filename)
+ self.versionchanges = {} # version -> list of (type, docname,
+ # lineno, module, descname, content)
+
+ # these map absolute path -> (docnames, unique filename)
+ self.images = FilenameUniqDict()
+ self.dlfiles = FilenameUniqDict()
# These are set while parsing a file
self.docname = None # current document name
@@ -362,7 +367,8 @@ class BuildEnvironment:
"""
suffix = suffix or self.config.source_suffix
if base is True:
- return path.join(self.srcdir, docname.replace(SEP, path.sep)) + suffix
+ return path.join(self.srcdir,
+ docname.replace(SEP, path.sep)) + suffix
elif base is None:
return docname.replace(SEP, path.sep) + suffix
else:
@@ -375,8 +381,10 @@ class BuildEnvironment:
exclude_dirs = [d.replace(SEP, path.sep) for d in config.exclude_dirs]
exclude_trees = [d.replace(SEP, path.sep) for d in config.exclude_trees]
self.found_docs = set(get_matching_docs(
- self.srcdir, config.source_suffix, exclude_docs=set(config.unused_docs),
- exclude_dirs=exclude_dirs, exclude_trees=exclude_trees,
+ self.srcdir, config.source_suffix,
+ exclude_docs=set(config.unused_docs),
+ exclude_dirs=exclude_dirs,
+ exclude_trees=exclude_trees,
exclude_dirnames=['_sources'] + config.exclude_dirnames))
def get_outdated_files(self, config_changed):
@@ -428,16 +436,17 @@ class BuildEnvironment:
return added, changed, removed
def update(self, config, srcdir, doctreedir, app=None):
- """(Re-)read all files new or changed since last update. Yields a summary
- and then docnames as it processes them. Store all environment docnames
- in the canonical format (ie using SEP as a separator in place of
- os.path.sep)."""
+ """(Re-)read all files new or changed since last update.
+ Yields a summary and then docnames as it processes them.
+ Store all environment docnames in the canonical format
+ (ie using SEP as a separator in place of os.path.sep)."""
config_changed = False
if self.config is None:
msg = '[new config] '
config_changed = True
else:
- # check if a config value was changed that affects how doctrees are read
+ # check if a config value was changed that affects how
+ # doctrees are read
for key, descr in config.config_values.iteritems():
if not descr[1]:
continue
@@ -577,7 +586,8 @@ class BuildEnvironment:
if save_parsed:
# save the parsed doctree
- doctree_filename = self.doc2path(docname, self.doctreedir, '.doctree')
+ doctree_filename = self.doc2path(docname, self.doctreedir,
+ '.doctree')
dirname = path.dirname(doctree_filename)
if not path.isdir(dirname):
os.makedirs(dirname)
@@ -638,7 +648,8 @@ class BuildEnvironment:
node['candidates'] = candidates = {}
imguri = node['uri']
if imguri.find('://') != -1:
- self.warn(docname, 'Nonlocal image URI found: %s' % imguri, node.line)
+ self.warn(docname, 'Nonlocal image URI found: %s' % imguri,
+ node.line)
candidates['?'] = imguri
continue
# imgpath is the image path *from srcdir*
@@ -660,7 +671,8 @@ class BuildEnvironment:
finally:
f.close()
except (OSError, IOError):
- self.warn(docname, 'Image file %s not readable' % filename)
+ self.warn(docname,
+ 'Image file %s not readable' % filename)
if imgtype:
candidates['image/' + imgtype] = new_imgpath
else:
@@ -725,7 +737,8 @@ class BuildEnvironment:
continue
if name in self.labels:
self.warn(docname, 'duplicate label %s, ' % name +
- 'other instance in %s' % self.doc2path(self.labels[name][0]),
+ 'other instance in ' +
+ self.doc2path(self.labels[name][0]),
node.line)
self.anonlabels[name] = docname, labelid
if node.tagname == 'section':
@@ -835,7 +848,8 @@ class BuildEnvironment:
if fullname in self.descrefs:
self.warn(self.docname,
'duplicate canonical description name %s, ' % fullname +
- 'other instance in %s' % self.doc2path(self.descrefs[fullname][0]),
+ 'other instance in ' +
+ self.doc2path(self.descrefs[fullname][0]),
line)
self.descrefs[fullname] = (self.docname, desctype)
@@ -851,7 +865,8 @@ class BuildEnvironment:
def note_versionchange(self, type, version, node, lineno):
self.versionchanges.setdefault(version, []).append(
- (type, self.docname, lineno, self.currmodule, self.currdesc, node.astext()))
+ (type, self.docname, lineno, self.currmodule, self.currdesc,
+ node.astext()))
def note_dependency(self, filename):
basename = path.dirname(self.doc2path(self.docname, base=None))
@@ -915,7 +930,8 @@ class BuildEnvironment:
def _walk_depth(node, depth, maxdepth, titleoverrides):
"""Utility: Cut a TOC at a specified depth."""
for subnode in node.children[:]:
- if isinstance(subnode, (addnodes.compact_paragraph, nodes.list_item)):
+ if isinstance(subnode, (addnodes.compact_paragraph,
+ nodes.list_item)):
subnode['classes'].append('toctree-l%d' % (depth-1))
_walk_depth(subnode, depth, maxdepth, titleoverrides)
elif isinstance(subnode, nodes.bullet_list):
@@ -934,27 +950,30 @@ class BuildEnvironment:
toc = self.tocs[includefile].deepcopy()
if not toc.children:
# empty toc means: no titles will show up in the toctree
- self.warn(docname, 'toctree contains reference to document '
- '%r that doesn\'t have a title: no link will be '
- 'generated' % includefile)
+ self.warn(docname,
+ 'toctree contains reference to document '
+ '%r that doesn\'t have a title: no link '
+ 'will be generated' % includefile)
except KeyError:
# this is raised if the included file does not exist
- self.warn(docname, 'toctree contains reference to nonexisting '
- 'document %r' % includefile)
+ self.warn(docname, 'toctree contains reference to '
+ 'nonexisting document %r' % includefile)
else:
# if titles_only is given, only keep the main title and
# sub-toctrees
if titles_only:
- # delete everything but the toplevel title(s) and toctrees
+ # delete everything but the toplevel title(s)
+ # and toctrees
for toplevel in toc:
# nodes with length 1 don't have any children anyway
if len(toplevel) > 1:
- subtoctrees = toplevel.traverse(addnodes.toctree)
- toplevel[1][:] = subtoctrees
+ subtrees = toplevel.traverse(addnodes.toctree)
+ toplevel[1][:] = subtrees
# resolve all sub-toctrees
for toctreenode in toc.traverse(addnodes.toctree):
i = toctreenode.parent.index(toctreenode) + 1
- for item in _entries_from_toctree(toctreenode, subtree=True):
+ for item in _entries_from_toctree(toctreenode,
+ subtree=True):
toctreenode.parent.insert(i, item)
i += 1
toctreenode.parent.remove(toctreenode)
@@ -993,8 +1012,9 @@ class BuildEnvironment:
refnode.children = [nodes.Text(newtitle)]
return newnode
- descroles = frozenset(('data', 'exc', 'func', 'class', 'const', 'attr', 'obj',
- 'meth', 'cfunc', 'cmember', 'cdata', 'ctype', 'cmacro'))
+ descroles = frozenset(('data', 'exc', 'func', 'class', 'const',
+ 'attr', 'obj', 'meth', 'cfunc', 'cmember',
+ 'cdata', 'ctype', 'cmacro'))
def resolve_references(self, doctree, fromdocname, builder):
reftarget_roles = set(('token', 'term', 'citation'))
@@ -1011,30 +1031,32 @@ class BuildEnvironment:
try:
if typ == 'ref':
if node['refcaption']:
- # reference to anonymous label; the reference uses the supplied
- # link caption
+ # reference to anonymous label; the reference uses
+ # the supplied link caption
docname, labelid = self.anonlabels.get(target, ('',''))
sectname = node.astext()
if not docname:
newnode = doctree.reporter.system_message(
2, 'undefined label: %s' % target)
else:
- # reference to the named label; the final node will contain the
- # section name after the label
- docname, labelid, sectname = self.labels.get(target, ('','',''))
+ # reference to the named label; the final node will
+ # contain the section name after the label
+ docname, labelid, sectname = self.labels.get(target,
+ ('','',''))
if not docname:
newnode = doctree.reporter.system_message(
- 2, 'undefined label: %s -- if you don\'t ' % target +
- 'give a link caption the label must precede a section '
- 'header.')
+ 2, 'undefined label: %s' % target +
+ ' -- if you don\'t give a link caption '
+ 'the label must precede a section header.')
if docname:
newnode = nodes.reference('', '')
innernode = nodes.emphasis(sectname, sectname)
if docname == fromdocname:
newnode['refid'] = labelid
else:
- # set more info in contnode in case the get_relative_uri call
- # raises NoUri, the builder will then have to resolve these
+ # set more info in contnode; in case the
+ # get_relative_uri call raises NoUri,
+ # the builder will then have to resolve these
contnode = addnodes.pending_xref('')
contnode['refdocname'] = docname
contnode['refsectname'] = sectname
@@ -1044,8 +1066,8 @@ class BuildEnvironment:
newnode['refuri'] += '#' + labelid
newnode.append(innernode)
elif typ == 'doc':
- # directly reference to document by source name; can be absolute
- # or relative
+ # directly reference to document by source name;
+ # can be absolute or relative
docname = docname_join(fromdocname, target)
if docname not in self.all_docs:
newnode = doctree.reporter.system_message(
@@ -1077,7 +1099,8 @@ class BuildEnvironment:
newnode.append(contnode)
elif typ == 'option':
progname = node['refprogram']
- docname, labelid = self.progoptions.get((progname, target), ('', ''))
+ docname, labelid = self.progoptions.get((progname, target),
+ ('', ''))
if not docname:
newnode = contnode
else:
@@ -1089,13 +1112,16 @@ class BuildEnvironment:
fromdocname, docname) + '#' + labelid
newnode.append(contnode)
elif typ in reftarget_roles:
- docname, labelid = self.reftargets.get((typ, target), ('', ''))
+ docname, labelid = self.reftargets.get((typ, target),
+ ('', ''))
if not docname:
if typ == 'term':
- self.warn(fromdocname, 'term not in glossary: %s' % target,
+ self.warn(fromdocname,
+ 'term not in glossary: %s' % target,
node.line)
elif typ == 'citation':
- self.warn(fromdocname, 'citation not found: %s' % target,
+ self.warn(fromdocname,
+ 'citation not found: %s' % target,
node.line)
newnode = contnode
else:
@@ -1110,8 +1136,8 @@ class BuildEnvironment:
docname, synopsis, platform, deprecated = \
self.modules.get(target, ('','','', ''))
if not docname:
- newnode = builder.app.emit_firstresult('missing-reference',
- self, node, contnode)
+ newnode = builder.app.emit_firstresult(
+ 'missing-reference', self, node, contnode)
if not newnode:
newnode = contnode
elif docname == fromdocname:
@@ -1133,8 +1159,8 @@ class BuildEnvironment:
name, desc = self.find_desc(modname, clsname,
target, typ, searchorder)
if not desc:
- newnode = builder.app.emit_firstresult('missing-reference',
- self, node, contnode)
+ newnode = builder.app.emit_firstresult(
+ 'missing-reference', self, node, contnode)
if not newnode:
newnode = contnode
else:
@@ -1148,7 +1174,8 @@ class BuildEnvironment:
newnode['reftitle'] = name
newnode.append(contnode)
else:
- raise RuntimeError('unknown xfileref node encountered: %s' % node)
+ raise RuntimeError('unknown xfileref node encountered: %s'
+ % node)
except NoUri:
newnode = contnode
if newnode:
@@ -1232,8 +1259,10 @@ class BuildEnvironment:
m = _fixre.match(key)
if m:
if oldkey == m.group(1):
- # prefixes match: add entry as subitem of the previous entry
- oldsubitems.setdefault(m.group(2), [[], {}])[0].extend(targets)
+ # prefixes match: add entry as subitem of the
+ # previous entry
+ oldsubitems.setdefault(m.group(2), [[], {}])[0].\
+ extend(targets)
del newlist[i]
continue
oldkey = m.group(1)
@@ -1253,7 +1282,8 @@ class BuildEnvironment:
else:
# get all other symbols under one heading
return 'Symbols'
- return [(key, list(group)) for (key, group) in groupby(newlist, keyfunc)]
+ return [(key, list(group))
+ for (key, group) in groupby(newlist, keyfunc)]
def collect_relations(self):
relations = {}
@@ -1292,7 +1322,8 @@ class BuildEnvironment:
# else it will stay None
# same for children
if includes:
- for subindex, args in enumerate(izip(includes, [None] + includes,
+ for subindex, args in enumerate(izip(includes,
+ [None] + includes,
includes[1:] + [None])):
collect([(docname, subindex)] + parents, *args)
relations[docname] = [parents[0][0], previous, next]
@@ -1360,14 +1391,16 @@ class BuildEnvironment:
def find_keyword(self, keyword, avoid_fuzzy=False, cutoff=0.6, n=20):
"""
- Find keyword matches for a keyword. If there's an exact match, just return
- it, else return a list of fuzzy matches if avoid_fuzzy isn't True.
+ Find keyword matches for a keyword. If there's an exact match,
+ just return it, else return a list of fuzzy matches if avoid_fuzzy
+ isn't True.
Keywords searched are: first modules, then descrefs.
Returns: None if nothing found
(type, docname, anchorname) if exact match found
- list of (quality, type, docname, anchorname, description) if fuzzy
+ list of (quality, type, docname, anchorname, description)
+ if fuzzy
"""
if keyword in self.modules:
diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py
index d0512f2b..c005e36a 100644
--- a/sphinx/ext/autodoc.py
+++ b/sphinx/ext/autodoc.py
@@ -54,7 +54,7 @@ def get_method_type(obj):
(isinstance(obj, MethodType) and obj.im_self is not None):
return 'classmethod'
elif isinstance(obj, FunctionType) or \
- (isinstance(obj, BuiltinFunctionType) and obj.__self__ is not None):
+ (isinstance(obj, BuiltinFunctionType) and obj.__self__ is not None):
return 'staticmethod'
else:
return 'method'
@@ -193,7 +193,8 @@ class RstGenerator(object):
docstrings.append(obj.__doc__)
# skip some lines in module docstrings if configured (deprecated!)
- if what == 'module' and self.env.config.automodule_skip_lines and docstrings:
+ if what == 'module' and self.env.config.automodule_skip_lines \
+ and docstrings:
docstrings[0] = '\n'.join(docstrings[0].splitlines()
[self.env.config.automodule_skip_lines:])
@@ -212,7 +213,8 @@ class RstGenerator(object):
docstrings.append(initdocstring)
# the default is only the class docstring
- # make sure we have Unicode docstrings, then sanitize and split into lines
+ # make sure we have Unicode docstrings, then sanitize and split
+ # into lines
return [prepare_docstring(force_decode(docstring, encoding))
for docstring in docstrings]
@@ -233,8 +235,9 @@ class RstGenerator(object):
Returns a tuple of: the full name, the module name, a path of
names to get via getattr, the signature and return annotation.
"""
- # first, parse the definition -- auto directives for classes and functions
- # can contain a signature which is then used instead of an autogenerated one
+ # first, parse the definition -- auto directives for classes and
+ # functions can contain a signature which is then used instead of
+ # an autogenerated one
try:
mod, path, base, args, retann = py_ext_sig_re.match(name).groups()
except:
@@ -261,8 +264,8 @@ class RstGenerator(object):
if path:
mod = path.rstrip('.')
else:
- # if documenting a toplevel object without explicit module, it can
- # be contained in another auto directive ...
+ # if documenting a toplevel object without explicit module,
+ # it can be contained in another auto directive ...
if hasattr(self.env, 'autodoc_current_module'):
mod = self.env.autodoc_current_module
# ... or in the scope of a module directive
@@ -276,8 +279,9 @@ class RstGenerator(object):
mod_cls = path.rstrip('.')
else:
mod_cls = None
- # if documenting a class-level object without path, there must be a
- # current class, either from a parent auto directive ...
+ # if documenting a class-level object without path,
+ # there must be a current class, either from a parent
+ # auto directive ...
if hasattr(self.env, 'autodoc_current_class'):
mod_cls = self.env.autodoc_current_class
# ... or from a class directive
@@ -313,7 +317,8 @@ class RstGenerator(object):
args = None
getargs = True
if what == 'class':
- # for classes, the relevant signature is the __init__ method's
+ # for classes, the relevant signature is the
+ # __init__ method's
obj = getattr(obj, '__init__', None)
# classes without __init__ method, default __init__ or
# __init__ written in C?
@@ -334,8 +339,9 @@ class RstGenerator(object):
args = None
err = e
- result = self.env.app.emit_firstresult('autodoc-process-signature', what,
- name, obj, self.options, args, retann)
+ result = self.env.app.emit_firstresult(
+ 'autodoc-process-signature', what, name, obj,
+ self.options, args, retann)
if result:
args, retann = result
@@ -347,22 +353,24 @@ class RstGenerator(object):
else:
return ''
- def generate(self, what, name, members, add_content, indent=u'', check_module=False,
- no_docstring=False):
+ def generate(self, what, name, members, add_content, indent=u'',
+ check_module=False, no_docstring=False):
"""
Generate reST for the object in self.result.
"""
mod, objpath, args, retann = self.resolve_name(what, name)
if not mod:
# need a module to import
- self.warn('don\'t know which module to import for autodocumenting %r '
- '(try placing a "module" or "currentmodule" directive in the '
- 'document, or giving an explicit module name)' % name)
+ self.warn('don\'t know which module to import for autodocumenting '
+ '%r (try placing a "module" or "currentmodule" directive '
+ 'in the document, or giving an explicit module name)'
+ % name)
return
# fully-qualified name
fullname = mod + (objpath and '.' + '.'.join(objpath) or '')
- # the name to put into the generated directive -- doesn't contain the module
+ # the name to put into the generated directive -- doesn't contain
+ # the module
name_in_directive = '.'.join(objpath) or mod
# now, import the module and get object to document
@@ -372,8 +380,8 @@ class RstGenerator(object):
for part in objpath:
todoc = getattr(todoc, part)
except (ImportError, AttributeError), err:
- self.warn('autodoc can\'t import/find %s %r, it reported error: "%s", '
- 'please check your spelling and sys.path' %
+ self.warn('autodoc can\'t import/find %s %r, it reported error: '
+ '"%s", please check your spelling and sys.path' %
(what, str(fullname), err))
return
@@ -388,7 +396,7 @@ class RstGenerator(object):
else:
self.filename_set.add(analyzer.srcname)
- # check __module__ of object if wanted (for members not given explicitly)
+ # check __module__ of object for members not given explicitly
if check_module:
if hasattr(todoc, '__module__'):
if todoc.__module__ != mod:
@@ -417,16 +425,16 @@ class RstGenerator(object):
if what == 'module':
# Add some module-specific options
if self.options.synopsis:
- self.result.append(indent + u' :synopsis: ' + self.options.synopsis,
- '')
+ self.result.append(indent + u' :synopsis: ' +
+ self.options.synopsis, '')
if self.options.platform:
- self.result.append(indent + u' :platform: ' + self.options.platform,
- '')
+ self.result.append(indent + u' :platform: ' +
+ self.options.platform, '')
if self.options.deprecated:
self.result.append(indent + u' :deprecated:', '')
else:
- # Be explicit about the module, this is necessary since .. class:: doesn't
- # support a prepended module name
+ # Be explicit about the module, this is necessary since .. class::
+ # doesn't support a prepended module name
self.result.append(indent + u' :module: %s' % mod, '')
if self.options.noindex:
self.result.append(indent + u' :noindex:', '')
@@ -439,7 +447,8 @@ class RstGenerator(object):
u':class:`%s`' % b.__name__ or
u':class:`%s.%s`' % (b.__module__, b.__name__)
for b in todoc.__bases__]
- self.result.append(indent + _(u' Bases: %s') % ', '.join(bases),
+ self.result.append(indent + _(u' Bases: %s') %
+ ', '.join(bases),
'')
self.result.append(u'', '')
@@ -513,7 +522,8 @@ class RstGenerator(object):
# base classes
all_members = inspect.getmembers(todoc)
else:
- # __dict__ contains only the members directly defined in the class
+ # __dict__ contains only the members directly defined
+ # in the class
all_members = sorted(todoc.__dict__.iteritems())
else:
all_members = [(mname, getattr(todoc, mname)) for mname in members]
@@ -524,7 +534,8 @@ class RstGenerator(object):
for (membername, member) in all_members:
# if isattr is True, the member is documented as an attribute
isattr = False
- # if content is not None, no extra content from docstrings will be added
+ # if content is not None, no extra content from docstrings
+ # will be added
content = None
if want_all_members and membername.startswith('_'):
@@ -536,15 +547,18 @@ class RstGenerator(object):
skip = False
isattr = True
else:
- # ignore undocumented members if :undoc-members: is not given
+ # ignore undocumented members if :undoc-members:
+ # is not given
doc = getattr(member, '__doc__', None)
skip = not self.options.undoc_members and not doc
- # give the user a chance to decide whether this member should be skipped
+ # give the user a chance to decide whether this member
+ # should be skipped
if self.env.app:
# let extensions preprocess docstrings
skip_user = self.env.app.emit_firstresult(
- 'autodoc-skip-member', what, membername, member, skip, self.options)
+ 'autodoc-skip-member', what, membername, member,
+ skip, self.options)
if skip_user is not None:
skip = skip_user
if skip:
@@ -560,8 +574,9 @@ class RstGenerator(object):
if member.__name__ != membername:
# assume it's aliased
memberwhat = 'data'
- content = ViewList([_('alias of :class:`%s`') % member.__name__],
- source='')
+ content = ViewList(
+ [_('alias of :class:`%s`') % member.__name__],
+ source='')
elif issubclass(member, base_exception):
memberwhat = 'exception'
else:
@@ -577,8 +592,9 @@ class RstGenerator(object):
if member.__name__ != membername:
# assume it's aliased
memberwhat = 'attribute'
- content = ViewList([_('alias of :class:`%s`') % member.__name__],
- source='')
+ content = ViewList(
+ [_('alias of :class:`%s`') % member.__name__],
+ source='')
else:
memberwhat = 'class'
elif isdescriptor(member):
@@ -586,8 +602,8 @@ class RstGenerator(object):
else:
continue
- # give explicitly separated module name, so that members of inner classes
- # can be documented
+ # give explicitly separated module name, so that members
+ # of inner classes can be documented
full_membername = mod + '::' + '.'.join(objpath + [membername])
self.generate(memberwhat, full_membername, ['__all__'],
add_content=content, no_docstring=bool(content),
@@ -653,13 +669,17 @@ def members_option(arg):
def setup(app):
- mod_options = {'members': members_option, 'undoc-members': directives.flag,
- 'noindex': directives.flag, 'inherited-members': directives.flag,
- 'show-inheritance': directives.flag, 'synopsis': lambda x: x,
- 'platform': lambda x: x, 'deprecated': directives.flag}
- cls_options = {'members': members_option, 'undoc-members': directives.flag,
- 'noindex': directives.flag, 'inherited-members': directives.flag,
- 'show-inheritance': directives.flag}
+ mod_options = {
+ 'members': members_option, 'undoc-members': directives.flag,
+ 'noindex': directives.flag, 'inherited-members': directives.flag,
+ 'show-inheritance': directives.flag, 'synopsis': lambda x: x,
+ 'platform': lambda x: x, 'deprecated': directives.flag
+ }
+ cls_options = {
+ 'members': members_option, 'undoc-members': directives.flag,
+ 'noindex': directives.flag, 'inherited-members': directives.flag,
+ 'show-inheritance': directives.flag
+ }
app.add_directive('automodule', automodule_directive,
1, (1, 0, 1), **mod_options)
app.add_directive('autoclass', autoclass_directive,
diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py
index a3dc5889..964e58ee 100644
--- a/sphinx/ext/coverage.py
+++ b/sphinx/ext/coverage.py
@@ -53,17 +53,17 @@ class CoverageBuilder(Builder):
self.c_ignorexps = {}
for (name, exps) in self.config.coverage_ignore_c_items.iteritems():
- self.c_ignorexps[name] = compile_regex_list('coverage_ignore_c_items',
- exps, self.warn)
- self.mod_ignorexps = compile_regex_list('coverage_ignore_modules',
- self.config.coverage_ignore_modules,
- self.warn)
- self.cls_ignorexps = compile_regex_list('coverage_ignore_classes',
- self.config.coverage_ignore_classes,
- self.warn)
- self.fun_ignorexps = compile_regex_list('coverage_ignore_functions',
- self.config.coverage_ignore_functions,
- self.warn)
+ self.c_ignorexps[name] = compile_regex_list(
+ 'coverage_ignore_c_items', exps, self.warn)
+ self.mod_ignorexps = compile_regex_list(
+ 'coverage_ignore_modules', self.config.coverage_ignore_modules,
+ self.warn)
+ self.cls_ignorexps = compile_regex_list(
+ 'coverage_ignore_classes', self.config.coverage_ignore_classes,
+ self.warn)
+ self.fun_ignorexps = compile_regex_list(
+ 'coverage_ignore_functions', self.config.coverage_ignore_functions,
+ self.warn)
def get_outdated_docs(self):
return 'coverage overview'
@@ -128,7 +128,8 @@ class CoverageBuilder(Builder):
try:
mod = __import__(mod_name, fromlist=['foo'])
except ImportError, err:
- self.warn('module %s could not be imported: %s' % (mod_name, err))
+ self.warn('module %s could not be imported: %s' %
+ (mod_name, err))
self.py_undoc[mod_name] = {'error': err}
continue
@@ -168,7 +169,8 @@ class CoverageBuilder(Builder):
attrs = []
- for attr_name, attr in inspect.getmembers(obj, inspect.ismethod):
+ for attr_name, attr in inspect.getmembers(
+ obj, inspect.ismethod):
if attr_name[0] == '_':
# starts with an underscore, ignore it
continue
diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py
index d6697f98..d2aacaa4 100644
--- a/sphinx/ext/doctest.py
+++ b/sphinx/ext/doctest.py
@@ -262,13 +262,15 @@ Doctest summary
for group in groups.itervalues():
group.add_code(code)
if self.config.doctest_global_setup:
- code = TestCode(self.config.doctest_global_setup, 'testsetup', lineno=0)
+ code = TestCode(self.config.doctest_global_setup,
+ 'testsetup', lineno=0)
for group in groups.itervalues():
group.add_code(code, prepend=True)
if not groups:
return
- self._out('\nDocument: %s\n----------%s\n' % (docname, '-'*len(docname)))
+ self._out('\nDocument: %s\n----------%s\n' %
+ (docname, '-'*len(docname)))
for group in groups.itervalues():
self.test_group(group, self.env.doc2path(docname, base=None))
# Separately count results from setup code
@@ -287,7 +289,8 @@ Doctest summary
ns = {}
examples = []
for setup in group.setup:
- examples.append(doctest.Example(setup.code, '', lineno=setup.lineno))
+ examples.append(doctest.Example(setup.code, '',
+ lineno=setup.lineno))
if examples:
# simulate a doctest with the setup code
setup_doctest = doctest.DocTest(examples, {},
diff --git a/sphinx/ext/ifconfig.py b/sphinx/ext/ifconfig.py
index f622ec49..af3975b8 100644
--- a/sphinx/ext/ifconfig.py
+++ b/sphinx/ext/ifconfig.py
@@ -13,8 +13,8 @@
This stuff is only included in the built docs for unstable versions.
The argument for ``ifconfig`` is a plain Python expression, evaluated in the
- namespace of the project configuration (that is, all variables from ``conf.py``
- are available.)
+ namespace of the project configuration (that is, all variables from
+ ``conf.py`` are available.)
:copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
diff --git a/sphinx/ext/jsmath.py b/sphinx/ext/jsmath.py
index 1bea3304..e51af455 100644
--- a/sphinx/ext/jsmath.py
+++ b/sphinx/ext/jsmath.py
@@ -32,7 +32,8 @@ def html_visit_displaymath(self, node):
if i == 0:
# necessary to e.g. set the id property correctly
if node['number']:
- self.body.append('(%s)' % node['number'])
+ self.body.append('(%s)' %
+ node['number'])
self.body.append(self.starttag(node, 'div', CLASS='math'))
else:
# but only once!
diff --git a/sphinx/ext/pngmath.py b/sphinx/ext/pngmath.py
index dc1d2ee8..61df53a5 100644
--- a/sphinx/ext/pngmath.py
+++ b/sphinx/ext/pngmath.py
@@ -117,9 +117,9 @@ def render_math(self, math):
if err.errno != 2: # No such file or directory
raise
if not hasattr(self.builder, '_mathpng_warned_latex'):
- self.builder.warn('LaTeX command %r cannot be run (needed for math '
- 'display), check the pngmath_latex setting' %
- self.builder.config.pngmath_latex)
+ self.builder.warn('LaTeX command %r cannot be run (needed for '
+ 'math display), check the pngmath_latex '
+ 'setting' % self.builder.config.pngmath_latex)
self.builder._mathpng_warned_latex = True
return relfn, None
finally:
@@ -127,8 +127,8 @@ def render_math(self, math):
stdout, stderr = p.communicate()
if p.returncode != 0:
- raise MathExtError('latex exited with error:\n[stderr]\n%s\n[stdout]\n%s'
- % (stderr, stdout))
+ raise MathExtError('latex exited with error:\n[stderr]\n%s\n'
+ '[stdout]\n%s' % (stderr, stdout))
ensuredir(path.dirname(outfn))
# use some standard dvipng arguments
@@ -146,15 +146,15 @@ def render_math(self, math):
if err.errno != 2: # No such file or directory
raise
if not hasattr(self.builder, '_mathpng_warned_dvipng'):
- self.builder.warn('dvipng command %r cannot be run (needed for math '
- 'display), check the pngmath_dvipng setting' %
- self.builder.config.pngmath_dvipng)
+ self.builder.warn('dvipng command %r cannot be run (needed for '
+ 'math display), check the pngmath_dvipng setting'
+ % self.builder.config.pngmath_dvipng)
self.builder._mathpng_warned_dvipng = True
return relfn, None
stdout, stderr = p.communicate()
if p.returncode != 0:
- raise MathExtError('dvipng exited with error:\n[stderr]\n%s\n[stdout]\n%s'
- % (stderr, stdout))
+ raise MathExtError('dvipng exited with error:\n[stderr]\n%s\n'
+ '[stdout]\n%s' % (stderr, stdout))
depth = None
if use_preview:
for line in stdout.splitlines():
@@ -187,7 +187,8 @@ def html_visit_math(self, node):
raise nodes.SkipNode
self.body.append('' %
(fname, self.encode(node['latex']).strip(),
- depth and 'style="vertical-align: %dpx" ' % (-depth) or ''))
+ depth and 'style="vertical-align: %dpx" ' %
+ (-depth) or ''))
raise nodes.SkipNode
def html_visit_displaymath(self, node):
diff --git a/sphinx/ext/refcounting.py b/sphinx/ext/refcounting.py
index c31d6627..cad9d7f1 100644
--- a/sphinx/ext/refcounting.py
+++ b/sphinx/ext/refcounting.py
@@ -55,7 +55,8 @@ class Refcounts(dict):
refcount = None
else:
refcount = int(refcount)
- # Update the entry with the new parameter or the result information.
+ # Update the entry with the new parameter or the result
+ # information.
if arg:
entry.args.append((arg, type, refcount))
else:
@@ -81,7 +82,8 @@ class Refcounts(dict):
if entry.result_refs is None:
rc += "Always NULL."
else:
- rc += (entry.result_refs and "New" or "Borrowed") + " reference."
+ rc += (entry.result_refs and "New" or "Borrowed") + \
+ " reference."
node.insert(0, refcount(rc, rc))
diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py
index dac90660..b7a8d500 100644
--- a/sphinx/ext/todo.py
+++ b/sphinx/ext/todo.py
@@ -72,8 +72,8 @@ def process_todo_nodes(app, doctree, fromdocname):
para = nodes.paragraph()
filename = env.doc2path(todo_info['docname'], base=None)
description = (
- _('(The original entry is located in %s, line %d and can be found ') %
- (filename, todo_info['lineno']))
+ _('(The original entry is located in %s, line %d and '
+ 'can be found ') % (filename, todo_info['lineno']))
para += nodes.Text(description, description)
# Create a reference
diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py
index 61cea214..61c413d7 100644
--- a/sphinx/highlighting.py
+++ b/sphinx/highlighting.py
@@ -101,7 +101,8 @@ class PygmentsBridge(object):
style = NoneStyle
elif '.' in stylename:
module, stylename = stylename.rsplit('.', 1)
- style = getattr(__import__(module, None, None, ['__name__']), stylename)
+ style = getattr(__import__(module, None, None, ['__name__']),
+ stylename)
else:
style = get_style_by_name(stylename)
if dest == 'html':
diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py
index c2086da5..d195d6c3 100644
--- a/sphinx/pycode/__init__.py
+++ b/sphinx/pycode/__init__.py
@@ -91,7 +91,8 @@ class AttrDocVisitor(nodes.NodeVisitor):
return
if prev.type == sym.simple_stmt and \
prev[0].type == sym.expr_stmt and _eq in prev[0].children:
- # need to "eval" the string because it's returned in its original form
+ # need to "eval" the string because it's returned in its
+ # original form
docstring = literals.evalString(node[0].value, self.encoding)
docstring = prepare_docstring(docstring)
self.add_docstring(prev[0], docstring)
@@ -159,7 +160,8 @@ class ModuleAnalyzer(object):
try:
source = mod.__loader__.get_source(modname)
except Exception, err:
- raise PycodeError('error getting source for %r' % modname, err)
+ raise PycodeError('error getting source for %r' % modname,
+ err)
obj = cls.for_string(source, modname)
cls.cache['module', modname] = obj
return obj
@@ -279,8 +281,9 @@ class ModuleAnalyzer(object):
namespace.pop()
result[fullname] = (dtype, startline, endline)
elif type == token.NEWLINE:
- # if this line contained a definition, expect an INDENT to start the
- # suite; if there is no such INDENT it's a one-line definition
+ # if this line contained a definition, expect an INDENT
+ # to start the suite; if there is no such INDENT
+ # it's a one-line definition
if defline:
defline = False
expect_indent = True
@@ -292,7 +295,8 @@ if __name__ == '__main__':
import time, pprint
x0 = time.time()
#ma = ModuleAnalyzer.for_file(__file__.rstrip('c'), 'sphinx.builders.html')
- ma = ModuleAnalyzer.for_file('sphinx/builders/html.py', 'sphinx.builders.html')
+ ma = ModuleAnalyzer.for_file('sphinx/builders/html.py',
+ 'sphinx.builders.html')
ma.tokenize()
x1 = time.time()
ma.parse()
diff --git a/sphinx/pycode/nodes.py b/sphinx/pycode/nodes.py
index 4d27fc66..efdf8f06 100644
--- a/sphinx/pycode/nodes.py
+++ b/sphinx/pycode/nodes.py
@@ -102,7 +102,8 @@ class Node(BaseNode):
ch.parent = self
def __repr__(self):
- return '%s(%s, %r)' % (self.__class__.__name__, self.type, self.children)
+ return '%s(%s, %r)' % (self.__class__.__name__,
+ self.type, self.children)
def __str__(self):
"""This reproduces the input source exactly."""
@@ -174,7 +175,8 @@ def nice_repr(node, number2name, prefix=False):
', '.join(map(_repr, node.children)))
def _prepr(node):
if isinstance(node, Leaf):
- return "%s(%r, %r)" % (number2name[node.type], node.prefix, node.value)
+ return "%s(%r, %r)" % (number2name[node.type],
+ node.prefix, node.value)
else:
return "%s(%s)" % (number2name[node.type],
', '.join(map(_prepr, node.children)))
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index 67ab6273..c0a219ef 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -15,7 +15,8 @@ from os import path
TERM_ENCODING = getattr(sys.stdin, 'encoding', None)
from sphinx.util import make_filename
-from sphinx.util.console import purple, bold, red, turquoise, nocolor, color_terminal
+from sphinx.util.console import purple, bold, red, turquoise, \
+ nocolor, color_terminal
from sphinx.util import texescape
@@ -29,9 +30,6 @@ QUICKSTART_CONF = '''\
#
# This file is execfile()d with the current directory set to its containing dir.
#
-# The contents of this file are pickled, so don't put values in the namespace
-# that aren't pickleable (module imports are okay, they're removed automatically).
-#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
@@ -45,8 +43,7 @@ import sys, os
# absolute, like shown here.
#sys.path.append(os.path.abspath('.'))
-# General configuration
-# ---------------------
+# -- General configuration -----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
@@ -112,8 +109,7 @@ exclude_trees = [%(exclude_trees)s]
pygments_style = 'sphinx'
-# Options for HTML output
-# -----------------------
+# -- Options for HTML output ---------------------------------------------------
# The style sheet to use for HTML and HTML Help pages. A file of that name
# must exist either in Sphinx' static/ path, or in one of the custom paths
@@ -180,8 +176,7 @@ html_static_path = ['%(dot)sstatic']
htmlhelp_basename = '%(project_fn)sdoc'
-# Options for LaTeX output
-# ------------------------
+# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
@@ -190,7 +185,7 @@ htmlhelp_basename = '%(project_fn)sdoc'
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, document class [howto/manual]).
+# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('%(master)s', '%(project_fn)s.tex', ur'%(project_doc_texescaped)s',
ur'%(author_texescaped)s', 'manual'),
@@ -217,11 +212,12 @@ latex_documents = [
INTERSPHINX_CONFIG = '''
# Example configuration for intersphinx: refer to the Python standard library.
-intersphinx_mapping = {'http://docs.python.org/dev': None}
+intersphinx_mapping = {'http://docs.python.org/': None}
'''
MASTER_FILE = '''\
-.. %(project)s documentation master file, created by sphinx-quickstart on %(now)s.
+.. %(project)s documentation master file, created by
+ sphinx-quickstart on %(now)s.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
@@ -267,7 +263,7 @@ help:
\t@echo " htmlhelp to make HTML files and a HTML help project"
\t@echo " qthelp to make HTML files and a qthelp project"
\t@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
-\t@echo " changes to make an overview over all changed/added/deprecated items"
+\t@echo " changes to make an overview of all changed/added/deprecated items"
\t@echo " linkcheck to check all external links for integrity"
clean:
@@ -466,8 +462,9 @@ def do_prompt(d, key, text, default=None, validator=nonempty):
if TERM_ENCODING:
x = x.decode(TERM_ENCODING)
else:
- print turquoise('* Note: non-ASCII characters entered and terminal '
- 'encoding unknown -- assuming UTF-8 or Latin-1.')
+ print turquoise('* Note: non-ASCII characters entered '
+ 'and terminal encoding unknown -- assuming '
+ 'UTF-8 or Latin-1.')
try:
x = x.decode('utf-8')
except UnicodeDecodeError:
@@ -502,8 +499,8 @@ Enter the root path for documentation.'''
'selected root path.')
print 'sphinx-quickstart will not overwrite existing Sphinx projects.'
print
- do_prompt(d, 'path', 'Please enter a new root path (or just Enter to exit)',
- '', is_path)
+ do_prompt(d, 'path', 'Please enter a new root path (or just Enter '
+ 'to exit)', '', is_path)
if not d['path']:
sys.exit(1)
@@ -516,8 +513,8 @@ Either, you use a directory "_build" within the root path, or you separate
print '''
Inside the root directory, two more directories will be created; "_templates"
-for custom HTML templates and "_static" for custom stylesheets and other
-static files. You can enter another prefix (such as ".") to replace the underscore.'''
+for custom HTML templates and "_static" for custom stylesheets and other static
+files. You can enter another prefix (such as ".") to replace the underscore.'''
do_prompt(d, 'dot', 'Name prefix for templates and static dir', '_', ok)
print '''
@@ -549,26 +546,29 @@ Please indicate if you want to use one of the following Sphinx extensions:'''
'from modules (y/N)', 'n', boolean)
do_prompt(d, 'ext_doctest', 'doctest: automatically test code snippets '
'in doctest blocks (y/N)', 'n', boolean)
- do_prompt(d, 'ext_intersphinx', 'intersphinx: link between Sphinx documentation '
- 'of different projects (y/N)', 'n', boolean)
+ do_prompt(d, 'ext_intersphinx', 'intersphinx: link between Sphinx '
+ 'documentation of different projects (y/N)', 'n', boolean)
print '''
A Makefile and a Windows command file can be generated for you so that you
only have to run e.g. `make html' instead of invoking sphinx-build
directly.'''
do_prompt(d, 'makefile', 'Create Makefile? (Y/n)', 'y', boolean)
- do_prompt(d, 'batchfile', 'Create Windows command file? (Y/n)', 'y', boolean)
+ do_prompt(d, 'batchfile', 'Create Windows command file? (Y/n)',
+ 'y', boolean)
d['project_fn'] = make_filename(d['project'])
d['now'] = time.asctime()
d['underline'] = len(d['project']) * '='
d['extensions'] = ', '.join(
- repr('sphinx.ext.' + name) for name in ('autodoc', 'doctest', 'intersphinx')
+ repr('sphinx.ext.' + name) for name in ('autodoc', 'doctest',
+ 'intersphinx')
if d['ext_' + name].upper() in ('Y', 'YES'))
d['copyright'] = time.strftime('%Y') + ', ' + d['author']
- d['author_texescaped'] = unicode(d['author']).translate(texescape.tex_escape_map)
+ d['author_texescaped'] = unicode(d['author']).\
+ translate(texescape.tex_escape_map)
d['project_doc'] = d['project'] + ' Documentation'
- d['project_doc_texescaped'] = \
- unicode(d['project'] + ' Documentation').translate(texescape.tex_escape_map)
+ d['project_doc_texescaped'] = unicode(d['project'] + ' Documentation').\
+ translate(texescape.tex_escape_map)
if not path.isdir(d['path']):
mkdir_p(d['path'])
diff --git a/sphinx/roles.py b/sphinx/roles.py
index d2e9558e..837b5cb8 100644
--- a/sphinx/roles.py
+++ b/sphinx/roles.py
@@ -36,7 +36,8 @@ for rolename, nodeclass in generic_docroles.iteritems():
roles.register_generic_role(rolename, nodeclass)
-def indexmarkup_role(typ, rawtext, etext, lineno, inliner, options={}, content=[]):
+def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
+ options={}, content=[]):
env = inliner.document.settings.env
if not typ:
typ = env.config.default_role
@@ -56,13 +57,14 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner, options={}, content=[
options, content)[0]
return [indexnode, targetnode] + xref_nodes, []
elif typ == 'pep':
- indexnode['entries'] = [('single',
- _('Python Enhancement Proposals!PEP %s') % text,
- targetid, 'PEP %s' % text)]
+ indexnode['entries'] = [
+ ('single', _('Python Enhancement Proposals!PEP %s') % text,
+ targetid, 'PEP %s' % text)]
try:
pepnum = int(text)
except ValueError:
- msg = inliner.reporter.error('invalid PEP number %s' % text, line=lineno)
+ msg = inliner.reporter.error('invalid PEP number %s' % text,
+ line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]
ref = inliner.document.settings.pep_base_url + 'pep-%04d' % pepnum
@@ -76,7 +78,8 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner, options={}, content=[
try:
rfcnum = int(text)
except ValueError:
- msg = inliner.reporter.error('invalid RFC number %s' % text, line=lineno)
+ msg = inliner.reporter.error('invalid RFC number %s' % text,
+ line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]
ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum
@@ -129,7 +132,8 @@ def xfileref_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
modname=env.currmodule, classname=env.currclass)
# we may need the line number for warnings
pnode.line = lineno
- # the link title may differ from the target, but by default they are the same
+ # the link title may differ from the target, but by default
+ # they are the same
title = target = text
titleistarget = True
# look if explicit title and target are given with `foo ` syntax
@@ -146,7 +150,8 @@ def xfileref_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
target = text[brace+1:]
title = text[:brace]
# special target for Python object cross-references
- if typ in ('data', 'exc', 'func', 'class', 'const', 'attr', 'meth', 'mod', 'obj'):
+ if typ in ('data', 'exc', 'func', 'class', 'const', 'attr',
+ 'meth', 'mod', 'obj'):
# fix-up parentheses in link title
if titleistarget:
title = title.lstrip('.') # only has a meaning for the target
@@ -171,7 +176,8 @@ def xfileref_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
elif typ == 'option':
program = env.currprogram
if titleistarget:
- if ' ' in title and not (title.startswith('/') or title.startswith('-')):
+ if ' ' in title and not (title.startswith('/') or
+ title.startswith('-')):
program, target = re.split(' (?=-|--|/)', title, 1)
program = ws_re.sub('-', program)
target = target.strip()
@@ -190,18 +196,21 @@ def xfileref_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
# remove all whitespace to avoid referencing problems
target = ws_re.sub('', target)
pnode['reftarget'] = target
- pnode += innernodetypes.get(typ, nodes.literal)(rawtext, title, classes=['xref'])
+ pnode += innernodetypes.get(typ, nodes.literal)(rawtext, title,
+ classes=['xref'])
return [pnode], []
def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
return [nodes.emphasis(
- rawtext, utils.unescape(text).replace('-->', u'\N{TRIANGULAR BULLET}'))], []
+ rawtext,
+ utils.unescape(text).replace('-->', u'\N{TRIANGULAR BULLET}'))], []
_litvar_re = re.compile('{([^}]+)}')
-def emph_literal_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
+def emph_literal_role(typ, rawtext, text, lineno, inliner,
+ options={}, content=[]):
text = utils.unescape(text)
pos = 0
retnode = nodes.literal(role=typ.lower())
diff --git a/sphinx/setup_command.py b/sphinx/setup_command.py
index c874bdd4..ac395f39 100644
--- a/sphinx/setup_command.py
+++ b/sphinx/setup_command.py
@@ -84,6 +84,7 @@ class BuildDoc(Command):
from docutils.utils import SystemMessage
if isinstance(err, SystemMessage):
sys.stderr, darkred('reST markup error:')
- print >>sys.stderr, err.args[0].encode('ascii', 'backslashreplace')
+ print >>sys.stderr, err.args[0].encode('ascii',
+ 'backslashreplace')
else:
raise
diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html
index f92e188c..b4b1e7c5 100644
--- a/sphinx/themes/basic/layout.html
+++ b/sphinx/themes/basic/layout.html
@@ -68,8 +68,8 @@
{% include customsidebar %}
{%- endif %}
{%- block sidebarsearch %}
- {%- if pagename != "search" %}
-
+ {%- if pagename != "search" %}
+
{{ _('Quick search') }}
-
+
{{ _('Enter search terms or a module, class or function name.') }}
diff --git a/sphinx/util/smartypants.py b/sphinx/util/smartypants.py
index 42f20f36..75888ea4 100644
--- a/sphinx/util/smartypants.py
+++ b/sphinx/util/smartypants.py
@@ -162,7 +162,8 @@ def educateQuotes(s):
"""
# Special case if the very first character is a quote
- # followed by punctuation at a non-word-break. Close the quotes by brute force:
+ # followed by punctuation at a non-word-break. Close the quotes
+ # by brute force:
s = single_quote_start_re.sub("’", s)
s = double_quote_start_re.sub("”", s)
@@ -200,7 +201,8 @@ def educateQuotesLatex(s, dquotes=("``", "''")):
"""
# Special case if the very first character is a quote
- # followed by punctuation at a non-word-break. Close the quotes by brute force:
+ # followed by punctuation at a non-word-break. Close the quotes
+ # by brute force:
s = single_quote_start_re.sub("\x04", s)
s = double_quote_start_re.sub("\x02", s)
@@ -300,4 +302,5 @@ __author__ = "Chad Miller "
__version__ = "1.5_1.5: Sat, 13 Aug 2005 15:50:24 -0400"
__url__ = "http://wiki.chad.org/SmartyPantsPy"
__description__ = \
- "Smart-quotes, smart-ellipses, and smart-dashes for weblog entries in pyblosxom"
+ "Smart-quotes, smart-ellipses, and smart-dashes for weblog entries" \
+ " in pyblosxom"
diff --git a/sphinx/util/stemmer.py b/sphinx/util/stemmer.py
index 7eeb77b2..10ce9065 100644
--- a/sphinx/util/stemmer.py
+++ b/sphinx/util/stemmer.py
@@ -111,14 +111,16 @@ class PorterStemmer(object):
return self.cons(j)
def cvc(self, i):
- """cvc(i) is TRUE <=> i-2,i-1,i has the form consonant - vowel - consonant
+ """cvc(i) is TRUE <=> i-2,i-1,i has the form
+ consonant - vowel - consonant
and also if the second c is not w,x or y. this is used when trying to
restore an e at the end of a short e.g.
cav(e), lov(e), hop(e), crim(e), but
snow, box, tray.
"""
- if i < (self.k0 + 2) or not self.cons(i) or self.cons(i-1) or not self.cons(i-2):
+ if i < (self.k0 + 2) or not self.cons(i) or self.cons(i-1) \
+ or not self.cons(i-2):
return 0
ch = self.b[i]
if ch == 'w' or ch == 'x' or ch == 'y':
@@ -138,7 +140,8 @@ class PorterStemmer(object):
return 1
def setto(self, s):
- """setto(s) sets (j+1),...k to the characters in the string s, readjusting k."""
+ """setto(s) sets (j+1),...k to the characters in the string s,
+ readjusting k."""
length = len(s)
self.b = self.b[:self.j+1] + s + self.b[self.j+length+1:]
self.k = self.j + length
@@ -193,7 +196,8 @@ class PorterStemmer(object):
self.setto("e")
def step1c(self):
- """step1c() turns terminal y to i when there is another vowel in the stem."""
+ """step1c() turns terminal y to i when there is another vowel in
+ the stem."""
if (self.ends("y") and self.vowelinstem()):
self.b = self.b[:self.k] + 'i' + self.b[self.k+1:]
@@ -236,7 +240,8 @@ class PorterStemmer(object):
# To match the published algorithm, delete this phrase
def step3(self):
- """step3() dels with -ic-, -full, -ness etc. similar strategy to step2."""
+ """step3() dels with -ic-, -full, -ness etc. similar strategy
+ to step2."""
if self.b[self.k] == 'e':
if self.ends("icate"): self.r("ic")
elif self.ends("ative"): self.r("")
diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py
index 79197606..17a22b7a 100644
--- a/sphinx/writers/html.py
+++ b/sphinx/writers/html.py
@@ -68,13 +68,15 @@ class HTMLTranslator(BaseTranslator):
# the id is set automatically
self.body.append(self.starttag(node, 'dt'))
# anchor for per-desc interactive data
- if node.parent['desctype'] != 'describe' and node['ids'] and node['first']:
+ if node.parent['desctype'] != 'describe' \
+ and node['ids'] and node['first']:
self.body.append('' % node['ids'][0])
if node.parent['desctype'] in ('class', 'exception'):
self.body.append('%s ' % node.parent['desctype'])
def depart_desc_signature(self, node):
if node['ids'] and self.add_permalinks and self.builder.add_permalinks:
- self.body.append(u'\u00B6' %
_('Permalink to this definition'))
self.body.append('\n')
@@ -192,14 +194,17 @@ class HTMLTranslator(BaseTranslator):
# most probably a parsed-literal block -- don't highlight
return BaseTranslator.visit_literal_block(self, node)
lang = self.highlightlang
- linenos = node.rawsource.count('\n') >= self.highlightlinenothreshold - 1
+ linenos = node.rawsource.count('\n') >= \
+ self.highlightlinenothreshold - 1
if node.has_key('language'):
# code-block directives
lang = node['language']
if node.has_key('linenos'):
linenos = node['linenos']
- highlighted = self.highlighter.highlight_block(node.rawsource, lang, linenos)
- starttag = self.starttag(node, 'div', suffix='', CLASS='highlight-%s' % lang)
+ highlighted = self.highlighter.highlight_block(node.rawsource,
+ lang, linenos)
+ starttag = self.starttag(node, 'div', suffix='',
+ CLASS='highlight-%s' % lang)
self.body.append(starttag + highlighted + '
\n')
raise nodes.SkipNode
@@ -211,7 +216,8 @@ class HTMLTranslator(BaseTranslator):
if len(node.children) == 1 and \
node.children[0] in ('None', 'True', 'False'):
node['classes'].append('xref')
- self.body.append(self.starttag(node, 'tt', '', CLASS='docutils literal'))
+ self.body.append(self.starttag(node, 'tt', '',
+ CLASS='docutils literal'))
self.protect_literal_text += 1
def depart_literal(self, node):
self.protect_literal_text -= 1
@@ -243,7 +249,8 @@ class HTMLTranslator(BaseTranslator):
pass
def visit_centered(self, node):
- self.body.append(self.starttag(node, 'p', CLASS="centered") + '')
+ self.body.append(self.starttag(node, 'p', CLASS="centered")
+ + '')
def depart_centered(self, node):
self.body.append('')
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index 885cac44..a9fa7662 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -167,7 +167,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
'pointsize': builder.config.latex_font_size,
# if empty, the title is set to the first section title
'title': document.settings.title,
- 'date': ustrftime(builder.config.today_fmt or _('%B %d, %Y')),
+ 'date': ustrftime(builder.config.today_fmt
+ or _('%B %d, %Y')),
'release': builder.config.release,
'author': document.settings.author,
'releasename': _('Release'),
@@ -253,7 +254,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
for bi in self.bibitems:
# cite_key: underscores must not be escaped
cite_key = bi[0].replace(r"\_", "_")
- self.body.append('\\bibitem[%s]{%s}{%s}\n' % (bi[0], cite_key, bi[1]))
+ self.body.append('\\bibitem[%s]{%s}{%s}\n' %
+ (bi[0], cite_key, bi[1]))
self.body.append('\\end{thebibliography}\n')
self.bibitems = []
@@ -304,7 +306,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
# self.body.append(r'\hypertarget{%s}{}' % id)
# self.written_ids.add(id)
def depart_section(self, node):
- self.sectionlevel = max(self.sectionlevel - 1, self.top_sectionlevel - 1)
+ self.sectionlevel = max(self.sectionlevel - 1,
+ self.top_sectionlevel - 1)
def visit_problematic(self, node):
self.body.append(r'{\color{red}\bfseries{}')
@@ -335,7 +338,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_production(self, node):
if node['tokenname']:
- self.body.append('\\production{%s}{' % self.encode(node['tokenname']))
+ self.body.append('\\production{%s}{' %
+ self.encode(node['tokenname']))
else:
self.body.append('\\productioncont{')
def depart_production(self, node):
@@ -352,7 +356,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
# the environment already handles this
raise nodes.SkipNode
elif self.this_is_the_title:
- if len(node.children) != 1 and not isinstance(node.children[0], nodes.Text):
+ if len(node.children) != 1 and not isinstance(node.children[0],
+ nodes.Text):
self.builder.warn('document title is not a single Text node')
if not self.elements['title']:
# text needs to be escaped since it is inserted into
@@ -585,7 +590,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
else:
if self.table.has_verbatim:
colwidth = 0.95 / self.table.colcount
- colspec = ('p{%.3f\\textwidth}|' % colwidth) * self.table.colcount
+ colspec = ('p{%.3f\\textwidth}|' % colwidth) * \
+ self.table.colcount
self.body.append('{|' + colspec + '}\n')
else:
self.body.append('{|' + ('L|' * self.table.colcount) + '}\n')
@@ -648,7 +654,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
# this is a list in the source, but should be rendered as a
# comma-separated list here
self.body.append('\n\n')
- self.body.append(', '.join(n.astext() for n in node.children[0].children) + '.')
+ self.body.append(', '.join(n.astext()
+ for n in node.children[0].children) + '.')
self.body.append('\n\n')
raise nodes.SkipNode
@@ -743,9 +750,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_module(self, node):
modname = node['modname']
- self.body.append('\n\\declaremodule[%s]{}{%s}' % (modname.replace('_', ''),
- self.encode(modname)))
- self.body.append('\n\\modulesynopsis{%s}' % self.encode(node['synopsis']))
+ self.body.append('\n\\declaremodule[%s]{}{%s}' % (
+ modname.replace('_', ''), self.encode(modname)))
+ self.body.append('\n\\modulesynopsis{%s}' %
+ self.encode(node['synopsis']))
if node.has_key('platform'):
self.body.append('\\platform{%s}' % self.encode(node['platform']))
def depart_module(self, node):
@@ -922,15 +930,18 @@ class LaTeXTranslator(nodes.NodeVisitor):
entries = node['entries']
for type, string, tid, _ in entries:
if type == 'single':
- self.body.append(r'\index{%s}' % scre.sub('!', self.encode(string)))
+ self.body.append(r'\index{%s}' %
+ scre.sub('!', self.encode(string)))
elif type == 'pair':
- parts = tuple(self.encode(x.strip()) for x in string.split(';', 1))
+ parts = tuple(self.encode(x.strip())
+ for x in string.split(';', 1))
try:
self.body.append(r'\indexii{%s}{%s}' % parts)
except TypeError:
self.builder.warn('invalid pair index entry %r' % string)
elif type == 'triple':
- parts = tuple(self.encode(x.strip()) for x in string.split(';', 2))
+ parts = tuple(self.encode(x.strip())
+ for x in string.split(';', 2))
try:
self.body.append(r'\indexiii{%s}{%s}{%s}' % parts)
except TypeError:
@@ -957,7 +968,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.context.append('}')
elif uri.startswith('%'):
hashindex = uri.find('#')
- targetname = (hashindex == -1) and '--doc-' + uri[1:] or uri[hashindex+1:]
+ targetname = (hashindex == -1) and '--doc-' + uri[1:] \
+ or uri[hashindex+1:]
self.body.append('\\hyperlink{%s}{' % targetname)
self.context.append('}')
elif uri.startswith('@token'):
diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py
index 4718d41d..787e7020 100644
--- a/sphinx/writers/text.py
+++ b/sphinx/writers/text.py
@@ -419,7 +419,8 @@ class TextTranslator(nodes.NodeVisitor):
def visit_acks(self, node):
self.new_state(0)
- self.add_text(', '.join(n.astext() for n in node.children[0].children) + '.')
+ self.add_text(', '.join(n.astext() for n in node.children[0].children)
+ + '.')
self.end_state()
raise nodes.SkipNode
diff --git a/utils/check_sources.py b/utils/check_sources.py
index c244d9c0..def13ee9 100755
--- a/utils/check_sources.py
+++ b/utils/check_sources.py
@@ -56,7 +56,7 @@ def check_syntax(fn, lines):
def check_style_and_encoding(fn, lines):
encoding = 'ascii'
for lno, line in enumerate(lines):
- if len(line) > 90:
+ if len(line) > 81:
yield lno+1, "line too long"
if lno < 2:
co = coding_re.search(line)
--
cgit v1.2.1
From 0c000f5e64f324c3fa8d05f8bf0c884a7bc39cdf Mon Sep 17 00:00:00 2001
From: mq
Date: Sat, 10 Jan 2009 21:54:26 +0100
Subject: add modindex_common_prefix config value
---
CHANGES | 3 +++
doc/conf.py | 3 +++
doc/config.rst | 32 +++++++++++++++++++++-----------
sphinx/builders/html.py | 38 +++++++++++++++++++++++++++++++++-----
sphinx/config.py | 1 +
sphinx/quickstart.py | 3 +++
sphinx/templates/modindex.html | 4 ++--
7 files changed, 66 insertions(+), 18 deletions(-)
diff --git a/CHANGES b/CHANGES
index 47cfa672..6e7d5934 100644
--- a/CHANGES
+++ b/CHANGES
@@ -61,6 +61,9 @@ New features added
- The default value for ``htmlhelp_basename`` is now the project
title, cleaned up as a filename.
+ - The new ``modindex_common_prefix`` config value can be used to
+ ignore certain package names for module index sorting.
+
* Builders:
- New builder for Qt help collections, by Antonio Valentino.
diff --git a/doc/conf.py b/doc/conf.py
index 709d6f75..87afc659 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -68,6 +68,9 @@ show_authors = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'friendly'
+# A list of ignored prefixes names for module index sorting.
+modindex_common_prefix = ['sphinx.']
+
# Options for HTML output
# -----------------------
diff --git a/doc/config.rst b/doc/config.rst
index dd25cf22..ad14d1ed 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -76,7 +76,7 @@ General configuration
.. versionadded:: 0.5
Previously, Sphinx accepted only UTF-8 encoded sources.
-
+
.. confval:: master_doc
The document name of the "master" document, that is, the document that
@@ -164,9 +164,19 @@ General configuration
.. versionadded:: 0.5
+.. confval:: modindex_common_prefix
+
+ A list of prefixes that are ignored for sorting the module index (e.g.,
+ if this is set to ``['foo.']``, then ``foo.bar`` is shown under ``B``, not
+ ``F``). This can be handy if you document a project that consists of a single
+ package. Works only for the HTML builder currently. Default is ``[]``.
+
+ .. versionadded:: 0.6
+
+
Project information
-------------------
-
+
.. confval:: project
The documented project's name.
@@ -233,7 +243,7 @@ Project information
:ref:`code-examples` for more details.
.. versionadded:: 0.5
-
+
.. confval:: pygments_style
The style name to use for Pygments highlighting of source code. Default is
@@ -341,7 +351,7 @@ that use Sphinx' HTMLWriter class.
.. versionadded:: 0.6
Previously, this was always activated.
-
+
.. confval:: html_sidebars
Custom sidebar templates, must be a dictionary that maps document names to
@@ -439,7 +449,7 @@ that use Sphinx' HTMLWriter class.
support different web server setups).
.. versionadded:: 0.6
-
+
.. confval:: html_translator_class
A string with the fully-qualified name of a HTML Translator class, that is, a
@@ -527,7 +537,7 @@ These options influence LaTeX output.
avoid interpretation as escape sequences.
* Keys that you may want to override include:
-
+
``'papersize'``
Paper size option of the document class (``'a4paper'`` or
``'letterpaper'``), default ``'letterpaper'``.
@@ -551,9 +561,9 @@ These options influence LaTeX output.
Additional preamble content, default empty.
``'footer'```
Additional footer content (before the indices), default empty.
-
+
* Keys that don't need be overridden unless in special cases are:
-
+
``'inputenc'``
"inputenc" package inclusion, default
``'\\usepackage[utf8]{inputenc}'``.
@@ -570,9 +580,9 @@ These options influence LaTeX output.
"printindex" call, the last thing in the file, default
``'\\printindex'``. Override if you want to generate the index
differently or append some content after the index.
-
+
* Keys that are set by other options and therefore should not be overridden are:
-
+
``'docclass'``
``'classoptions'``
``'title'``
@@ -585,7 +595,7 @@ These options influence LaTeX output.
``'makemodindex'``
``'shorthandoff'``
``'printmodindex'``
-
+
.. confval:: latex_preamble
Additional LaTeX markup for the preamble.
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 91682aa2..20223ee0 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -292,11 +292,24 @@ class StandaloneHTMLBuilder(Builder):
for mn, (fn, sy, pl, dep) in modules:
pl = pl and pl.split(', ') or []
platforms.update(pl)
+
+ ignore = self.env.config['modindex_common_prefix']
+ ignore = sorted(ignore, key=len, reverse=True)
+ for i in ignore:
+ if mn.startswith(i):
+ mn = mn[len(i):]
+ stripped = i
+ break
+ else:
+ stripped = ''
+
if fl != mn[0].lower() and mn[0] != '_':
# heading
- modindexentries.append(['', False, 0, False,
- mn[0].upper(), '', [], False])
- letters.append(mn[0].upper())
+ letter = mn[0].upper()
+ if letter not in letters:
+ modindexentries.append(['', False, 0, False,
+ letter, '', [], False, ''])
+ letters.append(letter)
tn = mn.split('.')[0]
if tn != mn:
# submodule
@@ -307,11 +320,13 @@ class StandaloneHTMLBuilder(Builder):
elif not pmn.startswith(tn):
# submodule without parent in list, add dummy entry
cg += 1
- modindexentries.append([tn, True, cg, False, '', '', [], False])
+ modindexentries.append([tn, True, cg, False, '', '',
+ [], False, stripped])
else:
num_toplevels += 1
cg += 1
- modindexentries.append([mn, False, cg, (tn != mn), fn, sy, pl, dep])
+ modindexentries.append([mn, False, cg, (tn != mn), fn, sy, pl,
+ dep, stripped])
pmn = mn
fl = mn[0].lower()
platforms = sorted(platforms)
@@ -321,6 +336,19 @@ class StandaloneHTMLBuilder(Builder):
# number of submodules
collapse = len(modules) - num_toplevels < num_toplevels
+ # As some parts of the module names may have been stripped, those
+ # names have changed, thus it is necessary to sort the entries.
+ if ignore:
+ def sorthelper(entry):
+ name = entry[0]
+ if name == '':
+ # heading
+ name = entry[4]
+ return name.lower()
+
+ modindexentries.sort(key=sorthelper)
+ letters.sort()
+
modindexcontext = dict(
modindexentries = modindexentries,
platforms = platforms,
diff --git a/sphinx/config.py b/sphinx/config.py
index 428c0bf4..2f8ee1e5 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -51,6 +51,7 @@ class Config(object):
templates_path = ([], False),
template_bridge = (None, False),
keep_warnings = (False, True),
+ modindex_common_prefix = ([], False),
# HTML options
html_title = (lambda self: '%s v%s documentation' %
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index 67ab6273..343ea0cf 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -111,6 +111,9 @@ exclude_trees = [%(exclude_trees)s]
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
# Options for HTML output
# -----------------------
diff --git a/sphinx/templates/modindex.html b/sphinx/templates/modindex.html
index 6e33e55c..0392edc8 100644
--- a/sphinx/templates/modindex.html
+++ b/sphinx/templates/modindex.html
@@ -18,7 +18,7 @@
- {%- for modname, collapse, cgroup, indent, fname, synops, pform, dep in modindexentries %}
+ {%- for modname, collapse, cgroup, indent, fname, synops, pform, dep, stripped in modindexentries %}
{%- if not modname -%}
The code can be found in a Mercurial repository, at
http://bitbucket.org/birkenfeld/sphinx/.
-
+
{% endblock %}
diff --git a/doc/builders.rst b/doc/builders.rst
index dd07a96a..0055a28b 100644
--- a/doc/builders.rst
+++ b/doc/builders.rst
@@ -29,7 +29,7 @@ The builder's "name" must be given to the **-b** command-line option of
also generates HTML Help support files that allow the Microsoft HTML Help
Workshop to compile them into a CHM file.
- Its name is ``htmlhelp``.
+ Its name is ``htmlhelp``.
.. module:: sphinx.builders.latex
.. class:: LaTeXBuilder
@@ -85,7 +85,7 @@ The builder's "name" must be given to the **-b** command-line option of
.. _PHP serialization: http://pypi.python.org/pypi/phpserialize
.. attribute:: implementation
-
+
A module that implements `dump()`, `load()`, `dumps()` and `loads()`
functions that conform to the functions with the same names from the
pickle module. Known modules implementing this interface are
diff --git a/doc/concepts.rst b/doc/concepts.rst
index 379888ff..afb9503b 100644
--- a/doc/concepts.rst
+++ b/doc/concepts.rst
@@ -55,19 +55,19 @@ tables of contents. The ``toctree`` directive is the central element.
``strings`` and so forth, and it knows that they are children of the shown
document, the library index. From this information it generates "next
chapter", "previous chapter" and "parent chapter" links.
-
+
Document titles in the :dir:`toctree` will be automatically read from the
title of the referenced document. If that isn't what you want, you can give
the specify an explicit title and target using a similar syntax to reST
hyperlinks (and Sphinx's :ref:`cross-referencing syntax `). This
looks like::
-
+
.. toctree::
-
+
intro
All about strings
datatypes
-
+
The second line above will link to the ``strings`` document, but will use the
title "All about strings" instead of the title of the ``strings`` document.
@@ -97,7 +97,7 @@ tables of contents. The ``toctree`` directive is the central element.
This will still notify Sphinx of the document hierarchy, but not insert links
into the document at the location of the directive -- this makes sense if you
intend to insert these links yourself, in a different style.
-
+
In the end, all documents in the :term:`source directory` (or subdirectories)
must occur in some ``toctree`` directive; Sphinx will emit a warning if it
finds a file that is not included, because that means that this file will not
diff --git a/doc/conf.py b/doc/conf.py
index b3bd7149..e1c89eae 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -59,7 +59,7 @@ html_use_opensearch = 'http://sphinx.pocoo.org'
htmlhelp_basename = 'Sphinxdoc'
# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, document class [howto/manual]).
+# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [('contents', 'sphinx.tex', 'Sphinx Documentation',
'Georg Brandl', 'manual', 1)]
@@ -125,5 +125,6 @@ def setup(app):
app.add_description_unit('directive', 'dir', 'pair: %s; directive',
parse_directive)
app.add_description_unit('role', 'role', 'pair: %s; role', parse_role)
- app.add_description_unit('confval', 'confval', 'pair: %s; configuration value')
+ app.add_description_unit('confval', 'confval',
+ 'pair: %s; configuration value')
app.add_description_unit('event', 'event', 'pair: %s; event', parse_event)
diff --git a/doc/config.rst b/doc/config.rst
index dd25cf22..ff2ca909 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -76,7 +76,7 @@ General configuration
.. versionadded:: 0.5
Previously, Sphinx accepted only UTF-8 encoded sources.
-
+
.. confval:: master_doc
The document name of the "master" document, that is, the document that
@@ -166,7 +166,7 @@ General configuration
Project information
-------------------
-
+
.. confval:: project
The documented project's name.
@@ -233,7 +233,7 @@ Project information
:ref:`code-examples` for more details.
.. versionadded:: 0.5
-
+
.. confval:: pygments_style
The style name to use for Pygments highlighting of source code. Default is
@@ -341,7 +341,7 @@ that use Sphinx' HTMLWriter class.
.. versionadded:: 0.6
Previously, this was always activated.
-
+
.. confval:: html_sidebars
Custom sidebar templates, must be a dictionary that maps document names to
@@ -439,7 +439,7 @@ that use Sphinx' HTMLWriter class.
support different web server setups).
.. versionadded:: 0.6
-
+
.. confval:: html_translator_class
A string with the fully-qualified name of a HTML Translator class, that is, a
@@ -527,7 +527,7 @@ These options influence LaTeX output.
avoid interpretation as escape sequences.
* Keys that you may want to override include:
-
+
``'papersize'``
Paper size option of the document class (``'a4paper'`` or
``'letterpaper'``), default ``'letterpaper'``.
@@ -551,9 +551,9 @@ These options influence LaTeX output.
Additional preamble content, default empty.
``'footer'```
Additional footer content (before the indices), default empty.
-
+
* Keys that don't need be overridden unless in special cases are:
-
+
``'inputenc'``
"inputenc" package inclusion, default
``'\\usepackage[utf8]{inputenc}'``.
@@ -570,9 +570,9 @@ These options influence LaTeX output.
"printindex" call, the last thing in the file, default
``'\\printindex'``. Override if you want to generate the index
differently or append some content after the index.
-
+
* Keys that are set by other options and therefore should not be overridden are:
-
+
``'docclass'``
``'classoptions'``
``'title'``
@@ -585,7 +585,7 @@ These options influence LaTeX output.
``'makemodindex'``
``'shorthandoff'``
``'printmodindex'``
-
+
.. confval:: latex_preamble
Additional LaTeX markup for the preamble.
diff --git a/doc/contents.rst b/doc/contents.rst
index 6ddbcbcb..5a1187ee 100644
--- a/doc/contents.rst
+++ b/doc/contents.rst
@@ -14,7 +14,7 @@ Sphinx documentation contents
config
templating
extensions
-
+
glossary
changes
examples
diff --git a/doc/ext/appapi.rst b/doc/ext/appapi.rst
index 3dd5282b..84978da3 100644
--- a/doc/ext/appapi.rst
+++ b/doc/ext/appapi.rst
@@ -45,12 +45,12 @@ the following public API:
:exc:`docutils.nodes.SkipNode`. Example::
class math(docutils.nodes.Element)
-
+
def visit_math_html(self, node):
self.body.append(self.starttag(node, 'math'))
def depart_math_html(self, node):
self.body.append('')
-
+
app.add_node(math, html=(visit_math_html, depart_math_html))
Obviously, translators for which you don't specify visitor methods will choke
@@ -174,7 +174,7 @@ the following public API:
highlight code blocks with the given language *alias*.
.. versionadded:: 0.6
-
+
.. method:: Sphinx.connect(event, callback)
Register *callback* to be called when *event* is emitted. For details on
@@ -237,7 +237,7 @@ registered event handlers.
since the module declarations could have been removed from the file.
.. versionadded:: 0.5
-
+
.. event:: source-read (app, docname, source)
Emitted when a source file has been read. The *source* argument is a list
@@ -249,7 +249,7 @@ registered event handlers.
``:math:`...```.
.. versionadded:: 0.5
-
+
.. event:: doctree-read (app, doctree)
Emitted when a doctree has been parsed and read by the environment, and is
@@ -271,7 +271,7 @@ registered event handlers.
future reference and should be a child of the returned reference node.
.. versionadded:: 0.5
-
+
.. event:: doctree-resolved (app, doctree, docname)
Emitted when a doctree has been "resolved" by the environment, that is, all
@@ -287,7 +287,7 @@ registered event handlers.
completed, that is, the environment and all doctrees are now up-to-date.
.. versionadded:: 0.5
-
+
.. event:: page-context (app, pagename, templatename, context, doctree)
Emitted when the HTML builder has created a context dictionary to render a
@@ -318,7 +318,7 @@ registered event handlers.
cleanup actions depending on the exception status.
.. versionadded:: 0.5
-
+
.. _template-bridge:
diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst
index e0766938..11ea4ab4 100644
--- a/doc/ext/autodoc.rst
+++ b/doc/ext/autodoc.rst
@@ -61,7 +61,7 @@ directive.
Boil the noodle *time* minutes.
**Options and advanced usage**
-
+
* If you want to automatically document members, there's a ``members``
option::
diff --git a/doc/ext/doctest.rst b/doc/ext/doctest.rst
index 7117f6a9..19905dc7 100644
--- a/doc/ext/doctest.rst
+++ b/doc/ext/doctest.rst
@@ -131,7 +131,7 @@ completely equivalent. ::
Test-Output example:
- .. testcode::
+ .. testcode::
parrot.voom(3000)
@@ -156,7 +156,7 @@ There are also these config values for customizing the doctest extension:
e.g. import modules you will always need in your doctests.
.. versionadded:: 0.6
-
+
.. confval:: doctest_test_doctest_blocks
If this is a nonempty string (the default is ``'default'``), standard reST
@@ -187,7 +187,7 @@ There are also these config values for customizing the doctest extension:
>>> print 1
1
- Some more documentation text.
+ Some more documentation text.
This feature makes it easy for you to test doctests in docstrings included
with the :mod:`~sphinx.ext.autodoc` extension without marking them up with a
diff --git a/doc/ext/intersphinx.rst b/doc/ext/intersphinx.rst
index befae2c0..302ab6a3 100644
--- a/doc/ext/intersphinx.rst
+++ b/doc/ext/intersphinx.rst
@@ -49,7 +49,7 @@ linking:
This will download the corresponding :file:`objects.inv` file from the
Internet and generate links to the pages under the given URI. The downloaded
inventory is cached in the Sphinx environment, so it must be redownloaded
- whenever you do a full rebuild.
+ whenever you do a full rebuild.
A second example, showing the meaning of a non-``None`` value::
diff --git a/doc/ext/math.rst b/doc/ext/math.rst
index f2664dfe..57d844ff 100644
--- a/doc/ext/math.rst
+++ b/doc/ext/math.rst
@@ -90,7 +90,7 @@ further translation is necessary when building LaTeX output.
Euler's identity, equation :eq:`euler`, was elected one of the most
beautiful mathematical formulas.
-
+
:mod:`sphinx.ext.pngmath` -- Render math as PNG images
------------------------------------------------------
@@ -133,7 +133,7 @@ There are various config values you can set to influence how the images are buil
list.
.. versionadded:: 0.5.1
-
+
.. confval:: pngmath_latex_preamble
Additional LaTeX code to put into the preamble of the short LaTeX files that
@@ -145,7 +145,7 @@ There are various config values you can set to influence how the images are buil
Additional arguments to give to dvipng, as a list. The default value is
``['-gamma 1.5', '-D 110']`` which makes the image a bit darker and larger
then it is by default.
-
+
An arguments you might want to add here is e.g. ``'-bg Transparent'``,
which produces PNGs with a transparent background. This is not enabled by
default because some Internet Explorer versions don't like transparent PNGs.
diff --git a/doc/ext/todo.rst b/doc/ext/todo.rst
index 7bc65a02..4f5a379d 100644
--- a/doc/ext/todo.rst
+++ b/doc/ext/todo.rst
@@ -21,9 +21,9 @@ There are two additional directives when using this extension:
This directive is replaced by a list of all todo directives in the whole
documentation, if :confval:`todo_include_todos` is true.
-
+
There is also an additional config value:
-
+
.. confval:: todo_include_todos
If this is ``True``, :dir:`todo` and :dir:`todolist` produce output, else
diff --git a/doc/ext/tutorial.rst b/doc/ext/tutorial.rst
index ae9b7a77..85763e83 100644
--- a/doc/ext/tutorial.rst
+++ b/doc/ext/tutorial.rst
@@ -158,7 +158,7 @@ Let's start with the node classes::
def visit_todo_node(self, node):
self.visit_admonition(node)
-
+
def depart_todo_node(self, node):
self.depart_admonition(node)
@@ -190,14 +190,14 @@ The ``todo`` directive function looks like this::
def todo_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
env = state.document.settings.env
-
+
targetid = "todo-%s" % env.index_num
env.index_num += 1
targetnode = nodes.target('', '', ids=[targetid])
-
+
ad = make_admonition(todo, name, [_('Todo')], options, content, lineno,
content_offset, block_text, state, state_machine)
-
+
if not hasattr(env, 'todo_all_todos'):
env.todo_all_todos = []
env.todo_all_todos.append({
@@ -206,7 +206,7 @@ The ``todo`` directive function looks like this::
'todo': ad[0].deepcopy(),
'target': targetnode,
})
-
+
return [targetnode] + ad
Several important things are covered here. First, as you can see, you can refer
@@ -264,18 +264,18 @@ emitted at the end of phase 3 and allows custom resolving to be done::
if not app.config.todo_include_todos:
for node in doctree.traverse(todo_node):
node.parent.remove(node)
-
+
# Replace all todolist nodes with a list of the collected todos.
# Augment each todo with a backlink to the original location.
env = app.builder.env
-
+
for node in doctree.traverse(todolist):
if not app.config.todo_include_todos:
node.replace_self([])
continue
-
+
content = []
-
+
for todo_info in env.todo_all_todos:
para = nodes.paragraph()
filename = env.doc2path(todo_info['docname'], base=None)
@@ -283,7 +283,7 @@ emitted at the end of phase 3 and allows custom resolving to be done::
_('(The original entry is located in %s, line %d and can be found ') %
(filename, todo_info['lineno']))
para += nodes.Text(description, description)
-
+
# Create a reference
newnode = nodes.reference('', '')
innernode = nodes.emphasis(_('here'), _('here'))
@@ -294,11 +294,11 @@ emitted at the end of phase 3 and allows custom resolving to be done::
newnode.append(innernode)
para += newnode
para += nodes.Text('.)', '.)')
-
+
# Insert into the todolist
content.append(todo_info['todo'])
content.append(para)
-
+
node.replace_self(content)
It is a bit more involved. If our new "todo_include_todos" config value is
diff --git a/doc/intro.rst b/doc/intro.rst
index 7ce9d1fa..f9e23e18 100644
--- a/doc/intro.rst
+++ b/doc/intro.rst
@@ -109,7 +109,7 @@ The :program:`sphinx-build` script has several more options:
Don't look for a configuration file; only take options via the ``-D`` option.
.. versionadded:: 0.5
-
+
**-D** *setting=value*
Override a configuration value set in the :file:`conf.py` file. The value
must be a string or dictionary value. For the latter, supply the setting
diff --git a/doc/markup/code.rst b/doc/markup/code.rst
index 0bf8343b..4872d6a4 100644
--- a/doc/markup/code.rst
+++ b/doc/markup/code.rst
@@ -101,7 +101,7 @@ Includes
The directive also supports the ``linenos`` flag option to switch on line
numbers, and a ``language`` option to select a language different from the
current file's standard language. Example with options::
-
+
.. literalinclude:: example.rb
:language: ruby
:linenos:
diff --git a/doc/markup/desc.rst b/doc/markup/desc.rst
index 67605c77..ec8ede37 100644
--- a/doc/markup/desc.rst
+++ b/doc/markup/desc.rst
@@ -260,7 +260,7 @@ explained by an example::
.. function:: format_exception(etype, value, tb[, limit=None])
Format the exception with a traceback.
-
+
:param etype: exception type
:param value: exception value
:param tb: traceback object
diff --git a/doc/markup/para.rst b/doc/markup/para.rst
index b071e46c..2ea2fd2d 100644
--- a/doc/markup/para.rst
+++ b/doc/markup/para.rst
@@ -1,4 +1,4 @@
-.. highlight:: rest
+x.. highlight:: rest
Paragraph-level markup
----------------------
@@ -85,9 +85,9 @@ units as well as normal text:
This directive creates a paragraph heading that is not used to create a
table of contents node.
-
+
.. note::
-
+
If the *title* of the rubric is "Footnotes", this rubric is ignored by
the LaTeX writer, since it is assumed to only contain footnote
definitions and therefore would create an empty heading.
@@ -239,7 +239,7 @@ the definition of the symbol. There is this directive:
Note that no further reST parsing is done in the production, so that you
don't have to escape ``*`` or ``|`` characters.
-.. XXX describe optional first parameter
+.. XXX describe optional first parameter
The following is an example taken from the Python Reference Manual::
diff --git a/doc/rest.rst b/doc/rest.rst
index d7ef5c7b..2b6ba8c5 100644
--- a/doc/rest.rst
+++ b/doc/rest.rst
@@ -282,7 +282,7 @@ markup blocks, like this::
See the `reST reference for substitutions
`_
for details.
-
+
If you want to use some substitutions for all documents, put them into a
separate file and include it into all documents you want to use them in, using
the :dir:`include` directive. Be sure to give the include file a file name
@@ -301,7 +301,7 @@ footnotes above) is regarded as a comment. For example::
.. This is a comment.
You can indent text after a comment start to form multiline comments::
-
+
..
This whole indented block
is a comment.
@@ -329,5 +329,5 @@ There are some problems one commonly runs into while authoring reST documents:
* **No nested inline markup:** Something like ``*see :func:`foo`*`` is not
possible.
-
+
.. XXX more?
diff --git a/setup.py b/setup.py
index 37cdd1e8..095ef114 100644
--- a/setup.py
+++ b/setup.py
@@ -98,7 +98,8 @@ else:
else:
for locale in os.listdir(self.directory):
po_file = os.path.join(self.directory, locale,
- 'LC_MESSAGES', self.domain + '.po')
+ 'LC_MESSAGES',
+ self.domain + '.po')
if os.path.exists(po_file):
po_files.append((locale, po_file))
js_files.append(os.path.join(self.directory, locale,
diff --git a/sphinx/theming.py b/sphinx/theming.py
index ff14dacc..d0727c98 100644
--- a/sphinx/theming.py
+++ b/sphinx/theming.py
@@ -5,7 +5,7 @@
Theming support for HTML builders.
- :copyright: 2007-2009 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/root/conf.py b/tests/root/conf.py
index 12951d03..a2054f45 100644
--- a/tests/root/conf.py
+++ b/tests/root/conf.py
@@ -5,9 +5,6 @@
#
# This file is execfile()d with the current directory set to its containing dir.
#
-# The contents of this file are pickled, so don't put values in the namespace
-# that aren't pickleable (module imports are okay, they're removed automatically).
-#
# All configuration values have a default value; values that are commented out
# serve to show the default value.
@@ -57,8 +54,8 @@ today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
#unused_docs = []
-# List of directories, relative to source directories, that shouldn't be searched
-# for source files.
+# List of directories, relative to source directories, that shouldn't be
+# searched for source files.
exclude_trees = ['_build']
keep_warnings = True
@@ -150,7 +147,7 @@ htmlhelp_basename = 'SphinxTestsdoc'
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, document class [howto/manual]).
+# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('contents', 'SphinxTests.tex', 'Sphinx Tests Documentation',
'Georg Brandl', 'manual'),
diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py
index 67220180..d59ff304 100644
--- a/tests/test_autodoc.py
+++ b/tests/test_autodoc.py
@@ -131,7 +131,8 @@ def test_format_signature():
# test for functions
def f(a, b, c=1, **d):
pass
- assert gen.format_signature('function', 'f', f, None, None) == '(a, b, c=1, **d)'
+ assert gen.format_signature('function', 'f', f, None, None) == \
+ '(a, b, c=1, **d)'
assert gen.format_signature('function', 'f', f, 'a, b, c, d', None) == \
'(a, b, c, d)'
assert gen.format_signature('function', 'f', f, None, 'None') == \
@@ -151,7 +152,8 @@ def test_format_signature():
class G(F, object):
pass
for C in (F, G):
- assert gen.format_signature('class', 'C', C, None, None) == '(a, b=None)'
+ assert gen.format_signature('class', 'C', C, None, None) == \
+ '(a, b=None)'
assert gen.format_signature('class', 'C', D, 'a, b', 'X') == '(a, b) -> X'
# test for methods
@@ -160,12 +162,16 @@ def test_format_signature():
pass
def foo2(b, *c):
pass
- assert gen.format_signature('method', 'H.foo', H.foo1, None, None) == '(b, *c)'
- assert gen.format_signature('method', 'H.foo', H.foo1, 'a', None) == '(a)'
- assert gen.format_signature('method', 'H.foo', H.foo2, None, None) == '(b, *c)'
+ assert gen.format_signature('method', 'H.foo', H.foo1, None, None) == \
+ '(b, *c)'
+ assert gen.format_signature('method', 'H.foo', H.foo1, 'a', None) == \
+ '(a)'
+ assert gen.format_signature('method', 'H.foo', H.foo2, None, None) == \
+ '(b, *c)'
# test exception handling
- raises(RuntimeError, gen.format_signature, 'function', 'int', int, None, None)
+ raises(RuntimeError, gen.format_signature,
+ 'function', 'int', int, None, None)
# test processing by event handler
assert gen.format_signature('method', 'bar', H.foo1, None, None) == '42'
@@ -248,7 +254,8 @@ def test_docstring_processing():
# docstring processing by event handler
assert process('class', 'bar', E) == ['Init docstring', '', '42', '']
- lid = app.connect('autodoc-process-docstring', cut_lines(1, 1, ['function']))
+ lid = app.connect('autodoc-process-docstring',
+ cut_lines(1, 1, ['function']))
def f():
"""
first line
@@ -289,7 +296,8 @@ def test_generate():
del processed_docstrings[:]
del processed_signatures[:]
assert_works(*args)
- assert set(processed_docstrings) | set(processed_signatures) == set(items)
+ assert set(processed_docstrings) | set(processed_signatures) == \
+ set(items)
def assert_result_contains(item, *args):
gen.generate(*args)
@@ -313,8 +321,10 @@ def test_generate():
assert_result_contains(' Function.', 'method', 'Class.meth', [], None)
add_content = ViewList()
add_content.append('Content.', '', 0)
- assert_result_contains(' Function.', 'method', 'Class.meth', [], add_content)
- assert_result_contains(' Content.', 'method', 'Class.meth', [], add_content)
+ assert_result_contains(' Function.', 'method',
+ 'Class.meth', [], add_content)
+ assert_result_contains(' Content.', 'method',
+ 'Class.meth', [], add_content)
# test check_module
gen.generate('function', 'raises', None, None, check_module=True)
@@ -342,20 +352,23 @@ def test_generate():
assert_processes(should, 'class', 'Class', ['__all__'], None)
# test module flags
- assert_result_contains('.. module:: test_autodoc', 'module',
- 'test_autodoc', [], None)
+ assert_result_contains('.. module:: test_autodoc',
+ 'module', 'test_autodoc', [], None)
options.synopsis = 'Synopsis'
- assert_result_contains(' :synopsis: Synopsis', 'module', 'test_autodoc', [], None)
+ assert_result_contains(' :synopsis: Synopsis',
+ 'module', 'test_autodoc', [], None)
options.deprecated = True
- assert_result_contains(' :deprecated:', 'module', 'test_autodoc', [], None)
+ assert_result_contains(' :deprecated:',
+ 'module', 'test_autodoc', [], None)
options.platform = 'Platform'
- assert_result_contains(' :platform: Platform', 'module', 'test_autodoc', [], None)
+ assert_result_contains(' :platform: Platform',
+ 'module', 'test_autodoc', [], None)
# test if __all__ is respected for modules
- assert_result_contains('.. class:: Class', 'module', 'test_autodoc',
- ['__all__'], None)
+ assert_result_contains('.. class:: Class',
+ 'module', 'test_autodoc', ['__all__'], None)
try:
- assert_result_contains('.. exception:: CustomEx', 'module', 'test_autodoc',
- ['__all__'], None)
+ assert_result_contains('.. exception:: CustomEx',
+ 'module', 'test_autodoc', ['__all__'], None)
except AssertionError:
pass
else:
@@ -378,8 +391,10 @@ def test_generate():
# test generation for C modules (which have no source file)
gen.env.currmodule = 'time'
- assert_processes([('function', 'time.asctime')], 'function', 'asctime', [], None)
- assert_processes([('function', 'time.asctime')], 'function', 'asctime', [], None)
+ assert_processes([('function', 'time.asctime')],
+ 'function', 'asctime', [], None)
+ assert_processes([('function', 'time.asctime')],
+ 'function', 'asctime', [], None)
# --- generate fodder ------------
diff --git a/tests/test_build.py b/tests/test_build.py
index 9999abd0..cb206979 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -30,9 +30,10 @@ latex_warnfile = StringIO()
ENV_WARNINGS = """\
WARNING: %(root)s/images.txt:9: Image file not readable: foo.png
-WARNING: %(root)s/images.txt:23: Nonlocal image URI found: http://www.python.org/logo.png
-WARNING: %(root)s/includes.txt:: (WARNING/2) Encoding 'utf-8' used for reading included \
-file u'wrongenc.inc' seems to be wrong, try giving an :encoding: option
+WARNING: %(root)s/images.txt:23: Nonlocal image URI found: \
+http://www.python.org/logo.png
+WARNING: %(root)s/includes.txt:: (WARNING/2) Encoding 'utf-8' used for reading \
+included file u'wrongenc.inc' seems to be wrong, try giving an :encoding: option
WARNING: %(root)s/includes.txt:56: Download file not readable: nonexisting.png
"""
@@ -172,12 +173,15 @@ def test_latex(app):
return True
if kpsetest('article.sty') is None:
- print >>sys.stderr, 'info: not running latex, it doesn\'t seem to be installed'
+ print >>sys.stderr, \
+ 'info: not running latex, it doesn\'t seem to be installed'
return
- for filename in ['fancyhdr.sty', 'fancybox.sty', 'titlesec.sty', 'amsmath.sty',
- 'framed.sty', 'color.sty', 'fancyvrb.sty', 'threeparttable.sty']:
+ for filename in ['fancyhdr.sty', 'fancybox.sty', 'titlesec.sty',
+ 'amsmath.sty', 'framed.sty', 'color.sty', 'fancyvrb.sty',
+ 'threeparttable.sty']:
if not kpsetest(filename):
- print >>sys.stderr, 'info: not running latex, the %s package doesn\'t ' \
+ print >>sys.stderr, \
+ 'info: not running latex, the %s package doesn\'t ' \
'seem to be installed' % filename
return
@@ -186,8 +190,8 @@ def test_latex(app):
os.chdir(app.outdir)
try:
try:
- p = Popen(['pdflatex', '--interaction=nonstopmode', 'SphinxTests.tex'],
- stdout=PIPE, stderr=PIPE)
+ p = Popen(['pdflatex', '--interaction=nonstopmode',
+ 'SphinxTests.tex'], stdout=PIPE, stderr=PIPE)
except OSError, err:
pass # most likely pdflatex was not found
else:
diff --git a/tests/test_env.py b/tests/test_env.py
index 685311e6..32d87ebf 100644
--- a/tests/test_env.py
+++ b/tests/test_env.py
@@ -54,7 +54,8 @@ def test_images():
app._warning.reset()
htmlbuilder = StandaloneHTMLBuilder(app, env)
htmlbuilder.post_process_images(tree)
- assert "no matching candidate for image URI u'foo.*'" in app._warning.content[-1]
+ assert "no matching candidate for image URI u'foo.*'" in \
+ app._warning.content[-1]
assert set(htmlbuilder.images.keys()) == set(['subdir/img.png', 'img.png',
'subdir/simg.png'])
assert set(htmlbuilder.images.values()) == set(['img.png', 'img1.png',
@@ -63,8 +64,10 @@ def test_images():
app._warning.reset()
latexbuilder = LaTeXBuilder(app, env)
latexbuilder.post_process_images(tree)
- assert "no matching candidate for image URI u'foo.*'" in app._warning.content[-1]
- assert set(latexbuilder.images.keys()) == set(['subdir/img.png', 'subdir/simg.png',
+ assert "no matching candidate for image URI u'foo.*'" in \
+ app._warning.content[-1]
+ assert set(latexbuilder.images.keys()) == set(['subdir/img.png',
+ 'subdir/simg.png',
'img.png', 'img.pdf'])
assert set(latexbuilder.images.values()) == set(['img.pdf', 'img.png',
'img1.png', 'simg.png'])
diff --git a/tests/test_markup.py b/tests/test_markup.py
index 99c85911..cd6de561 100644
--- a/tests/test_markup.py
+++ b/tests/test_markup.py
@@ -25,7 +25,8 @@ def setup_module():
global app, settings, parser
texescape.init() # otherwise done by the latex builder
app = TestApp(cleanenv=True)
- optparser = frontend.OptionParser(components=(rst.Parser, HTMLWriter, LaTeXWriter))
+ optparser = frontend.OptionParser(
+ components=(rst.Parser, HTMLWriter, LaTeXWriter))
settings = optparser.get_default_values()
settings.env = app.builder.env
parser = rst.Parser()
@@ -85,7 +86,8 @@ def test_inline():
# interpolation of braces in samp and file roles (HTML only)
verify(':samp:`a{b}c`',
'
', '', '', ' not really)" or "")
+ print "reindented", file, \
+ (dryrun and "(dry run => not really)" or "")
if not dryrun:
if not no_backup:
bak = file + ".bak"
--
cgit v1.2.1
From b168c19de190a4b5bc1702ee66738c944bd8147d Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sat, 10 Jan 2009 22:32:19 +0100
Subject: Fix bugs in the template loader and the changes builder.
---
sphinx/builders/changes.py | 6 +++++-
sphinx/jinja2glue.py | 3 ++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/sphinx/builders/changes.py b/sphinx/builders/changes.py
index 6f057b49..d4520fa5 100644
--- a/sphinx/builders/changes.py
+++ b/sphinx/builders/changes.py
@@ -129,8 +129,12 @@ class ChangesBuilder(Builder):
f.write(self.templates.render('changes/rstsource.html', ctx))
finally:
f.close()
- shutil.copyfile(path.join(package_dir, 'static', 'default.css'),
+ shutil.copyfile(path.join(package_dir, 'themes', 'default',
+ 'static', 'default.css'),
path.join(self.outdir, 'default.css'))
+ shutil.copyfile(path.join(package_dir, 'themes', 'basic',
+ 'static', 'basic.css'),
+ path.join(self.outdir, 'basic.css'))
def hl(self, text, version):
text = escape(text)
diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py
index fc3efc01..c5160376 100644
--- a/sphinx/jinja2glue.py
+++ b/sphinx/jinja2glue.py
@@ -35,7 +35,8 @@ class BuiltinTemplateLoader(TemplateBridge, jinja2.BaseLoader):
# prepend explicit template paths
if builder.config.templates_path:
- chain[0:0] = builder.config.templates_path
+ chain[0:0] = [path.join(builder.confdir, tp)
+ for tp in builder.config.templates_path]
# make the paths into loaders
self.loaders = map(jinja2.FileSystemLoader, chain)
--
cgit v1.2.1
From cd95e96150ea161c144180a55dd4b2195454b102 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sat, 10 Jan 2009 22:32:54 +0100
Subject: Clean up tests conf.py, no need to include comments.
---
tests/root/conf.py | 142 ++---------------------------------------------------
1 file changed, 4 insertions(+), 138 deletions(-)
diff --git a/tests/root/conf.py b/tests/root/conf.py
index a2054f45..e9a6f15d 100644
--- a/tests/root/conf.py
+++ b/tests/root/conf.py
@@ -1,175 +1,41 @@
# -*- coding: utf-8 -*-
-#
-# Sphinx Tests documentation build configuration file, created by
-# sphinx-quickstart on Wed Jun 4 23:49:58 2008.
-#
-# This file is execfile()d with the current directory set to its containing dir.
-#
-# All configuration values have a default value; values that are commented out
-# serve to show the default value.
import sys, os
-# If your extensions are in another directory, add it here. If the directory
-# is relative to the documentation root, use os.path.abspath to make it
-# absolute, like shown here.
sys.path.append(os.path.abspath('.'))
-# General configuration
-# ---------------------
-
-# Add any Sphinx extension module names here, as strings. They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['ext', 'sphinx.ext.autodoc', 'sphinx.ext.jsmath',
'sphinx.ext.coverage', 'sphinx.ext.todo']
+
jsmath_path = 'dummy.js'
-# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
-# The suffix of source filenames.
-source_suffix = '.txt'
-
-# The master toctree document.
master_doc = 'contents'
+source_suffix = '.txt'
-# General substitutions.
project = 'Sphinx '
copyright = '2008, Georg Brandl & Team'
-
-# The default replacements for |version| and |release|, also used in various
-# other places throughout the built documents.
-#
-# The short X.Y version.
-version = '0.4'
-# The full version, including alpha/beta/rc tags.
-release = '0.4alpha1'
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
+version = '0.6'
+release = '0.6alpha1'
today_fmt = '%B %d, %Y'
-
-# List of documents that shouldn't be included in the build.
#unused_docs = []
-
-# List of directories, relative to source directories, that shouldn't be
-# searched for source files.
exclude_trees = ['_build']
-
keep_warnings = True
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
-
-# Options for HTML output
-# -----------------------
-
-# The style sheet to use for HTML and HTML Help pages. A file of that name
-# must exist either in Sphinx' static/ path, or in one of the custom paths
-# given in html_static_path.
html_style = 'default.css'
-
-# The name for this set of Sphinx documents. If None, it defaults to
-# " v documentation".
-#html_title = None
-
-# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (within the static path) to place at the top of
-# the sidebar.
-#html_logo = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_use_modindex = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the reST sources are included in the HTML build as _sources/.
-#html_copy_source = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a tag referring to it. The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = ''
-
html_context = {'hckey': 'hcval'}
-# Output file base name for HTML help builder.
htmlhelp_basename = 'SphinxTestsdoc'
-
-# Options for LaTeX output
-# ------------------------
-
-# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
-
-# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('contents', 'SphinxTests.tex', 'Sphinx Tests Documentation',
'Georg Brandl', 'manual'),
]
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_use_modindex = True
-
value_from_conf_py = 84
coverage_c_path = ['special/*.h']
--
cgit v1.2.1
From 66957f00d356e9b1a4c4b7e905330a9d039bbe62 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 11 Jan 2009 14:28:34 +0100
Subject: Fix the serializing builder.
---
sphinx/builders/html.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 52854559..13cb8554 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -64,6 +64,7 @@ class StandaloneHTMLBuilder(Builder):
def init(self):
self.init_templates()
+ self.init_highlighter()
self.init_translator_class()
if self.config.html_file_suffix:
self.out_suffix = self.config.html_file_suffix
@@ -79,6 +80,7 @@ class StandaloneHTMLBuilder(Builder):
if path.isfile(jsfile):
self.script_files.append('_static/translations.js')
+ def init_highlighter(self):
# determine Pygments style and create the highlighter
if self.config.pygments_style is not None:
style = self.config.pygments_style
@@ -600,6 +602,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder):
def init(self):
self.init_translator_class()
+ self.init_highlighter()
self.templates = None # no template bridge necessary
def get_target_uri(self, docname, typ=None):
--
cgit v1.2.1
From 10375e47856fccee00bebc38b692541ea7315feb Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 14 Jan 2009 00:06:56 +0100
Subject: fix
---
sphinx/util/__init__.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py
index 08e619ee..edfb31d8 100644
--- a/sphinx/util/__init__.py
+++ b/sphinx/util/__init__.py
@@ -366,6 +366,7 @@ def force_decode(string, encoding):
def movefile(source, dest):
# move a file, removing the destination if it exists
if os.path.exists(dest):
+ try:
os.unlink(dest)
except OSError:
pass
--
cgit v1.2.1
From 5710e17396814dee22169f8dbc340c30d90fe6ea Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 14 Jan 2009 00:30:34 +0100
Subject: Give
{% endblock %}
{% block header %}
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 57105d16..a2414164 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -179,6 +179,9 @@ class StandaloneHTMLBuilder(Builder):
logo = logo,
favicon = favicon,
)
+ self.globalcontext.update(
+ ('theme_' + key, val) for (key, val) in
+ self.theme.get_options(self.config.html_theme_options).iteritems())
self.globalcontext.update(self.config.html_context)
def _get_local_toctree(self, docname):
diff --git a/sphinx/config.py b/sphinx/config.py
index 2cba96a3..79f3ce8a 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -58,6 +58,7 @@ class Config(object):
# HTML options
html_theme = ('default', False),
html_theme_path = ([], False),
+ html_theme_options = ({}, False),
html_title = (lambda self: '%s v%s documentation' %
(self.project, self.release),
False),
diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py
index c7c163aa..9fc9ba3a 100644
--- a/sphinx/jinja2glue.py
+++ b/sphinx/jinja2glue.py
@@ -18,6 +18,12 @@ from sphinx.util import mtimes_of_files
from sphinx.application import TemplateBridge
+def _tobool(val):
+ if isinstance(val, basestring):
+ return val.lower() in ('true', '1', 'yes', 'on')
+ return bool(val)
+
+
class BuiltinTemplateLoader(TemplateBridge, jinja2.BaseLoader):
"""
Interfaces the rendering environment of jinja2 for use in Sphinx.
@@ -38,6 +44,8 @@ class BuiltinTemplateLoader(TemplateBridge, jinja2.BaseLoader):
chain[0:0] = [path.join(builder.confdir, tp)
for tp in builder.config.templates_path]
+ self.pathchain = chain
+
# make the paths into loaders
self.loaders = map(jinja2.FileSystemLoader, chain)
@@ -45,6 +53,7 @@ class BuiltinTemplateLoader(TemplateBridge, jinja2.BaseLoader):
extensions = use_i18n and ['jinja2.ext.i18n'] or []
self.environment = jinja2.Environment(loader=self,
extensions=extensions)
+ self.environment.filters['tobool'] = _tobool
if use_i18n:
self.environment.install_gettext_translations(builder.translator)
@@ -52,7 +61,7 @@ class BuiltinTemplateLoader(TemplateBridge, jinja2.BaseLoader):
return self.environment.get_template(template).render(context)
def newest_template_mtime(self):
- return max(mtimes_of_files(self.theme.get_dirchain(), '.html'))
+ return max(mtimes_of_files(self.pathchain, '.html'))
# Loader interface
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index c7bf92f6..d2a1c62d 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -114,10 +114,17 @@ pygments_style = 'sphinx'
# -- Options for HTML output ---------------------------------------------------
-# The style sheet to use for HTML and HTML Help pages. A file of that name
-# must exist either in Sphinx' static/ path, or in one of the custom paths
-# given in html_static_path.
-html_style = 'default.css'
+# The theme to use for HTML and HTML Help pages. Major themes that come with
+# Sphinx are currently 'default' and 'sphinxdoc'.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html
index b4b1e7c5..dda18343 100644
--- a/sphinx/themes/basic/layout.html
+++ b/sphinx/themes/basic/layout.html
@@ -27,7 +27,7 @@
{%- endmacro %}
{%- macro sidebar() %}
- {%- if not embedded %}
+ {%- if not embedded %}{% if not theme_nosidebar|tobool %}
- {%- if not embedded %}
+ {%- if not embedded %}{% if not theme_nosidebar|tobool %}
- {%- endif %}
+ {%- endif %}{% endif %}
{% block body %} {% endblock %}
- {%- if not embedded %}
+ {%- if not embedded %}{% if not theme_nosidebar|tobool %}
- {%- endif %}
+ {%- endif %}{% endif %}
{%- endblock %}
diff --git a/sphinx/themes/basic/theme.conf b/sphinx/themes/basic/theme.conf
index 3157eac5..d1fe6d1f 100644
--- a/sphinx/themes/basic/theme.conf
+++ b/sphinx/themes/basic/theme.conf
@@ -2,3 +2,6 @@
inherit = none
stylesheet = basic.css
pygments_style = none
+
+[options]
+nosidebar = false
diff --git a/sphinx/themes/default/layout.html b/sphinx/themes/default/layout.html
new file mode 100644
index 00000000..9a909b0b
--- /dev/null
+++ b/sphinx/themes/default/layout.html
@@ -0,0 +1,12 @@
+{% extends "basic/layout.html" %}
+{% block extrahead %}
+{%- if theme_rightsidebar|tobool %}
+
+{%- endif %}
+{%- if theme_stickysidebar|tobool %}
+
+{%- endif %}
+{{ super() }}
+{% endblock %}
diff --git a/sphinx/themes/default/static/rightsidebar.css b/sphinx/themes/default/static/rightsidebar.css
index bc604a89..d21eb096 100644
--- a/sphinx/themes/default/static/rightsidebar.css
+++ b/sphinx/themes/default/static/rightsidebar.css
@@ -1,10 +1,8 @@
-/**
- * Sphinx Doc Design -- Right Side Bar Overrides
- */
-
+/* rightsidebar overrides for default design */
div.sphinxsidebar {
float: right;
+ right: 0; /* for sticky sidebar */
}
div.bodywrapper {
diff --git a/sphinx/themes/default/static/stickysidebar.css b/sphinx/themes/default/static/stickysidebar.css
index dfc99c77..01450853 100644
--- a/sphinx/themes/default/static/stickysidebar.css
+++ b/sphinx/themes/default/static/stickysidebar.css
@@ -1,15 +1,17 @@
-/**
- * Sphinx Doc Design -- Sticky sidebar Overrides
- */
+/* stickysidebar overrides for default design */
div.sphinxsidebar {
top: 30px;
- left: 0px;
+ height: 100%;
+ overflow: auto;
position: fixed;
margin: 0;
- float: none;
+ background-color: #1c4e63;
}
+/* this is nice, but it it leads to hidden headings when jumping
+ to an anchor */
+/*
div.related {
position: fixed;
}
@@ -17,3 +19,4 @@ div.related {
div.documentwrapper {
margin-top: 30px;
}
+*/
diff --git a/sphinx/themes/default/theme.conf b/sphinx/themes/default/theme.conf
index 1f513206..f3d4d5ea 100644
--- a/sphinx/themes/default/theme.conf
+++ b/sphinx/themes/default/theme.conf
@@ -2,3 +2,7 @@
inherit = basic
stylesheet = default.css
pygments_style = sphinx
+
+[options]
+rightsidebar = false
+stickysidebar = false
diff --git a/sphinx/theming.py b/sphinx/theming.py
index cdfe314a..c6e4f4ce 100644
--- a/sphinx/theming.py
+++ b/sphinx/theming.py
@@ -108,7 +108,7 @@ class Theme(object):
"""
try:
return self.themeconf.get(section, name)
- except ConfigParser.NoOptionError:
+ except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
if self.base is not None:
return self.base.get_confstr(section, name)
if default is NODEFAULT:
@@ -117,6 +117,27 @@ class Theme(object):
else:
return default
+ def get_options(self, overrides):
+ """
+ Return a dictionary of theme options and their values.
+ """
+ chain = [self.themeconf]
+ base = self.base
+ while base is not None:
+ chain.append(base.themeconf)
+ base = base.base
+ options = {}
+ for conf in reversed(chain):
+ try:
+ options.update(conf.items('options'))
+ except ConfigParser.NoSectionError:
+ pass
+ for option, value in overrides.iteritems():
+ if option not in options:
+ raise ThemeError('unsupported theme option %r given' % option)
+ options[option] = value
+ return options
+
def get_dirchain(self):
"""
Return a list of theme directories, beginning with this theme's,
--
cgit v1.2.1
From 9ffe88706068d587576856dbce47aa97e13d725a Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 15 Feb 2009 02:30:12 +0100
Subject: Update Python docs URL.
---
EXAMPLES | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/EXAMPLES b/EXAMPLES
index dec3c3ef..87c69173 100644
--- a/EXAMPLES
+++ b/EXAMPLES
@@ -43,7 +43,7 @@ included, please mail to `the Google group
* PyPubSub: http://pubsub.sourceforge.net/
* pyrticle: http://documen.tician.de/pyrticle/
* Pysparse: http://pysparse.sourceforge.net/
-* Python: http://docs.python.org/dev/
+* Python: http://docs.python.org/
* python-apt: http://people.debian.org/~jak/python-apt-doc/
* PyUblas: http://documen.tician.de/pyublas/
* Reteisi: http://docs.argolinux.org/reteisi/
--
cgit v1.2.1
From 66a8c8ac80e7d8df9e233f2e8f03edde76eda924 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 15 Feb 2009 02:49:08 +0100
Subject: Use well-named IDs before auto-named IDs.
---
sphinx/environment.py | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/sphinx/environment.py b/sphinx/environment.py
index de04b0f8..2e6c6817 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -134,6 +134,19 @@ class HandleCodeBlocks(Transform):
nodes.doctest_block):
node.replace_self(node.children[0])
+
+class SortIds(Transform):
+ """
+ Sort secion IDs so that the "id[0-9]+" one comes last.
+ """
+ default_priority = 261
+
+ def apply(self):
+ for node in self.document.traverse(nodes.section):
+ if len(node['ids']) > 1 and node['ids'][0].startswith('id'):
+ node['ids'] = node['ids'][1:] + [node['ids'][0]]
+
+
class CitationReferences(Transform):
"""
Handle citation references before the default docutils transform does.
@@ -154,7 +167,7 @@ class SphinxStandaloneReader(standalone.Reader):
Add our own transforms.
"""
transforms = [CitationReferences, DefaultSubstitutions, MoveModuleTargets,
- HandleCodeBlocks]
+ HandleCodeBlocks, SortIds]
def get_transforms(self):
return standalone.Reader.get_transforms(self) + self.transforms
@@ -167,7 +180,6 @@ class SphinxDummyWriter(UnfilteredWriter):
pass
-
class SphinxContentsFilter(ContentsFilter):
"""
Used with BuildEnvironment.add_toc_from() to discard cross-file links
--
cgit v1.2.1
From 6e4173a520f83ee6c58448243ba678b207ecf0bf Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 15 Feb 2009 07:55:30 +0100
Subject: Small template adjustment.
---
sphinx/themes/basic/layout.html | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html
index dda18343..53b9b8b1 100644
--- a/sphinx/themes/basic/layout.html
+++ b/sphinx/themes/basic/layout.html
@@ -165,11 +165,10 @@
{%- endif %}{% endif %}
-{%- endblock %}
-
-{%- block sidebar2 %}{{ sidebar() }}{% endblock %}
+ {%- block sidebar2 %}{{ sidebar() }}{% endblock %}
+{%- endblock %}
{%- block relbar2 %}{{ relbar() }}{% endblock %}
--
cgit v1.2.1
From e24658da95db788a38f55f62fdcb3580247f8c14 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 15 Feb 2009 07:19:48 +0100
Subject: Add support for templated static files.
---
sphinx/application.py | 11 +++++++++--
sphinx/builders/html.py | 11 ++++++++++-
sphinx/jinja2glue.py | 4 ++++
3 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/sphinx/application.py b/sphinx/application.py
index 856f9108..0d87c493 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -350,7 +350,14 @@ class TemplateBridge(object):
def render(self, template, context):
"""
- Called by the builder to render a *template* with a specified
- context (a Python dictionary).
+ Called by the builder to render a template given as a filename with a
+ specified context (a Python dictionary).
+ """
+ raise NotImplementedError('must be implemented in subclasses')
+
+ def render_string(self, template, context):
+ """
+ Called by the builder to render a template given as a string with a
+ specified context (a Python dictionary).
"""
raise NotImplementedError('must be implemented in subclasses')
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index a2414164..3332aa14 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -460,7 +460,16 @@ class StandaloneHTMLBuilder(Builder):
fullname = path.join(staticdirname, filename)
targetname = path.join(self.outdir, '_static', filename)
if path.isfile(fullname):
- shutil.copyfile(fullname, targetname)
+ if fullname.lower().endswith('_t'):
+ # templated!
+ fsrc = open(fullname, 'rb')
+ fdst = open(targetname[:-2], 'wb')
+ fdst.write(self.templates.render_string(
+ fsrc.read(), self.globalcontext))
+ fsrc.close()
+ fdst.close()
+ else:
+ shutil.copyfile(fullname, targetname)
elif path.isdir(fullname):
if filename in self.config.exclude_dirnames:
continue
diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py
index 9fc9ba3a..b7f99e6f 100644
--- a/sphinx/jinja2glue.py
+++ b/sphinx/jinja2glue.py
@@ -44,6 +44,7 @@ class BuiltinTemplateLoader(TemplateBridge, jinja2.BaseLoader):
chain[0:0] = [path.join(builder.confdir, tp)
for tp in builder.config.templates_path]
+ # store it for use in newest_template_mtime
self.pathchain = chain
# make the paths into loaders
@@ -60,6 +61,9 @@ class BuiltinTemplateLoader(TemplateBridge, jinja2.BaseLoader):
def render(self, template, context):
return self.environment.get_template(template).render(context)
+ def render_string(self, source, context):
+ return self.environment.from_string(source).render(context)
+
def newest_template_mtime(self):
return max(mtimes_of_files(self.pathchain, '.html'))
--
cgit v1.2.1
From d45786e63f1c185b799c727de81d7237d0031e90 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 15 Feb 2009 09:35:54 +0100
Subject: Make the default theme color-customizable.
---
sphinx/themes/default/static/default.css | 197 -----------------------------
sphinx/themes/default/static/default.css_t | 197 +++++++++++++++++++++++++++++
sphinx/themes/default/theme.conf | 16 +++
3 files changed, 213 insertions(+), 197 deletions(-)
delete mode 100644 sphinx/themes/default/static/default.css
create mode 100644 sphinx/themes/default/static/default.css_t
diff --git a/sphinx/themes/default/static/default.css b/sphinx/themes/default/static/default.css
deleted file mode 100644
index 40b1f152..00000000
--- a/sphinx/themes/default/static/default.css
+++ /dev/null
@@ -1,197 +0,0 @@
-/**
- * Sphinx stylesheet -- default theme
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-@import url("basic.css");
-
-/* -- page layout ----------------------------------------------------------- */
-
-body {
- font-family: sans-serif;
- font-size: 100%;
- background-color: #11303d;
- color: #000;
- margin: 0;
- padding: 0;
-}
-
-div.document {
- background-color: #1c4e63;
-}
-
-div.body {
- background-color: white;
- padding: 0 20px 30px 20px;
-}
-
-div.footer {
- color: #fff;
- width: 100%;
- padding: 9px 0 9px 0;
- text-align: center;
- font-size: 75%;
-}
-
-div.footer a {
- color: #fff;
- text-decoration: underline;
-}
-
-div.related {
- background-color: #133f52;
- line-height: 30px;
- color: #fff;
-}
-
-div.related a {
- color: white;
-}
-
-div.sphinxsidebar h3 {
- font-family: 'Trebuchet MS', sans-serif;
- color: white;
- font-size: 1.4em;
- font-weight: normal;
- margin: 0;
- padding: 0;
-}
-
-div.sphinxsidebar h3 a {
- color: white;
-}
-
-div.sphinxsidebar h4 {
- font-family: 'Trebuchet MS', sans-serif;
- color: white;
- font-size: 1.3em;
- font-weight: normal;
- margin: 5px 0 0 0;
- padding: 0;
-}
-
-div.sphinxsidebar p {
- color: white;
-}
-
-div.sphinxsidebar p.topless {
- margin: 5px 10px 10px 10px;
-}
-
-div.sphinxsidebar ul {
- margin: 10px;
- padding: 0;
- color: white;
-}
-
-div.sphinxsidebar a {
- color: #98dbcc;
-}
-
-div.sphinxsidebar input {
- border: 1px solid #98dbcc;
- font-family: sans-serif;
- font-size: 1em;
-}
-
-/* -- body styles ----------------------------------------------------------- */
-
-a {
- color: #355f7c;
- text-decoration: none;
-}
-
-a:hover {
- text-decoration: underline;
-}
-
-div.body p, div.body dd, div.body li {
- text-align: justify;
- line-height: 130%;
-}
-
-div.body h1,
-div.body h2,
-div.body h3,
-div.body h4,
-div.body h5,
-div.body h6 {
- font-family: 'Trebuchet MS', sans-serif;
- background-color: #f2f2f2;
- font-weight: normal;
- color: #20435c;
- border-bottom: 1px solid #ccc;
- margin: 20px -20px 10px -20px;
- padding: 3px 0 3px 10px;
-}
-
-div.body h1 { margin-top: 0; font-size: 200%; }
-div.body h2 { font-size: 160%; }
-div.body h3 { font-size: 140%; }
-div.body h4 { font-size: 120%; }
-div.body h5 { font-size: 110%; }
-div.body h6 { font-size: 100%; }
-
-a.headerlink {
- color: #c60f0f;
- font-size: 0.8em;
- padding: 0 4px 0 4px;
- text-decoration: none;
-}
-
-a.headerlink:hover {
- background-color: #c60f0f;
- color: white;
-}
-
-div.body p, div.body dd, div.body li {
- text-align: justify;
- line-height: 130%;
-}
-
-div.admonition p.admonition-title + p {
- display: inline;
-}
-
-div.note {
- background-color: #eee;
- border: 1px solid #ccc;
-}
-
-div.seealso {
- background-color: #ffc;
- border: 1px solid #ff6;
-}
-
-div.topic {
- background-color: #eee;
-}
-
-div.warning {
- background-color: #ffe4e4;
- border: 1px solid #f66;
-}
-
-p.admonition-title {
- display: inline;
-}
-
-p.admonition-title:after {
- content: ":";
-}
-
-pre {
- padding: 5px;
- background-color: #efc;
- color: #333;
- line-height: 120%;
- border: 1px solid #ac9;
- border-left: none;
- border-right: none;
-}
-
-tt {
- background-color: #ecf0f3;
- padding: 0 1px 0 1px;
- font-size: 0.95em;
-}
diff --git a/sphinx/themes/default/static/default.css_t b/sphinx/themes/default/static/default.css_t
new file mode 100644
index 00000000..b1feb252
--- /dev/null
+++ b/sphinx/themes/default/static/default.css_t
@@ -0,0 +1,197 @@
+/**
+ * Sphinx stylesheet -- default theme
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+@import url("basic.css");
+
+/* -- page layout ----------------------------------------------------------- */
+
+body {
+ font-family: {{ theme_bodyfont }};
+ font-size: 100%;
+ background-color: {{ theme_footerbgcolor }};
+ color: #000;
+ margin: 0;
+ padding: 0;
+}
+
+div.document {
+ background-color: {{ theme_sidebarbgcolor }};
+}
+
+div.body {
+ background-color: white;
+ padding: 0 20px 30px 20px;
+}
+
+div.footer {
+ color: {{ theme_footertextcolor }};
+ width: 100%;
+ padding: 9px 0 9px 0;
+ text-align: center;
+ font-size: 75%;
+}
+
+div.footer a {
+ color: {{ theme_footertextcolor }};
+ text-decoration: underline;
+}
+
+div.related {
+ background-color: {{ theme_relbarbgcolor }};
+ line-height: 30px;
+ color: {{ theme_relbartextcolor }};
+}
+
+div.related a {
+ color: {{ theme_relbarlinkcolor }};
+}
+
+div.sphinxsidebar h3 {
+ font-family: {{ theme_headfont }};
+ color: {{ theme_sidebartextcolor }};
+ font-size: 1.4em;
+ font-weight: normal;
+ margin: 0;
+ padding: 0;
+}
+
+div.sphinxsidebar h3 a {
+ color: {{ theme_sidebartextcolor }};
+}
+
+div.sphinxsidebar h4 {
+ font-family: {{ theme_headfont }};
+ color: {{ theme_sidebartextcolor }};
+ font-size: 1.3em;
+ font-weight: normal;
+ margin: 5px 0 0 0;
+ padding: 0;
+}
+
+div.sphinxsidebar p {
+ color: {{ theme_sidebartextcolor }};
+}
+
+div.sphinxsidebar p.topless {
+ margin: 5px 10px 10px 10px;
+}
+
+div.sphinxsidebar ul {
+ margin: 10px;
+ padding: 0;
+ color: {{ theme_sidebartextcolor }};
+}
+
+div.sphinxsidebar a {
+ color: {{ theme_sidebarlinkcolor }};
+}
+
+div.sphinxsidebar input {
+ border: 1px solid {{ theme_sidebarlinkcolor }};
+ font-family: sans-serif;
+ font-size: 1em;
+}
+
+/* -- body styles ----------------------------------------------------------- */
+
+a {
+ color: {{ theme_linkcolor }};
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+div.body p, div.body dd, div.body li {
+ text-align: justify;
+ line-height: 130%;
+}
+
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+ font-family: {{ theme_headfont }};
+ background-color: #f2f2f2;
+ font-weight: normal;
+ color: {{ theme_headcolor }};
+ border-bottom: 1px solid #ccc;
+ margin: 20px -20px 10px -20px;
+ padding: 3px 0 3px 10px;
+}
+
+div.body h1 { margin-top: 0; font-size: 200%; }
+div.body h2 { font-size: 160%; }
+div.body h3 { font-size: 140%; }
+div.body h4 { font-size: 120%; }
+div.body h5 { font-size: 110%; }
+div.body h6 { font-size: 100%; }
+
+a.headerlink {
+ color: #c60f0f;
+ font-size: 0.8em;
+ padding: 0 4px 0 4px;
+ text-decoration: none;
+}
+
+a.headerlink:hover {
+ background-color: #c60f0f;
+ color: white;
+}
+
+div.body p, div.body dd, div.body li {
+ text-align: justify;
+ line-height: 130%;
+}
+
+div.admonition p.admonition-title + p {
+ display: inline;
+}
+
+div.note {
+ background-color: #eee;
+ border: 1px solid #ccc;
+}
+
+div.seealso {
+ background-color: #ffc;
+ border: 1px solid #ff6;
+}
+
+div.topic {
+ background-color: #eee;
+}
+
+div.warning {
+ background-color: #ffe4e4;
+ border: 1px solid #f66;
+}
+
+p.admonition-title {
+ display: inline;
+}
+
+p.admonition-title:after {
+ content: ":";
+}
+
+pre {
+ padding: 5px;
+ background-color: {{ theme_codebgcolor }};
+ color: {{ theme_codecolor }};
+ line-height: 120%;
+ border: 1px solid #ac9;
+ border-left: none;
+ border-right: none;
+}
+
+tt {
+ background-color: #ecf0f3;
+ padding: 0 1px 0 1px;
+ font-size: 0.95em;
+}
diff --git a/sphinx/themes/default/theme.conf b/sphinx/themes/default/theme.conf
index f3d4d5ea..5c1f13ac 100644
--- a/sphinx/themes/default/theme.conf
+++ b/sphinx/themes/default/theme.conf
@@ -6,3 +6,19 @@ pygments_style = sphinx
[options]
rightsidebar = false
stickysidebar = false
+
+footerbgcolor = #11303d
+footertextcolor = #ffffff
+sidebarbgcolor = #1c4e63
+sidebartextcolor = #ffffff
+sidebarlinkcolor = #98dbcc
+relbarbgcolor = #133f52
+relbartextcolor = #ffffff
+relbarlinkcolor = #ffffff
+headcolor = #20435c
+linkcolor = #355f7c
+codebgcolor = #eeffcc
+codecolor = #333333
+
+bodyfont = sans-serif
+headfont = 'Trebuchet MS', sans-serif
--
cgit v1.2.1
From 60b16073391c89a574e345f5e652f6b0c81db00b Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 15 Feb 2009 10:25:39 +0100
Subject: Make the relbar and sidebar fonts a bit larger.
---
sphinx/themes/sphinxdoc/static/sphinxdoc.css | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/sphinx/themes/sphinxdoc/static/sphinxdoc.css b/sphinx/themes/sphinxdoc/static/sphinxdoc.css
index df8c4e02..1d11e8b9 100644
--- a/sphinx/themes/sphinxdoc/static/sphinxdoc.css
+++ b/sphinx/themes/sphinxdoc/static/sphinxdoc.css
@@ -46,6 +46,10 @@ div.body {
padding: 0.5em 20px 20px 20px;
}
+div.related {
+ font-size: 1em;
+}
+
div.related ul {
background-image: url(navigation.png);
height: 2em;
@@ -85,6 +89,7 @@ div.sphinxsidebar {
padding: 0.5em 15px 15px 0;
width: 210px;
float: right;
+ font-size: 1em;
text-align: left;
}
--
cgit v1.2.1
From 7c126bab2dd6aac2dca713e6fbe5723f270f5110 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 15 Feb 2009 10:25:55 +0100
Subject: Make body colors customizable.
---
sphinx/themes/default/static/default.css_t | 5 +++--
sphinx/themes/default/theme.conf | 4 +++-
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/sphinx/themes/default/static/default.css_t b/sphinx/themes/default/static/default.css_t
index b1feb252..2f4b4d86 100644
--- a/sphinx/themes/default/static/default.css_t
+++ b/sphinx/themes/default/static/default.css_t
@@ -21,7 +21,8 @@ div.document {
}
div.body {
- background-color: white;
+ background-color: {{ theme_bgcolor }};
+ color: {{ theme_textcolor }};
padding: 0 20px 30px 20px;
}
@@ -183,7 +184,7 @@ p.admonition-title:after {
pre {
padding: 5px;
background-color: {{ theme_codebgcolor }};
- color: {{ theme_codecolor }};
+ color: {{ theme_codetextcolor }};
line-height: 120%;
border: 1px solid #ac9;
border-left: none;
diff --git a/sphinx/themes/default/theme.conf b/sphinx/themes/default/theme.conf
index 5c1f13ac..9a24b978 100644
--- a/sphinx/themes/default/theme.conf
+++ b/sphinx/themes/default/theme.conf
@@ -15,10 +15,12 @@ sidebarlinkcolor = #98dbcc
relbarbgcolor = #133f52
relbartextcolor = #ffffff
relbarlinkcolor = #ffffff
+bgcolor = #ffffff
+textcolor = #000000
headcolor = #20435c
linkcolor = #355f7c
codebgcolor = #eeffcc
-codecolor = #333333
+codetextcolor = #333333
bodyfont = sans-serif
headfont = 'Trebuchet MS', sans-serif
--
cgit v1.2.1
From 32c59d15b976d1c6900e173fffab2df37d07b880 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 15 Feb 2009 10:31:52 +0100
Subject: Handle sidebar options directly in the stylesheet.
---
sphinx/themes/default/layout.html | 12 ---------
sphinx/themes/default/static/default.css_t | 36 ++++++++++++++++++++++++++
sphinx/themes/default/static/rightsidebar.css | 14 ----------
sphinx/themes/default/static/stickysidebar.css | 22 ----------------
4 files changed, 36 insertions(+), 48 deletions(-)
delete mode 100644 sphinx/themes/default/layout.html
delete mode 100644 sphinx/themes/default/static/rightsidebar.css
delete mode 100644 sphinx/themes/default/static/stickysidebar.css
diff --git a/sphinx/themes/default/layout.html b/sphinx/themes/default/layout.html
deleted file mode 100644
index 9a909b0b..00000000
--- a/sphinx/themes/default/layout.html
+++ /dev/null
@@ -1,12 +0,0 @@
-{% extends "basic/layout.html" %}
-{% block extrahead %}
-{%- if theme_rightsidebar|tobool %}
-
-{%- endif %}
-{%- if theme_stickysidebar|tobool %}
-
-{%- endif %}
-{{ super() }}
-{% endblock %}
diff --git a/sphinx/themes/default/static/default.css_t b/sphinx/themes/default/static/default.css_t
index 2f4b4d86..77307084 100644
--- a/sphinx/themes/default/static/default.css_t
+++ b/sphinx/themes/default/static/default.css_t
@@ -26,6 +26,12 @@ div.body {
padding: 0 20px 30px 20px;
}
+{%- if theme_rightsidebar|tobool %}
+div.bodywrapper {
+ margin: 0 230px 0 0;
+}
+{%- endif %}
+
div.footer {
color: {{ theme_footertextcolor }};
width: 100%;
@@ -49,6 +55,36 @@ div.related a {
color: {{ theme_relbarlinkcolor }};
}
+div.sphinxsidebar {
+ {%- if theme_stickysidebar|tobool %}
+ top: 30px;
+ margin: 0;
+ position: fixed;
+ overflow: auto;
+ height: 100%;
+ {%- endif %}
+ {%- if theme_rightsidebar|tobool %}
+ float: right;
+ {%- if theme_stickysidebar|tobool %}
+ right: 0;
+ {%- endif %}
+ {%- endif %}
+}
+
+{%- if theme_stickysidebar|tobool %}
+/* this is nice, but it it leads to hidden headings when jumping
+ to an anchor */
+/*
+div.related {
+ position: fixed;
+}
+
+div.documentwrapper {
+ margin-top: 30px;
+}
+*/
+{%- endif %}
+
div.sphinxsidebar h3 {
font-family: {{ theme_headfont }};
color: {{ theme_sidebartextcolor }};
diff --git a/sphinx/themes/default/static/rightsidebar.css b/sphinx/themes/default/static/rightsidebar.css
deleted file mode 100644
index d21eb096..00000000
--- a/sphinx/themes/default/static/rightsidebar.css
+++ /dev/null
@@ -1,14 +0,0 @@
-/* rightsidebar overrides for default design */
-
-div.sphinxsidebar {
- float: right;
- right: 0; /* for sticky sidebar */
-}
-
-div.bodywrapper {
- margin: 0 230px 0 0;
-}
-
-div.inlinecomments {
- right: 250px;
-}
diff --git a/sphinx/themes/default/static/stickysidebar.css b/sphinx/themes/default/static/stickysidebar.css
deleted file mode 100644
index 01450853..00000000
--- a/sphinx/themes/default/static/stickysidebar.css
+++ /dev/null
@@ -1,22 +0,0 @@
-/* stickysidebar overrides for default design */
-
-div.sphinxsidebar {
- top: 30px;
- height: 100%;
- overflow: auto;
- position: fixed;
- margin: 0;
- background-color: #1c4e63;
-}
-
-/* this is nice, but it it leads to hidden headings when jumping
- to an anchor */
-/*
-div.related {
- position: fixed;
-}
-
-div.documentwrapper {
- margin-top: 30px;
-}
-*/
--
cgit v1.2.1
From da34a553fef2c92961ea8ac3ef1b19428f0cb2c6 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 15 Feb 2009 10:45:46 +0100
Subject: Theming docs, part 1.
---
CHANGES | 2 ++
doc/config.rst | 30 ++++++++++++++--
doc/contents.rst | 1 +
doc/theming.rst | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 137 insertions(+), 3 deletions(-)
create mode 100644 doc/theming.rst
diff --git a/CHANGES b/CHANGES
index addc3e73..2d4342bc 100644
--- a/CHANGES
+++ b/CHANGES
@@ -20,6 +20,8 @@ New features added
if name.startswith('_'):
return True
+* Theming support.
+
* Markup:
- Due to popular demand, added a ``:doc:`` role which directly
diff --git a/doc/config.rst b/doc/config.rst
index e5ec3810..c55f4a8e 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -305,6 +305,29 @@ Options for HTML output
These options influence HTML as well as HTML Help output, and other builders
that use Sphinx' HTMLWriter class.
+.. confval:: html_theme
+
+ The "theme" that the HTML output should use. See the :doc:`section about
+ theming `. The default is ``'default'``.
+
+ .. versionadded:: 0.6
+
+.. confval:: html_theme_options
+
+ A dictionary of options that influence the look and feel of the selected
+ theme. These are theme-specific. For the options understood by the builtin
+ themes, see :ref:`this section `.
+
+ .. versionadded:: 0.6
+
+.. confval:: html_theme_path
+
+ A list of paths that contain custom themes, either as subdirectories or as
+ zip files. Relative paths are taken as relative to the configuration
+ directory.
+
+ .. versionadded:: 0.6
+
.. confval:: html_title
The "title" for HTML documentation generated with Sphinx' own templates.
@@ -325,7 +348,8 @@ that use Sphinx' HTMLWriter class.
The style sheet to use for HTML pages. A file of that name must exist either
in Sphinx' :file:`static/` path, or in one of the custom paths given in
- :confval:`html_static_path`. Default is ``'default.css'``.
+ :confval:`html_static_path`. Default is the stylesheet given by the selected
+ theme.
.. confval:: html_logo
@@ -350,8 +374,8 @@ that use Sphinx' HTMLWriter class.
A list of paths that contain custom static files (such as style sheets or
script files). Relative paths are taken as relative to the configuration
- directory. They are copied to the output directory after the builtin static
- files, so a file named :file:`default.css` will overwrite the builtin
+ directory. They are copied to the output directory after the theme's static
+ files, so a file named :file:`default.css` will overwrite the theme's
:file:`default.css`.
.. versionchanged:: 0.4
diff --git a/doc/contents.rst b/doc/contents.rst
index c4fb107b..1f3860ea 100644
--- a/doc/contents.rst
+++ b/doc/contents.rst
@@ -12,6 +12,7 @@ Sphinx documentation contents
markup/index
builders
config
+ theming
templating
extensions
diff --git a/doc/theming.rst b/doc/theming.rst
new file mode 100644
index 00000000..b3721248
--- /dev/null
+++ b/doc/theming.rst
@@ -0,0 +1,107 @@
+.. highlightlang:: python
+
+HTML theming support
+====================
+
+.. versionadded:: 0.6
+
+Sphinx supports changing the appearance of its HTML output via *themes*. A
+theme is a collection of HTML templates, stylesheet(s) and other static files.
+Additionally, it has a configuration file which specifies from which theme to
+inherit, which highlighting style to use, and what options exist for customizing
+the theme's look and feel.
+
+Themes are meant to be project-unaware, so they can be used for different
+projects without change.
+
+
+Using a theme
+-------------
+
+Using an existing theme is easy. If the theme is builtin to Sphinx, you only
+need to set the :confval:`html_theme` config value. With the
+:confval:`html_theme_options` config value you can set theme-specific options
+that change the look and feel. For example, you could have the following in
+your :file:`conf.py`::
+
+ html_theme = "default"
+ html_theme_options = {
+ "rightsidebar": "true",
+ "relbarbgcolor: "black"
+ }
+
+That would give you the default theme, but with a sidebar on the right side and
+a black background for the relation bar (the bar with the navigation links at
+the page's top and bottom).
+
+If the theme does not come with Sphinx, it can be in two forms: either a
+directory (containing :file:`theme.conf` and other needed files), or a zip file
+with the same contents. Either of them must be put where Sphinx can find it;
+for this there is the config value :confval:`html_theme_path`. It gives a list
+of directories, relative to the directory containing :file:`conf.py`, that can
+contain theme directories or zip files. For example, if you have a theme in the
+file :file:`blue.zip`, you can put it right in the directory containing
+:file:`conf.py` and use this configuration::
+
+ html_theme = "blue"
+ html_theme_path = ["."]
+
+
+.. _builtin-themes:
+
+Builtin themes
+--------------
+
+Sphinx comes with a selection of themes to choose from:
+
+* **basic** -- This is a basically unstyled layout used as the base for the
+ *default* and *sphinxdoc* themes, and usable as the base for custom themes as
+ well. The HTML contains all important elements like sidebar and relation bar.
+ There is one option (which is inherited by *default* and *sphinxdoc*):
+
+ - **nosidebar** (true or false): Don't include the sidebar. Defaults to
+ false.
+
+* **default** -- This is the default theme. It can be customized via these
+ options:
+
+ - **rightsidebar** (true or false): Put the sidebar on the right side.
+ Defaults to false.
+
+ - **stickysidebar** (true or false): Make the sidebar "fixed" so that it
+ doesn't scroll out of view for long body content. This may not work well
+ with all browsers. Defaults to false.
+
+ There are also various color and font options that can change the color scheme
+ without having to write a custom stylesheet:
+
+ - **footerbgcolor** (CSS color): Background color for the footer line.
+ - **footertextcolor** (CSS color): Text color for the footer line.
+ - **sidebarbgcolor** (CSS color): Background color for the sidebar.
+ - **sidebartextcolor** (CSS color): Text color for the sidebar.
+ - **sidebarlinkcolor** (CSS color): Link color for the sidebar.
+ - **relbarbgcolor** (CSS color): Background color for the relation bar.
+ - **relbartextcolor** (CSS color): Text color for the relation bar.
+ - **relbarlinkcolor** (CSS color): Link color for the relation bar.
+ - **bgcolor** (CSS color): Body background color.
+ - **textcolor** (CSS color): Body text color.
+ - **linkcolor** (CSS color): Body link color.
+ - **headcolor** (CSS color): Text color for headings.
+ - **codebgcolor** (CSS color): Background color for code blocks.
+ - **codetextcolor** (CSS color): Default text color for code blocks, if not
+ set differently by the highlighting style.
+
+ - **bodyfont** (CSS font-family): Font for normal text.
+ - **headfont** (CSS font-family): Font for headings.
+
+* **sphinxdoc** -- The theme used for this documentation. It features a sidebar
+ on the right side. There are currently no options beyond *nosidebar*.
+
+..
+ * option specs
+ * zipfiles
+ * old config values work
+ * static/
+ * theme.conf
+ * _t templates
+
--
cgit v1.2.1
From 7f87c67ccded0036793a2f9cc20120ea3678dcc4 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 15 Feb 2009 11:03:16 +0100
Subject: Theming docs, part 2.
---
doc/config.rst | 22 +++++++++---------
doc/theming.rst | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++------
2 files changed, 74 insertions(+), 17 deletions(-)
diff --git a/doc/config.rst b/doc/config.rst
index c55f4a8e..efa1fa85 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -131,15 +131,16 @@ General configuration
.. confval:: templates_path
A list of paths that contain extra templates (or templates that overwrite
- builtin templates). Relative paths are taken as relative to the
- configuration directory.
+ builtin/theme-specific templates). Relative paths are taken as relative to
+ the configuration directory.
.. confval:: template_bridge
A string with the fully-qualified name of a callable (or simply a class) that
returns an instance of :class:`~sphinx.application.TemplateBridge`. This
instance is then used to render HTML documents, and possibly the output of
- other builders (currently the changes builder).
+ other builders (currently the changes builder). (Note that the template
+ bridge must be made theme-aware if HTML themes are to be used.)
.. confval:: rst_epilog
@@ -328,6 +329,14 @@ that use Sphinx' HTMLWriter class.
.. versionadded:: 0.6
+.. confval:: html_style
+
+ The style sheet to use for HTML pages. A file of that name must exist either
+ in Sphinx' :file:`static/` path, or in one of the custom paths given in
+ :confval:`html_static_path`. Default is the stylesheet given by the selected
+ theme. If you only want to add or override a few things compared to the
+ theme's stylesheet, use CSS ``@import`` to import the theme's stylesheet.
+
.. confval:: html_title
The "title" for HTML documentation generated with Sphinx' own templates.
@@ -344,13 +353,6 @@ that use Sphinx' HTMLWriter class.
.. versionadded:: 0.4
-.. confval:: html_style
-
- The style sheet to use for HTML pages. A file of that name must exist either
- in Sphinx' :file:`static/` path, or in one of the custom paths given in
- :confval:`html_static_path`. Default is the stylesheet given by the selected
- theme.
-
.. confval:: html_logo
If given, this must be the name of an image file that is the logo of the
diff --git a/doc/theming.rst b/doc/theming.rst
index b3721248..9f7786fb 100644
--- a/doc/theming.rst
+++ b/doc/theming.rst
@@ -97,11 +97,66 @@ Sphinx comes with a selection of themes to choose from:
* **sphinxdoc** -- The theme used for this documentation. It features a sidebar
on the right side. There are currently no options beyond *nosidebar*.
-..
- * option specs
- * zipfiles
- * old config values work
- * static/
- * theme.conf
- * _t templates
+
+Creating themes
+---------------
+
+As said, themes are either a directory or a zipfile (whose name is the theme
+name), containing the following:
+
+* A :file:`theme.conf` file, see below.
+* HTML templates, if needed.
+* A ``static/`` directory containing any static files that will be copied to the
+ output statid directory on build. These can be images, styles, script files.
+
+The :file:`theme.conf` file is in INI format [1]_ (readable by the standard
+Python :mod:`ConfigParser` module) and has the following structure::
+
+ [theme]
+ inherit = base theme
+ stylesheet = main CSS name
+ pygments_style = stylename
+
+ [options]
+ variable = default value
+
+* The **inherit** setting gives the name of a "base theme", or ``none``. The
+ base theme will be used to locate missing templates, its options will be
+ inherited, and all of its static files will be used as well.
+
+* The **stylesheet** setting gives the name of a CSS file which will be
+ referenced in the HTML header. If you need more than one CSS file, either
+ include one from the other via CSS' ``@import``, or use a custom HTML template
+ that adds ```` tags as necessary. Setting the
+ :confval:`html_style` config value will override this setting.
+
+* The **pygments_style** setting gives the name of a Pygments style to use for
+ highlighting. This can be overridden by the user in the
+ :confval:`pygments_style` config value.
+
+* The **options** section contains pairs of variable names and default values.
+ These options can be overridden by the user in :confval:`html_theme_options`
+ and are accessible from all templates as ``theme_``.
+
+
+Static templates
+----------------
+
+Since theme options are meant for the user to configure a theme more easily,
+without having to write a custom stylesheet, it is necessary to be able to
+template static files as well as HTML files. Therefore, Sphinx supports
+so-called "static templates", like this:
+
+If the name of a file in the ``static/`` directory of a theme (or in the user's
+static path, for that matter) ends with ``_t``, it will be processed by the
+template engine. The ``_t`` will be left from the final file name. For
+example, the *default* theme has a file ``static/default.css_t`` which uses
+templating to put the color options into the stylesheet. When a documentation
+is built with the default theme, the output directory will contain a
+``_static/default.css`` file where all template tags have been processed.
+
+
+.. [1] It is not an executable Python file, as opposed to :file:`conf.py`,
+ because that would pose an unnecessary security risk if themes are
+ shared.
--
cgit v1.2.1
From d333ec6a6c091203be3d37af4550ecdc56c14727 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 15 Feb 2009 11:38:23 +0100
Subject: More templating docs.
---
doc/markup/misc.rst | 2 +
doc/templating.rst | 183 +++++++++++++++++++++++++++++++++++++++++-----------
doc/theming.rst | 24 ++++++-
3 files changed, 168 insertions(+), 41 deletions(-)
diff --git a/doc/markup/misc.rst b/doc/markup/misc.rst
index 98f5485e..03fbdeea 100644
--- a/doc/markup/misc.rst
+++ b/doc/markup/misc.rst
@@ -3,6 +3,8 @@
Miscellaneous markup
====================
+.. _metadata::
+
File-wide metadata
------------------
diff --git a/doc/templating.rst b/doc/templating.rst
index 20c61ce0..2ee449aa 100644
--- a/doc/templating.rst
+++ b/doc/templating.rst
@@ -1,3 +1,5 @@
+.. highlight:: html+jinja
+
.. _templating:
Templating
@@ -37,27 +39,26 @@ template, customizing it while also keeping the changes at a minimum.
To customize the output of your documentation you can override all the templates
(both the layout templates and the child templates) by adding files with the
-same name as the original filename into the template directory of the folder the
-Sphinx quickstart generated for you.
+same name as the original filename into the template directory of the structure
+the Sphinx quickstart generated for you.
Sphinx will look for templates in the folders of :confval:`templates_path`
first, and if it can't find the template it's looking for there, it falls back
-to the builtin templates that come with Sphinx.
+to the selected theme's templates.
A template contains **variables**, which are replaced with values when the
template is evaluated, **tags**, which control the logic of the template and
**blocks** which are used for template inheritance.
-Sphinx provides base templates with a couple of blocks it will fill with data.
-The default templates are located in the :file:`templates` folder of the Sphinx
-installation directory. Templates with the same name in the
-:confval:`templates_path` override templates located in the builtin folder.
+Sphinx' *basic* theme provides base templates with a couple of blocks it will
+fill with data. These are located in the :file:`themes/basic` subdirectory of
+the Sphinx installation directory, and used by all builtin Sphinx themes.
+Templates with the same name in the :confval:`templates_path` override templates
+supplied by the selected theme.
For example, to add a new link to the template area containing related links all
you have to do is to add a new template called ``layout.html`` with the
-following contents:
-
-.. sourcecode:: html+jinja
+following contents::
{% extends "!layout.html" %}
{% block rootrellink %}
@@ -65,16 +66,24 @@ following contents:
{{ super() }}
{% endblock %}
-By prefixing the name of the extended template with an exclamation mark, Sphinx
-will load the builtin layout template. If you override a block, you should call
-``{{ super() }}`` somewhere to render the block's content in the extended
-template -- unless you don't want that content to show up.
+By prefixing the name of the overridden template with an exclamation mark,
+Sphinx will load the layout template from the underlying HTML theme.
+
+**Important**: If you override a block, call ``{{ super() }}`` somewhere to
+render the block's content in the extended template -- unless you don't want
+that content to show up.
+
+Working the the builtin templates
+---------------------------------
+
+The builtin **basic** theme supplies the templates that all builtin Sphinx
+themes are based on. It has the following elements you can override or use:
Blocks
~~~~~~
-The following blocks exist in the ``layout`` template:
+The following blocks exist in the ``layout.html`` template:
`doctype`
The doctype of the output format. By default this is XHTML 1.0 Transitional
@@ -92,11 +101,11 @@ The following blocks exist in the ``layout`` template:
add references to JavaScript or extra CSS files.
`relbar1` / `relbar2`
- This block contains the list of related links (the parent documents on the
- left, and the links to index, modules etc. on the right). `relbar1` appears
- before the document, `relbar2` after the document. By default, both blocks
- are filled; to show the relbar only before the document, you would override
- `relbar2` like this::
+ This block contains the *relation bar*, the list of related links (the
+ parent documents on the left, and the links to index, modules etc. on the
+ right). `relbar1` appears before the document, `relbar2` after the
+ document. By default, both blocks are filled; to show the relbar only
+ before the document, you would override `relbar2` like this::
{% block relbar2 %}{% endblock %}
@@ -109,7 +118,8 @@ The following blocks exist in the ``layout`` template:
the :data:`reldelim1`.
`document`
- The contents of the document itself.
+ The contents of the document itself. It contains the block "body" where the
+ individual content is put by subtemplates like ``page.html``.
`sidebar1` / `sidebar2`
A possible location for a sidebar. `sidebar1` appears before the document
@@ -166,13 +176,17 @@ using the ``{% set %}`` tag:
defaults to ``' |'``. Each item except of the last one in the related bar
ends with the value of this variable.
-Overriding works like this:
-
-.. sourcecode:: html+jinja
+Overriding works like this::
{% extends "!layout.html" %}
{% set reldelim1 = ' >' %}
+.. data:: script_files
+
+ Add additional script files here, like this::
+
+ {% set script_files = script_files + [pathto("_static/myscript.js", 1)] %}
+
Helper Functions
~~~~~~~~~~~~~~~~
@@ -200,7 +214,7 @@ them to generate links or output multiply used elements.
.. function:: relbar()
- Return the rendered relbar.
+ Return the rendered relation bar.
Global Variables
@@ -210,45 +224,138 @@ These global variables are available in every template and are safe to use.
There are more, but most of them are an implementation detail and might change
in the future.
+.. data:: builder
+
+ The name of the builder (e.g. ``html`` or ``htmlhelp``).
+
+.. data:: copyright
+
+ The value of :confval:`copyright`.
+
.. data:: docstitle
The title of the documentation (the value of :confval:`html_title`).
-.. data:: sourcename
+.. data:: embedded
- The name of the copied source file for the current document. This is only
- nonempty if the :confval:`html_copy_source` value is true.
+ True if the built HTML is meant to be embedded in some viewing application
+ that handles navigation, not the web browser, such as for HTML help or Qt
+ help formats. In this case, the sidebar is not included.
-.. data:: builder
+.. data:: favicon
- The name of the builder (e.g. ``html`` or ``htmlhelp``).
+ The path to the HTML favicon in the static path, or ``''``.
-.. data:: embedded
+.. data:: file_suffix
+
+ The value of the builder's :attr:`out_suffix` attribute, i.e. the file name
+ extension that the output files will get. For a standard HTML builder, this
+ is usually ``.html``.
+
+.. data:: has_source
- True if the built HTML is supposed to be embedded in some application that
- handles navigation, e.g. HTML Help or Qt Help.
+ True if the reST document sources are copied (if :confval:`html_copy_source`
+ is true).
+
+.. data:: last_updated
+
+ The build date.
+
+.. data:: logo
+
+ The path to the HTML logo image in the static path, or ``''``.
+
+.. data:: master_doc
+
+ The value of :confval:`master_doc`, for usage with :func:`pathto`.
.. data:: next
The next document for the navigation. This variable is either false or has
two attributes `link` and `title`. The title contains HTML markup. For
- example, to generate a link to the next page, you can use this snippet:
-
- .. sourcecode:: html+jinja
+ example, to generate a link to the next page, you can use this snippet::
{% if next %}
{{ next.title }}
{% endif %}
+.. data:: pagename
+
+ The "page name" of the current file, i.e. either the document name if the
+ file is generated from a reST source, or the equivalent hierarchical name
+ relative to the output directory (``[directory/]filename_without_extension``).
+
+.. data:: parents
+
+ A list of parent documents for navigation, structured like the :data:`next`
+ item.
+
.. data:: prev
Like :data:`next`, but for the previous page.
+.. data:: project
+
+ The value of :confval:`project`.
+
+.. data:: release
+
+ The value of :confval:`release`.
+
+.. data:: rellinks
+
+ A list of links to put at the left side of the relbar, next to "next" and
+ "prev". This usually contains links to the index and the modindex. If you
+ add something yourself, it must be a tuple ``(pagename, link title,
+ accesskey, link text)``.
+
+.. data:: shorttitle
+
+ The value of :confval:`html_short_title`.
+
+.. data:: show_source
+
+ True if :confval:`html_show_sourcelink` is true.
+
+.. data:: sphinx_version
+
+ The version of Sphinx used to build.
+
+.. data:: style
+
+ The name of the main stylesheet, as given by the theme or
+ :confval:`html_style`.
+.. data:: title
+
+ The title of the current document, as used in the ```` tag.
+
+.. data:: use_opensearch
+
+ The value of :confval:`html_use_opensearch`.
+
+.. data:: version
+
+ The value of :confval:`version`.
+
+
+In addition to these values, there are also all **theme options** available
+(prefixed by ``theme_``), as well as the values given by the user in
+:confval:`html_context`.
+
In documents that are created from source files (as opposed to
automatically-generated files like the module index, or documents that already
are in HTML form), these variables are also available:
+.. data:: meta
+
+ Document metadata, see :ref:`metadata`.
+
+.. data:: sourcename
+
+ The name of the copied source file for the current document. This is only
+ nonempty if the :confval:`html_copy_source` value is true.
+
.. data:: toc
The local table of contents for the current page, rendered as HTML bullet
@@ -256,5 +363,5 @@ are in HTML form), these variables are also available:
.. data:: toctree
- The global TOC tree containing the current page, rendered as HTML bullet
- lists.
+ A callable yielding the global TOC tree containing the current page, rendered
+ as HTML bullet lists.
diff --git a/doc/theming.rst b/doc/theming.rst
index 9f7786fb..bc150f4c 100644
--- a/doc/theming.rst
+++ b/doc/theming.rst
@@ -121,8 +121,9 @@ Python :mod:`ConfigParser` module) and has the following structure::
variable = default value
* The **inherit** setting gives the name of a "base theme", or ``none``. The
- base theme will be used to locate missing templates, its options will be
- inherited, and all of its static files will be used as well.
+ base theme will be used to locate missing templates (most themes will not have
+ to supply most templates if they use ``basic`` as the base theme), its options
+ will be inherited, and all of its static files will be used as well.
* The **stylesheet** setting gives the name of a CSS file which will be
referenced in the HTML header. If you need more than one CSS file, either
@@ -139,8 +140,25 @@ Python :mod:`ConfigParser` module) and has the following structure::
and are accessible from all templates as ``theme_``.
+Templating
+~~~~~~~~~~
+
+The :doc:`guide to templating ` is helpful if you want to write your
+own templates. What is important to keep in mind is the order in which Sphinx
+searches for templates:
+
+* First, in the user's ``templates_path`` directories.
+* Then, in the selected theme.
+* Then, in its base theme, its base's base theme, etc.
+
+From all of these levels, you can inherit templates from the lowernext level by
+prefixing the template name with an exclamation mark in the ``extends`` tag, or
+(in the case of theme templates) giving an explicit path, like
+``basic/layout.html``.
+
+
Static templates
-----------------
+~~~~~~~~~~~~~~~~
Since theme options are meant for the user to configure a theme more easily,
without having to write a custom stylesheet, it is necessary to be able to
--
cgit v1.2.1
From 07098c9f40bde13fb724a4ac0d44d10b6d58a379 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 15 Feb 2009 12:35:05 +0100
Subject: Add language-binding.net.
---
EXAMPLES | 1 +
1 file changed, 1 insertion(+)
diff --git a/EXAMPLES b/EXAMPLES
index 87c69173..32c4b54e 100644
--- a/EXAMPLES
+++ b/EXAMPLES
@@ -12,6 +12,7 @@ included, please mail to `the Google group
* Chaco: http://code.enthought.com/projects/chaco/docs/html/
* CodePy: http://documen.tician.de/codepy/
* Cython: http://docs.cython.org/
+* C\\C++ Python language binding project: http://language-binding.net/index.html
* Director: http://packages.python.org/director/
* Django: http://docs.djangoproject.com/
* F2py: http://www.f2py.org/html/
--
cgit v1.2.1
From 6d60b52591dd394484769c37879a21e7a3fd1fbd Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 15 Feb 2009 12:44:10 +0100
Subject: Fix test suite.
---
tests/test_config.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/test_config.py b/tests/test_config.py
index 6343a364..b3aa4eea 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -36,7 +36,7 @@ def test_core_config(app):
# complex default values
assert 'html_title' not in cfg.__dict__
- assert cfg.html_title == 'Sphinx v0.4alpha1 documentation'
+ assert cfg.html_title == 'Sphinx v0.6alpha1 documentation'
# complex default values mustn't raise
for valuename in cfg.config_values:
--
cgit v1.2.1
From 34fcbfcd18e95bd948f971d977166b9e1f8863b0 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 15 Feb 2009 16:10:10 +0100
Subject: Add theming test suite, fix a few bugs and doc problems.
---
doc/theming.rst | 11 +++--
sphinx/config.py | 1 -
sphinx/jinja2glue.py | 5 ++-
sphinx/theming.py | 2 +-
tests/root/_templates/layout.html | 1 +
tests/root/conf.py | 4 ++
tests/root/rimg.png | Bin 67861 -> 218 bytes
tests/root/testtheme/layout.html | 5 +++
tests/root/testtheme/static/staticimg.png | Bin 0 -> 218 bytes
tests/root/testtheme/static/statictmpl.html_t | 2 +
tests/root/testtheme/theme.conf | 7 ++++
tests/root/ziptheme.zip | Bin 0 -> 1039 bytes
tests/test_build.py | 6 +++
tests/test_theming.py | 56 ++++++++++++++++++++++++++
14 files changed, 92 insertions(+), 8 deletions(-)
create mode 100644 tests/root/testtheme/layout.html
create mode 100644 tests/root/testtheme/static/staticimg.png
create mode 100644 tests/root/testtheme/static/statictmpl.html_t
create mode 100644 tests/root/testtheme/theme.conf
create mode 100644 tests/root/ziptheme.zip
create mode 100644 tests/test_theming.py
diff --git a/doc/theming.rst b/doc/theming.rst
index bc150f4c..b2448198 100644
--- a/doc/theming.rst
+++ b/doc/theming.rst
@@ -97,6 +97,9 @@ Sphinx comes with a selection of themes to choose from:
* **sphinxdoc** -- The theme used for this documentation. It features a sidebar
on the right side. There are currently no options beyond *nosidebar*.
+* **traditional** -- A theme resembling the old Python documentation. There are
+ currently no options beyond *nosidebar*.
+
Creating themes
---------------
@@ -151,10 +154,10 @@ searches for templates:
* Then, in the selected theme.
* Then, in its base theme, its base's base theme, etc.
-From all of these levels, you can inherit templates from the lowernext level by
-prefixing the template name with an exclamation mark in the ``extends`` tag, or
-(in the case of theme templates) giving an explicit path, like
-``basic/layout.html``.
+When extending a template in the base theme with the same name, use the theme
+name as an explicit directory: ``{% extends "basic/layout.html" %}``. From a
+user ``templates_path`` template, you can still use the "exclamation mark"
+syntax as described in the templating document.
Static templates
diff --git a/sphinx/config.py b/sphinx/config.py
index 79f3ce8a..474ddc23 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -133,7 +133,6 @@ class Config(object):
config.setdefault(realvalname, {})[key] = value
else:
config[valname] = value
- config.update(self.overrides)
for name in config:
if name in self.values:
self.__dict__[name] = config[name]
diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py
index b7f99e6f..d7ad328d 100644
--- a/sphinx/jinja2glue.py
+++ b/sphinx/jinja2glue.py
@@ -40,6 +40,7 @@ class BuiltinTemplateLoader(TemplateBridge, jinja2.BaseLoader):
chain.extend(self.theme.themepath)
# prepend explicit template paths
+ self.templatepathlen = len(builder.config.templates_path)
if builder.config.templates_path:
chain[0:0] = [path.join(builder.confdir, tp)
for tp in builder.config.templates_path]
@@ -71,9 +72,9 @@ class BuiltinTemplateLoader(TemplateBridge, jinja2.BaseLoader):
def get_source(self, environment, template):
loaders = self.loaders
- # exclamation mark starts search from base
+ # exclamation mark starts search from theme
if template.startswith('!'):
- loaders = loaders[1:]
+ loaders = loaders[self.templatepathlen:]
template = template[1:]
for loader in loaders:
try:
diff --git a/sphinx/theming.py b/sphinx/theming.py
index c6e4f4ce..d9fde636 100644
--- a/sphinx/theming.py
+++ b/sphinx/theming.py
@@ -110,7 +110,7 @@ class Theme(object):
return self.themeconf.get(section, name)
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
if self.base is not None:
- return self.base.get_confstr(section, name)
+ return self.base.get_confstr(section, name, default)
if default is NODEFAULT:
raise ThemeError('setting %s.%s occurs in none of the '
'searched theme configs' % (section, name))
diff --git a/tests/root/_templates/layout.html b/tests/root/_templates/layout.html
index 1f4688e6..e8920025 100644
--- a/tests/root/_templates/layout.html
+++ b/tests/root/_templates/layout.html
@@ -1,4 +1,5 @@
{% extends "!layout.html" %}
{% block extrahead %}
+{{ super() }}
{% endblock %}
diff --git a/tests/root/conf.py b/tests/root/conf.py
index 1a169f30..ace6ac6c 100644
--- a/tests/root/conf.py
+++ b/tests/root/conf.py
@@ -26,6 +26,10 @@ pygments_style = 'sphinx'
rst_epilog = '.. |subst| replace:: global substitution'
+html_theme = 'testtheme'
+html_theme_path = ['.']
+html_theme_options = {'testopt': 'testoverride'}
+
html_style = 'default.css'
html_static_path = ['_static']
html_last_updated_fmt = '%b %d, %Y'
diff --git a/tests/root/rimg.png b/tests/root/rimg.png
index 72c12d13..1081dc14 100644
Binary files a/tests/root/rimg.png and b/tests/root/rimg.png differ
diff --git a/tests/root/testtheme/layout.html b/tests/root/testtheme/layout.html
new file mode 100644
index 00000000..81372be0
--- /dev/null
+++ b/tests/root/testtheme/layout.html
@@ -0,0 +1,5 @@
+{% extends "basic/layout.html" %}
+{% block extrahead %}
+
+{{ super() }}
+{% endblock %}
diff --git a/tests/root/testtheme/static/staticimg.png b/tests/root/testtheme/static/staticimg.png
new file mode 100644
index 00000000..1081dc14
Binary files /dev/null and b/tests/root/testtheme/static/staticimg.png differ
diff --git a/tests/root/testtheme/static/statictmpl.html_t b/tests/root/testtheme/static/statictmpl.html_t
new file mode 100644
index 00000000..4ab292b4
--- /dev/null
+++ b/tests/root/testtheme/static/statictmpl.html_t
@@ -0,0 +1,2 @@
+
+{{ project|e }}
diff --git a/tests/root/testtheme/theme.conf b/tests/root/testtheme/theme.conf
new file mode 100644
index 00000000..a8776737
--- /dev/null
+++ b/tests/root/testtheme/theme.conf
@@ -0,0 +1,7 @@
+[theme]
+inherit = basic
+stylesheet = default.css
+pygments_style = emacs
+
+[options]
+testopt = optdefault
diff --git a/tests/root/ziptheme.zip b/tests/root/ziptheme.zip
new file mode 100644
index 00000000..8a246ed9
Binary files /dev/null and b/tests/root/ziptheme.zip differ
diff --git a/tests/test_build.py b/tests/test_build.py
index 207ae280..a8934b82 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -89,6 +89,7 @@ HTML_XPATH = {
},
'contents.html': {
".//meta[@name='hc'][@content='hcval']": '',
+ ".//meta[@name='testopt'][@content='testoverride']": '',
".//td[@class='label']": r'\[Ref1\]',
".//li[@class='toctree-l1']/a": 'Testing various markup',
".//li[@class='toctree-l2']/a": 'Admonitions',
@@ -96,6 +97,9 @@ HTML_XPATH = {
".//div[@class='footer']": 'Georg Brandl & Team',
".//a[@href='http://python.org/']": '',
},
+ '_static/statictmpl.html': {
+ ".//project": 'Sphinx ',
+ },
}
if pygments:
@@ -141,6 +145,8 @@ def test_html(app):
etree = ET.parse(os.path.join(app.outdir, fname), parser)
for path, check in paths.iteritems():
nodes = list(etree.findall(path))
+ if not nodes:
+ import pdb; pdb.set_trace()
assert nodes != []
if hasattr(check, '__call__'):
check(nodes)
diff --git a/tests/test_theming.py b/tests/test_theming.py
new file mode 100644
index 00000000..349a9ce4
--- /dev/null
+++ b/tests/test_theming.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+"""
+ test_theming
+ ~~~~~~~~~~~~
+
+ Test the Theme class.
+
+ :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import os
+import zipfile
+
+from util import *
+
+from sphinx.theming import Theme, ThemeError
+
+
+@with_app(confoverrides={'html_theme': 'ziptheme',
+ 'html_theme_options.testopt': 'foo'})
+def test_theme_api(app):
+ cfg = app.config
+
+ # test Theme class API
+ assert set(Theme.themes.keys()) == \
+ set(['basic', 'default', 'sphinxdoc', 'traditional',
+ 'testtheme', 'ziptheme'])
+ assert Theme.themes['testtheme'][1] is None
+ assert isinstance(Theme.themes['ziptheme'][1], zipfile.ZipFile)
+
+ # test Theme instance API
+ theme = app.builder.theme
+ assert theme.name == 'ziptheme'
+ assert theme.themedir_created
+ themedir = theme.themedir
+ assert theme.base.name == 'basic'
+ assert len(theme.get_dirchain()) == 2
+
+ # direct setting
+ assert theme.get_confstr('theme', 'stylesheet') == 'custom.css'
+ # inherited setting
+ assert theme.get_confstr('options', 'nosidebar') == 'false'
+ # nonexisting setting
+ assert theme.get_confstr('theme', 'foobar', 'def') == 'def'
+ raises(ThemeError, theme.get_confstr, 'theme', 'foobar')
+
+ # options API
+ raises(ThemeError, theme.get_options, {'nonexisting': 'foo'})
+ options = theme.get_options(cfg.html_theme_options)
+ assert options['testopt'] == 'foo'
+ assert options['nosidebar'] == 'false'
+
+ # cleanup temp directories
+ theme.cleanup()
+ assert not os.path.exists(themedir)
--
cgit v1.2.1
From a7a6b7a145648978ca914a24c2e0477b2c9b0e0f Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 15 Feb 2009 18:52:28 +0100
Subject: Fix #93: note that show-inheritance works with automodule.
---
doc/ext/autodoc.rst | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst
index ccaf7ca8..00fafdb7 100644
--- a/doc/ext/autodoc.rst
+++ b/doc/ext/autodoc.rst
@@ -115,9 +115,11 @@ directive.
.. versionadded:: 0.4
- * The :dir:`autoclass` and :dir:`autoexception` directives also support a
- flag option called ``show-inheritance``. When given, a list of base
- classes will be inserted just below the class signature.
+ * The :dir:`automodule`, :dir:`autoclass` and :dir:`autoexception` directives
+ also support a flag option called ``show-inheritance``. When given, a list
+ of base classes will be inserted just below the class signature (when used
+ with :dir:`automodule`, this will be inserted for every class that is
+ documented in the module).
.. versionadded:: 0.4
--
cgit v1.2.1
From c9b7d87c4b5bbca5df288f59873c959bf60716f7 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sun, 15 Feb 2009 20:08:31 +0100
Subject: Fix target.
---
doc/markup/misc.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/markup/misc.rst b/doc/markup/misc.rst
index 03fbdeea..5bff00dc 100644
--- a/doc/markup/misc.rst
+++ b/doc/markup/misc.rst
@@ -3,7 +3,7 @@
Miscellaneous markup
====================
-.. _metadata::
+.. _metadata:
File-wide metadata
------------------
--
cgit v1.2.1
From fe73fee37cbf5d9fa9992160b546715247aa403e Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Mon, 16 Feb 2009 00:02:36 +0100
Subject: Add Sprox.
---
EXAMPLES | 1 +
1 file changed, 1 insertion(+)
diff --git a/EXAMPLES b/EXAMPLES
index 32c4b54e..0562a7f2 100644
--- a/EXAMPLES
+++ b/EXAMPLES
@@ -53,6 +53,7 @@ included, please mail to `the Google group
* Self: http://selflanguage.org/
* SimPy: http://simpy.sourceforge.net/
* Sphinx: http://sphinx.pocoo.org/
+* Sprox: http://sprox.org/
* SQLAlchemy: http://www.sqlalchemy.org/docs/
* Sqlkit: http://sqlkit.argolinux.org/
* SymPy: http://docs.sympy.org/
--
cgit v1.2.1
From 70b27cb2c654f41f5ab56fe46ffb79c15eaf5b3d Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Mon, 16 Feb 2009 00:08:08 +0100
Subject: Change html_collapse_toctree setting to an argument of the toctree()
callable.
---
CHANGES | 7 +++----
doc/config.rst | 8 --------
doc/templating.rst | 3 ++-
sphinx/builders/html.py | 10 +++++-----
sphinx/config.py | 1 -
5 files changed, 10 insertions(+), 19 deletions(-)
diff --git a/CHANGES b/CHANGES
index 2d4342bc..80a27910 100644
--- a/CHANGES
+++ b/CHANGES
@@ -55,7 +55,9 @@ New features added
and ``staticmethod``.
- Added a ``toctree`` callable to the templates, and the ability
- to include external links in toctrees.
+ to include external links in toctrees. The 'collapse' keyword argument
+ indicates whether or not to only display subitems of the current page.
+ (Defaults to True.)
* Configuration:
@@ -70,9 +72,6 @@ New features added
- The new ``html_show_sourcelink`` config value can be used to
switch off the links to the reST sources in the sidebar.
- - The new ``html_collapse_toctree`` config value can be used to
- "collapse" the generated toctree given to the templates.
-
- The default value for ``htmlhelp_basename`` is now the project
title, cleaned up as a filename.
diff --git a/doc/config.rst b/doc/config.rst
index efa1fa85..2c9e09d0 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -515,14 +515,6 @@ that use Sphinx' HTMLWriter class.
.. versionadded:: 0.4
-.. confval:: html_collapse_toctree
-
- If true, the toctree given to the templates as ``toctree`` will be collapsed,
- i.e. only the subitems that contain the current page are visible. Default is
- ``False``.
-
- .. versionadded:: 0.6
-
.. confval:: htmlhelp_basename
Output file base name for HTML help builder. Default is ``'pydoc'``.
diff --git a/doc/templating.rst b/doc/templating.rst
index 2ee449aa..bccdd689 100644
--- a/doc/templating.rst
+++ b/doc/templating.rst
@@ -364,4 +364,5 @@ are in HTML form), these variables are also available:
.. data:: toctree
A callable yielding the global TOC tree containing the current page, rendered
- as HTML bullet lists.
+ as HTML bullet lists. If the optional keyword argument ``collapse`` is true,
+ all TOC entries that are not ancestors of the current page are collapsed.
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 3332aa14..1bbf359f 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -184,10 +184,6 @@ class StandaloneHTMLBuilder(Builder):
self.theme.get_options(self.config.html_theme_options).iteritems())
self.globalcontext.update(self.config.html_context)
- def _get_local_toctree(self, docname):
- return self.render_partial(self.env.get_toctree_for(
- docname, self, self.config.html_collapse_toctree))['fragment']
-
def get_doc_context(self, docname, body, metatags):
"""Collect items for the template context of a page."""
# find out relations
@@ -535,6 +531,10 @@ class StandaloneHTMLBuilder(Builder):
if self.indexer is not None and title:
self.indexer.feed(pagename, title, doctree)
+ def _get_local_toctree(self, docname, collapse=True):
+ return self.render_partial(self.env.get_toctree_for(
+ docname, self, collapse))['fragment']
+
# --------- these are overwritten by the serialization builder
def get_target_uri(self, docname, typ=None):
@@ -554,7 +554,7 @@ class StandaloneHTMLBuilder(Builder):
ctx['pathto'] = pathto
ctx['hasdoc'] = lambda name: name in self.env.all_docs
ctx['customsidebar'] = self.config.html_sidebars.get(pagename)
- ctx['toctree'] = lambda: self._get_local_toctree(pagename)
+ ctx['toctree'] = lambda **kw: self._get_local_toctree(pagename, **kw)
ctx.update(addctx)
self.app.emit('html-page-context', pagename, templatename,
diff --git a/sphinx/config.py b/sphinx/config.py
index 474ddc23..d5bb6471 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -72,7 +72,6 @@ class Config(object):
html_use_smartypants = (True, False),
html_translator_class = (None, False),
html_sidebars = ({}, False),
- html_collapse_toctree = (False, False),
html_additional_pages = ({}, False),
html_use_modindex = (True, False),
html_add_permalinks = (True, False),
--
cgit v1.2.1
From fd55c93fdd30b99b1bd220993997b747efe90174 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Mon, 16 Feb 2009 09:06:54 +0100
Subject: Fix templating inconsistency.
---
sphinx/themes/basic/layout.html | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html
index 53b9b8b1..7a2d5859 100644
--- a/sphinx/themes/basic/layout.html
+++ b/sphinx/themes/basic/layout.html
@@ -152,8 +152,8 @@
{%- block sidebar1 %} {# possible location for sidebar #} {% endblock %}
-{%- block document %}
+{%- block document %}
{%- if not embedded %}{% if not theme_nosidebar|tobool %}
-{%- endblock %}
{%- block relbar2 %}{{ relbar() }}{% endblock %}
--
cgit v1.2.1
From 4397c3f6b8247bf969056fd35777ef71aec55dd1 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Mon, 16 Feb 2009 09:13:49 +0100
Subject: Note incompatibility about the document block.
---
CHANGES | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/CHANGES b/CHANGES
index 80a27910..cfb56130 100644
--- a/CHANGES
+++ b/CHANGES
@@ -11,6 +11,11 @@ New features added
is largely the same, very few fixes should be necessary in
custom templates.
+ - The "document" div tag has been moved out of the ``layout.html``
+ template's "document" block, because the closing tag was already
+ outside. If you overwrite this block, you need to remove your
+ "document" div tag as well.
+
- The ``autodoc_skip_member`` event now also gets to decide
whether to skip members whose name starts with underscores.
Previously, these members were always automatically skipped.
--
cgit v1.2.1
From fe956651a0b4e346278c5cde98b551a968c1d63c Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Tue, 17 Feb 2009 12:20:09 +0100
Subject: Use INI lexer.
---
doc/theming.rst | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/doc/theming.rst b/doc/theming.rst
index b2448198..7c3ae709 100644
--- a/doc/theming.rst
+++ b/doc/theming.rst
@@ -113,7 +113,9 @@ name), containing the following:
output statid directory on build. These can be images, styles, script files.
The :file:`theme.conf` file is in INI format [1]_ (readable by the standard
-Python :mod:`ConfigParser` module) and has the following structure::
+Python :mod:`ConfigParser` module) and has the following structure:
+
+.. sourcecode:: ini
[theme]
inherit = base theme
--
cgit v1.2.1
From 79c547d3bea993cf3074aba40465c4d6cabf6906 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Tue, 17 Feb 2009 16:36:30 +0100
Subject: Refactor autodoc so that it gets easy to add support for custom types
of objects.
---
sphinx/application.py | 22 +-
sphinx/ext/autodoc.py | 1175 +++++++++++++++++++++++++++++--------------------
sphinx/util/compat.py | 63 ++-
3 files changed, 775 insertions(+), 485 deletions(-)
diff --git a/sphinx/application.py b/sphinx/application.py
index 0d87c493..9bb276f1 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -56,6 +56,7 @@ from sphinx.builders import BUILTIN_BUILDERS
from sphinx.directives import desc_directive, target_directive, \
additional_xref_types
from sphinx.environment import SphinxStandaloneReader
+from sphinx.util.compat import Directive, directive_dwim
from sphinx.util.console import bold
@@ -282,11 +283,17 @@ class Sphinx(object):
if depart:
setattr(translator, 'depart_'+node.__name__, depart)
- def add_directive(self, name, func, content, arguments, **options):
- func.content = content
- func.arguments = arguments
- func.options = options
- directives.register_directive(name, func)
+ def add_directive(self, name, obj, content=None, arguments=None, **options):
+ if isinstance(obj, Directive):
+ if content or arguments or options:
+ raise ExtensionError('when adding directive classes, no '
+ 'additional arguments may be given')
+ directives.register_directive(name, directive_dwim(obj))
+ else:
+ obj.content = content
+ obj.arguments = arguments
+ obj.options = options
+ directives.register_directive(name, obj)
def add_role(self, name, role):
roles.register_canonical_role(name, role)
@@ -325,6 +332,11 @@ class Sphinx(object):
return
lexers[alias] = lexer
+ def add_autodocumenter(self, cls):
+ from sphinx.ext import autodoc
+ autodoc.add_documenter(cls)
+ self.add_directive('auto' + cls.objtype, autodoc.AutoDirective)
+
class TemplateBridge(object):
"""
diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py
index 662682f3..b8e048e8 100644
--- a/sphinx/ext/autodoc.py
+++ b/sphinx/ext/autodoc.py
@@ -14,18 +14,20 @@
import re
import sys
import inspect
-import linecache
-from types import FunctionType, BuiltinFunctionType, MethodType, ClassType
+from types import ModuleType, FunctionType, BuiltinFunctionType, MethodType, \
+ ClassType
from docutils import nodes
-from docutils.parsers.rst import directives
+from docutils.utils import assemble_option_dict
from docutils.statemachine import ViewList
from sphinx.util import rpartition, nested_parse_with_titles, force_decode
from sphinx.pycode import ModuleAnalyzer, PycodeError
+from sphinx.application import ExtensionError
+from sphinx.util.compat import Directive
from sphinx.util.docstrings import prepare_docstring
-clstypes = (type, ClassType)
+
try:
base_exception = BaseException
except NameError:
@@ -42,22 +44,39 @@ py_ext_sig_re = re.compile(
''', re.VERBOSE)
-class Options(object):
- pass
+class DefDict(dict):
+ def __init__(self, default):
+ dict.__init__(self)
+ self.default = default
+ def __getitem__(self, key):
+ try:
+ return dict.__getitem__(self, key)
+ except KeyError:
+ return self.default
+ def __nonzero__(self):
+ # docutils check "if option_spec"
+ return True
+identity = lambda x: x
-def get_method_type(obj):
- """
- Return the method type for an object: method, staticmethod or classmethod.
- """
- if isinstance(obj, classmethod) or \
- (isinstance(obj, MethodType) and obj.im_self is not None):
- return 'classmethod'
- elif isinstance(obj, FunctionType) or \
- (isinstance(obj, BuiltinFunctionType) and obj.__self__ is not None):
- return 'staticmethod'
- else:
- return 'method'
+
+class Options(dict):
+ def __getattr__(self, name):
+ try:
+ return self[name.replace('_', '-')]
+ except KeyError:
+ return False
+
+
+ALL = object()
+
+def members_option(arg):
+ if arg is None:
+ return ALL
+ return [x.strip() for x in arg.split(',')]
+
+def bool_option(arg):
+ return True
class AutodocReporter(object):
@@ -171,229 +190,327 @@ def isdescriptor(x):
return False
-class RstGenerator(object):
- def __init__(self, options, document, lineno):
- self.options = options
- self.env = document.settings.env
- self.reporter = document.reporter
- self.lineno = lineno
- self.filename_set = set()
- self.warnings = []
- self.result = ViewList()
-
- def warn(self, msg):
- self.warnings.append(self.reporter.warning(msg, line=self.lineno))
-
- def get_doc(self, what, obj, encoding=None):
- """Decode and return lines of the docstring(s) for the object."""
- docstrings = []
-
- # add the regular docstring if present
- if getattr(obj, '__doc__', None):
- docstrings.append(obj.__doc__)
-
- # skip some lines in module docstrings if configured (deprecated!)
- if what == 'module' and self.env.config.automodule_skip_lines \
- and docstrings:
- docstrings[0] = '\n'.join(docstrings[0].splitlines()
- [self.env.config.automodule_skip_lines:])
-
- # for classes, what the "docstring" is can be controlled via an option
- if what in ('class', 'exception'):
- content = self.env.config.autoclass_content
- if content in ('both', 'init'):
- initdocstring = getattr(obj, '__init__', None).__doc__
- # for new-style classes, no __init__ means default __init__
- if initdocstring == object.__init__.__doc__:
- initdocstring = None
- if initdocstring:
- if content == 'init':
- docstrings = [initdocstring]
- else:
- docstrings.append(initdocstring)
- # the default is only the class docstring
-
- # make sure we have Unicode docstrings, then sanitize and split
- # into lines
- return [prepare_docstring(force_decode(docstring, encoding))
- for docstring in docstrings]
-
- def process_doc(self, docstrings, what, name, obj):
- """Let the user process the docstrings."""
- for docstringlines in docstrings:
- if self.env.app:
- # let extensions preprocess docstrings
- self.env.app.emit('autodoc-process-docstring',
- what, name, obj, self.options, docstringlines)
- for line in docstringlines:
- yield line
-
- def resolve_name(self, what, name):
+class Documenter(object):
+ # name by which the directive is called (auto...) and the default
+ # generated directive name
+ objtype = 'object'
+ # indentation by which to indent the directive content
+ content_indent = u' '
+ # priority if multiple documenters return True from can_document_member
+ priority = 0
+
+ option_spec = {'noindex': bool_option}
+
+ special_attrgetters = {}
+
+ @classmethod
+ def get_attr(cls, obj, name, *defargs):
+ """getattr() override for types such as Zope interfaces."""
+ for typ, func in cls.special_attrgetters.iteritems():
+ if isinstance(obj, typ):
+ return func(obj, name, *defargs)
+ return getattr(obj, name, *defargs)
+
+ @classmethod
+ def can_document_member(cls, member, membername, isattr, parent):
+ """Called to see if a member can be documented by this documenter."""
+ raise NotImplementedError('must be implemented in subclasses')
+
+ def __init__(self, directive, name, indent=u''):
+ self.directive = directive
+ self.env = directive.env
+ self.options = directive.genopt
+ self.name = name
+ self.indent = indent
+ # the module and object path within the module, and the fully
+ # qualified name (all set after resolve_name succeeds)
+ self.modname = None
+ self.module = None
+ self.objpath = None
+ self.fullname = None
+ # extra signature items (arguments and return annotation,
+ # also set after resolve_name succeeds)
+ self.args = None
+ self.retann = None
+ # the object to document (set after import_object succeeds)
+ self.object = None
+ # the module analyzer to get at attribute docs, or None
+ self.analyzer = None
+
+ def add_line(self, line, source, *lineno):
+ self.directive.result.append(self.indent + line, source, *lineno)
+
+ def resolve_name(self, modname, parents, path, base):
+ raise NotImplementedError('must be implemented in subclasses')
+
+ def parse_name(self):
"""
Determine what module to import and what attribute to document.
- Returns a tuple of: the full name, the module name, a path of
- names to get via getattr, the signature and return annotation.
+ Returns True if parsing and resolving was successful.
"""
# first, parse the definition -- auto directives for classes and
# functions can contain a signature which is then used instead of
# an autogenerated one
try:
- mod, path, base, args, retann = py_ext_sig_re.match(name).groups()
- except:
- self.warn('invalid signature for auto%s (%r)' % (what, name))
- return None, [], None, None
+ explicit_modname, path, base, args, retann = \
+ py_ext_sig_re.match(self.name).groups()
+ except AttributeError:
+ self.directive.warn('invalid signature for auto%s (%r)' %
+ (self.objtype, self.name))
+ return False
# support explicit module and class name separation via ::
- if mod is not None:
- mod = mod[:-2]
+ if explicit_modname is not None:
+ modname = explicit_modname[:-2]
parents = path and path.rstrip('.').split('.') or []
else:
+ modname = None
parents = []
- if what == 'module':
- if mod is not None:
- self.warn('"::" in automodule name doesn\'t make sense')
- if args or retann:
- self.warn('ignoring signature arguments and return annotation '
- 'for automodule %s' % name)
- return (path or '') + base, [], None, None
-
- elif what in ('exception', 'function', 'class', 'data'):
- if mod is None:
- if path:
- mod = path.rstrip('.')
- else:
- # if documenting a toplevel object without explicit module,
- # it can be contained in another auto directive ...
- if hasattr(self.env, 'autodoc_current_module'):
- mod = self.env.autodoc_current_module
- # ... or in the scope of a module directive
- if not mod:
- mod = self.env.currmodule
- return mod, parents + [base], args, retann
+ self.modname, self.objpath = \
+ self.resolve_name(modname, parents, path, base)
- else:
- if mod is None:
- if path:
- mod_cls = path.rstrip('.')
- else:
- mod_cls = None
- # if documenting a class-level object without path,
- # there must be a current class, either from a parent
- # auto directive ...
- if hasattr(self.env, 'autodoc_current_class'):
- mod_cls = self.env.autodoc_current_class
- # ... or from a class directive
- if mod_cls is None:
- mod_cls = self.env.currclass
- # ... if still None, there's no way to know
- if mod_cls is None:
- return None, [], None, None
- mod, cls = rpartition(mod_cls, '.')
- parents = [cls]
- # if the module name is still missing, get it like above
- if not mod and hasattr(self.env, 'autodoc_current_module'):
- mod = self.env.autodoc_current_module
- if not mod:
- mod = self.env.currmodule
- return mod, parents + [base], args, retann
-
- def format_signature(self, what, name, obj, args, retann):
- """
- Return the signature of the object, formatted for display.
- """
- if what not in ('class', 'method', 'staticmethod', 'classmethod',
- 'function'):
- return ''
+ self.args = args
+ self.retann = retann
+ self.fullname = self.modname + (self.objpath and
+ '.' + '.'.join(self.objpath) or '')
+ return True
+ def import_object(self):
+ try:
+ __import__(self.modname)
+ obj = self.module = sys.modules[self.modname]
+ for part in self.objpath:
+ obj = self.get_attr(obj, part)
+ self.object = obj
+ return True
+ except (ImportError, AttributeError), err:
+ self.directive.warn(
+ 'autodoc can\'t import/find %s %r, it reported error: '
+ '"%s", please check your spelling and sys.path' %
+ (self.objtype, str(self.fullname), err))
+ return False
+
+ def get_real_modname(self):
+ return self.get_attr(self.object, '__module__', None) or self.modname
+
+ def check_module(self):
+ modname = self.get_attr(self.object, '__module__', None)
+ if modname and modname != self.modname:
+ return False
+ return True
+
+ def format_args(self):
+ return None
+
+ def format_signature(self):
err = None
- if args is not None:
+ if self.args is not None:
# signature given explicitly
- args = "(%s)" % args
+ args = "(%s)" % self.args
else:
# try to introspect the signature
try:
- args = None
- getargs = True
- if what == 'class':
- # for classes, the relevant signature is the
- # __init__ method's
- obj = getattr(obj, '__init__', None)
- # classes without __init__ method, default __init__ or
- # __init__ written in C?
- if obj is None or obj is object.__init__ or not \
- (inspect.ismethod(obj) or inspect.isfunction(obj)):
- getargs = False
- elif inspect.isbuiltin(obj) or inspect.ismethoddescriptor(obj):
- # can never get arguments of a C function or method
- getargs = False
- if getargs:
- try:
- argspec = inspect.getargspec(obj)
- except TypeError:
- # if a class should be documented as function (yay duck
- # typing) we try to use the constructor signature as function
- # signature without the first argument.
- try:
- argspec = inspect.getargspec(obj.__new__)
- except TypeError:
- argspec = inspect.getargspec(obj.__init__)
- if argspec[0]:
- del argspec[0][0]
- if what in ('class', 'method', 'staticmethod',
- 'classmethod') and argspec[0] and \
- argspec[0][0] in ('cls', 'self'):
- del argspec[0][0]
- args = inspect.formatargspec(*argspec)
+ args = self.format_args()
except Exception, e:
args = None
err = e
+ if args is None:
+ return ''
+ retann = self.retann
result = self.env.app.emit_firstresult(
- 'autodoc-process-signature', what, name, obj,
- self.options, args, retann)
+ 'autodoc-process-signature', self.objtype, self.fullname,
+ self.object, self.options, args, retann)
if result:
args, retann = result
if args is not None:
- return '%s%s' % (args, retann and (' -> %s' % retann) or '')
+ return args + (retann and (' -> %s' % retann) or '')
elif err:
# re-raise the error for perusal of the handler in generate()
raise RuntimeError(err)
else:
return ''
- def generate(self, what, name, members, add_content, indent=u'',
- check_module=False, no_docstring=False, real_module=None):
- """
- Generate reST for the object in self.result.
- """
- mod, objpath, args, retann = self.resolve_name(what, name)
- if not mod:
+ def add_directive_header(self, sig):
+ directive = getattr(self, 'directivetype', self.objtype)
+ # the name to put into the generated directive -- doesn't contain
+ # the module (except for module directive of course)
+ name_in_directive = '.'.join(self.objpath) or self.modname
+ self.add_line(u'.. %s:: %s%s' % (directive, name_in_directive, sig),
+ '')
+ if self.options.noindex:
+ self.add_line(u' :noindex:', '')
+ if self.objpath:
+ # Be explicit about the module, this is necessary since .. class::
+ # etc. don't support a prepended module name
+ self.add_line(u' :module: %s' % self.modname, '')
+
+ def get_doc(self, encoding=None):
+ """Decode and return lines of the docstring(s) for the object."""
+ docstring = self.get_attr(self.object, '__doc__', None)
+ if docstring:
+ # make sure we have Unicode docstrings, then sanitize and split
+ # into lines
+ return [prepare_docstring(force_decode(docstring, encoding))]
+ return []
+
+ def process_doc(self, docstrings):
+ """Let the user process the docstrings."""
+ for docstringlines in docstrings:
+ if self.env.app:
+ # let extensions preprocess docstrings
+ self.env.app.emit('autodoc-process-docstring',
+ self.objtype, self.fullname, self.object,
+ self.options, docstringlines)
+ for line in docstringlines:
+ yield line
+
+ def add_content(self, more_content, no_docstring=False):
+ # set sourcename and add content from attribute documentation
+ if self.analyzer:
+ # prevent encoding errors when the file name is non-ASCII
+ filename = unicode(self.analyzer.srcname,
+ sys.getfilesystemencoding(), 'replace')
+ sourcename = u'%s:docstring of %s' % (filename, self.fullname)
+
+ attr_docs = self.analyzer.find_attr_docs()
+ if self.objpath:
+ key = ('.'.join(self.objpath[:-1]), self.objpath[-1])
+ if key in attr_docs:
+ no_docstring = True
+ docstrings = [attr_docs[key]]
+ for i, line in enumerate(self.process_doc(docstrings)):
+ self.add_line(line, sourcename, i)
+ else:
+ sourcename = u'docstring of %s' % self.fullname
+
+ # add content from docstrings
+ if not no_docstring:
+ encoding = self.analyzer and self.analyzer.encoding
+ docstrings = self.get_doc(encoding)
+ for i, line in enumerate(self.process_doc(docstrings)):
+ self.add_line(line, sourcename, i)
+
+ # add additional content (e.g. from document), if present
+ if more_content:
+ for line, src in zip(more_content.data, more_content.items):
+ self.add_line(line, src[0], src[1])
+
+ def get_object_members(self, members=ALL):
+ if members is not ALL:
+ # specific members given
+ ret = []
+ for mname in members:
+ try:
+ ret.append((mname, self.get_attr(self.object, mname)))
+ except AttributeError:
+ self.directive.warn('missing attribute %s in object %s: '
+ % (mname, self.fullname))
+ return False, ret
+ elif self.options.inherited_members:
+ # getmembers() uses dir() which pulls in members from all
+ # base classes
+ return False, inspect.getmembers(self.object)
+ else:
+ # __dict__ contains only the members directly defined in
+ # the class
+ return False, sorted(self.object.__dict__.iteritems())
+
+ def filter_members(self, members, want_all):
+ ret = []
+
+ # search for members in source code too
+ namespace = '.'.join(self.objpath) # will be empty for modules
+
+ if self.analyzer:
+ attr_docs = self.analyzer.find_attr_docs()
+ else:
+ attr_docs = {}
+
+ # process members and determine which to skip
+ for (membername, member) in members:
+ # if isattr is True, the member is documented as an attribute
+ isattr = False
+
+ if want_all and membername.startswith('_'):
+ # ignore members whose name starts with _ by default
+ skip = True
+ elif (namespace, membername) in attr_docs:
+ # keep documented attributes
+ skip = False
+ isattr = True
+ else:
+ # ignore undocumented members if :undoc-members:
+ # is not given
+ doc = self.get_attr(member, '__doc__', None)
+ skip = not self.options.undoc_members and not doc
+
+ # give the user a chance to decide whether this member
+ # should be skipped
+ if self.env.app:
+ # let extensions preprocess docstrings
+ skip_user = self.env.app.emit_firstresult(
+ 'autodoc-skip-member', self.objtype, membername, member,
+ skip, self.options)
+ if skip_user is not None:
+ skip = skip_user
+ if skip:
+ continue
+
+ ret.append((membername, member, isattr))
+
+ return ret
+
+ def document_members(self, all_members=False):
+ # set current namespace for finding members
+ self.env.autodoc_current_module = self.modname
+ if self.objpath:
+ self.env.autodoc_current_class = self.objpath[0]
+
+ want_all = all_members or self.options.inherited_members or \
+ self.options.members is ALL
+ # find out which members are documentable
+ members_check_module, members = self.get_object_members(want_all)
+
+ # document non-skipped members
+ for (mname, member, isattr) in self.filter_members(members, want_all):
+ classes = [cls for cls in AutoDirective._registry.itervalues()
+ if cls.can_document_member(member, mname, isattr, self)]
+ if not classes:
+ # don't know how to document this member
+ continue
+ # prefer the documenter with the highest priority
+ classes.sort(lambda cls: cls.priority)
+ # give explicitly separated module name, so that members
+ # of inner classes can be documented
+ full_mname = self.modname + '::' + \
+ '.'.join(self.objpath + [mname])
+ memberdocmtr = classes[-1](self.directive, full_mname,
+ self.indent)
+ memberdocmtr.generate(all_members=True,
+ real_modname=self.real_modname,
+ check_module=members_check_module)
+
+ # reset current objects
+ self.env.autodoc_current_module = None
+ self.env.autodoc_current_class = None
+
+ def generate(self, more_content=None, real_modname=None,
+ check_module=False, all_members=False):
+ if not self.parse_name():
# need a module to import
- self.warn('don\'t know which module to import for autodocumenting '
- '%r (try placing a "module" or "currentmodule" directive '
- 'in the document, or giving an explicit module name)'
- % name)
+ self.directive.warn(
+ 'don\'t know which module to import for autodocumenting '
+ '%r (try placing a "module" or "currentmodule" directive '
+ 'in the document, or giving an explicit module name)'
+ % self.name)
return
- # fully-qualified name
- fullname = mod + (objpath and '.' + '.'.join(objpath) or '')
-
- # the name to put into the generated directive -- doesn't contain
- # the module
- name_in_directive = '.'.join(objpath) or mod
# now, import the module and get object to document
- try:
- __import__(mod)
- todoc = module = sys.modules[mod]
- for part in objpath:
- todoc = getattr(todoc, part)
- except (ImportError, AttributeError), err:
- self.warn('autodoc can\'t import/find %s %r, it reported error: '
- '"%s", please check your spelling and sys.path' %
- (what, str(fullname), err))
+ if not self.import_object():
return
# If there is no real module defined, figure out which to use.
@@ -401,327 +518,427 @@ class RstGenerator(object):
# where the attribute documentation would actually be found in.
# This is used for situations where you have a module that collects the
# functions and classes of internal submodules.
- if real_module is None:
- real_module = getattr(todoc, '__module__', None) or mod
+ self.real_modname = real_modname or self.get_real_modname()
# try to also get a source code analyzer for attribute docs
try:
- analyzer = ModuleAnalyzer.for_module(real_module)
+ self.analyzer = ModuleAnalyzer.for_module(self.real_modname)
# parse right now, to get PycodeErrors on parsing
- analyzer.parse()
+ self.analyzer.parse()
except PycodeError, err:
# no source file -- e.g. for builtin and C modules
- analyzer = None
+ self.analyzer = None
else:
- self.filename_set.add(analyzer.srcname)
+ self.directive.filename_set.add(self.analyzer.srcname)
- # check __module__ of object for members not given explicitly
+ # check __module__ of object (for members not given explicitly)
if check_module:
- if hasattr(todoc, '__module__'):
- if todoc.__module__ != mod:
- return
+ if not self.check_module():
+ return
# make sure that the result starts with an empty line. This is
# necessary for some situations where another directive preprocesses
# reST and no starting newline is present
- self.result.append(u'', '')
+ self.add_line(u'', '')
# format the object's signature, if any
try:
- sig = self.format_signature(what, fullname, todoc, args, retann)
+ sig = self.format_signature()
except Exception, err:
- self.warn('error while formatting signature for %s: %s' %
- (fullname, err))
+ self.directive.warn('error while formatting signature for '
+ '%s: %s' % (self.fullname, err))
sig = ''
- # now, create the directive header
- if what == 'method':
- directive = get_method_type(todoc)
- else:
- directive = what
- self.result.append(indent + u'.. %s:: %s%s' %
- (directive, name_in_directive, sig), '