mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2026-09-03 20:52:55 -05:00
Further refactorings, add extensibility API.
This commit is contained in:
+50
-23
@@ -9,15 +9,51 @@
|
||||
:license: BSD.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import getopt
|
||||
from os import path
|
||||
from cStringIO import StringIO
|
||||
|
||||
from sphinx.config import Config, ConfigError
|
||||
from sphinx.builder import builders
|
||||
from sphinx.extension import EventManager
|
||||
from sphinx.util.console import nocolor
|
||||
|
||||
__version__ = '$Revision: 5369 $'
|
||||
__version__ = '$Revision: 5369 $'[11:-2]
|
||||
|
||||
|
||||
def init_builder(buildername, srcdirname, outdirname, doctreedir,
|
||||
confoverrides, status, warning=sys.stderr, freshenv=False):
|
||||
# read config
|
||||
config = Config(srcdirname, 'conf.py')
|
||||
if confoverrides:
|
||||
for key, val in confoverrides.items():
|
||||
setattr(config, key, val)
|
||||
|
||||
# extensibility
|
||||
events = EventManager()
|
||||
for extension in config.extensions:
|
||||
try:
|
||||
mod = __import__(extension, None, None, ['setup'])
|
||||
except ImportError, err:
|
||||
raise ConfigError('Could not import extension %s' % module, err)
|
||||
if hasattr(mod, 'setup'):
|
||||
mod.setup(events, builders)
|
||||
|
||||
if buildername not in builders:
|
||||
print >>warning, 'Builder name %s not registered' % buildername
|
||||
return None
|
||||
|
||||
if buildername is None:
|
||||
print >>status, 'No builder selected, using default: html'
|
||||
buildername = 'html'
|
||||
|
||||
builderclass = builders[buildername]
|
||||
builder = builderclass(srcdirname, outdirname, doctreedir,
|
||||
status_stream=status, warning_stream=warning,
|
||||
events=events, config=config, freshenv=freshenv)
|
||||
events.emit('builder-created', builder)
|
||||
|
||||
|
||||
def usage(argv, msg=None):
|
||||
@@ -42,6 +78,10 @@ modi:
|
||||
|
||||
|
||||
def main(argv):
|
||||
if not sys.stdout.isatty() or sys.platform == 'win32':
|
||||
# Windows' poor cmd box doesn't understand ANSI sequences
|
||||
nocolor()
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(argv[1:], 'ab:d:D:NEqP')
|
||||
srcdirname = path.abspath(args[0])
|
||||
@@ -68,17 +108,14 @@ def main(argv):
|
||||
if err:
|
||||
return 1
|
||||
|
||||
builder = all_files = None
|
||||
buildername = all_files = None
|
||||
freshenv = use_pdb = False
|
||||
status = sys.stdout
|
||||
confoverrides = {}
|
||||
doctreedir = path.join(outdirname, '.doctrees')
|
||||
for opt, val in opts:
|
||||
if opt == '-b':
|
||||
if val not in builders:
|
||||
usage(argv, 'Invalid builder value specified.')
|
||||
return 1
|
||||
builder = val
|
||||
buildername = val
|
||||
elif opt == '-a':
|
||||
if filenames:
|
||||
usage(argv, 'Cannot combine -a option and filenames.')
|
||||
@@ -101,28 +138,18 @@ def main(argv):
|
||||
elif opt == '-P':
|
||||
use_pdb = True
|
||||
|
||||
if not sys.stdout.isatty() or sys.platform == 'win32':
|
||||
# Windows' cmd box doesn't understand ANSI sequences
|
||||
nocolor()
|
||||
|
||||
if builder is None:
|
||||
print >>status, 'No builder selected, using default: html'
|
||||
builder = 'html'
|
||||
|
||||
builderobj = builders[builder]
|
||||
builder = init_builder(buildername, srcdirname, outdirname, doctreedir,
|
||||
confoverrides, status, sys.stderr, freshenv)
|
||||
if not builder:
|
||||
return 1
|
||||
|
||||
try:
|
||||
builderobj = builderobj(srcdirname, outdirname, doctreedir,
|
||||
status_stream=status,
|
||||
warning_stream=sys.stderr,
|
||||
confoverrides=confoverrides,
|
||||
freshenv=freshenv)
|
||||
if all_files:
|
||||
builderobj.build_all()
|
||||
builder.build_all()
|
||||
elif filenames:
|
||||
builderobj.build_specific(filenames)
|
||||
builder.build_specific(filenames)
|
||||
else:
|
||||
builderobj.build_update()
|
||||
builder.build_update()
|
||||
except:
|
||||
if not use_pdb:
|
||||
raise
|
||||
|
||||
+88
-111
@@ -12,7 +12,6 @@
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import types
|
||||
import codecs
|
||||
import shutil
|
||||
import cPickle as pickle
|
||||
@@ -31,8 +30,9 @@ from sphinx import addnodes
|
||||
from sphinx.util import (get_matching_files, attrdict, status_iterator,
|
||||
ensuredir, relative_uri, os_path, SEP)
|
||||
from sphinx.htmlhelp import build_hhx
|
||||
from sphinx.extension import DummyEventManager, import_object
|
||||
from sphinx.patchlevel import get_version_info, get_sys_version_info
|
||||
from sphinx.htmlwriter import HTMLWriter
|
||||
from sphinx.htmlwriter import HTMLWriter, HTMLTranslator, SmartyPantsHTMLTranslator
|
||||
from sphinx.latexwriter import LaTeXWriter
|
||||
from sphinx.environment import BuildEnvironment, NoUri
|
||||
from sphinx.highlighting import pygments, highlight_block, get_stylesheet
|
||||
@@ -45,25 +45,13 @@ from sphinx import directives
|
||||
ENV_PICKLE_FILENAME = 'environment.pickle'
|
||||
LAST_BUILD_FILENAME = 'last_build'
|
||||
|
||||
# Helper objects
|
||||
|
||||
class relpath_to(object):
|
||||
def __init__(self, builder, filename):
|
||||
self.baseuri = builder.get_target_uri(filename)
|
||||
self.builder = builder
|
||||
def __call__(self, otheruri, resource=False):
|
||||
if not resource:
|
||||
otheruri = self.builder.get_target_uri(otheruri + '.rst')
|
||||
return relative_uri(self.baseuri, otheruri)
|
||||
|
||||
|
||||
class Builder(object):
|
||||
"""
|
||||
Builds target formats from the reST sources.
|
||||
"""
|
||||
|
||||
def __init__(self, srcdirname, outdirname, doctreedirname,
|
||||
confoverrides=None, env=None, freshenv=False,
|
||||
config, env=None, freshenv=False, events=None,
|
||||
status_stream=None, warning_stream=None):
|
||||
self.srcdir = srcdirname
|
||||
self.outdir = outdirname
|
||||
@@ -75,35 +63,11 @@ class Builder(object):
|
||||
self.status_stream = status_stream or sys.stdout
|
||||
self.warning_stream = warning_stream or sys.stderr
|
||||
|
||||
# probably set in load_env()
|
||||
self.config = config
|
||||
# if None, this is set in load_env()
|
||||
self.env = env
|
||||
|
||||
self.config = {}
|
||||
olddir = os.getcwd()
|
||||
try:
|
||||
os.chdir(srcdirname)
|
||||
execfile(path.join(srcdirname, 'conf.py'), self.config)
|
||||
finally:
|
||||
os.chdir(olddir)
|
||||
# remove potentially pickling-problematic values
|
||||
del self.config['__builtins__']
|
||||
for key, val in self.config.items():
|
||||
if isinstance(val, types.ModuleType):
|
||||
del self.config[key]
|
||||
if confoverrides:
|
||||
self.config.update(confoverrides)
|
||||
# replace version info if '<auto>'
|
||||
if self.config['version'] == '<auto>' or self.config['release'] == '<auto>':
|
||||
try:
|
||||
version, release = get_version_info(srcdirname)
|
||||
except (IOError, OSError):
|
||||
version, release = get_sys_version_info()
|
||||
self.warn('Can\'t get version info from Include/patchlevel.h, '
|
||||
'using version of this interpreter (%s).' % release)
|
||||
if self.config['version'] == '<auto>':
|
||||
self.config['version'] = version
|
||||
if self.config['release'] == '<auto>':
|
||||
self.config['release'] = release
|
||||
self.events = events or DummyEventManager()
|
||||
|
||||
self.init()
|
||||
|
||||
@@ -124,12 +88,34 @@ class Builder(object):
|
||||
"""Load necessary templates and perform initialization."""
|
||||
raise NotImplementedError
|
||||
|
||||
def init_templates(self):
|
||||
"""Call if you need Jinja templates in the builder."""
|
||||
# lazily import this, maybe other builders won't need it
|
||||
from sphinx._jinja import Environment, SphinxFileSystemLoader
|
||||
|
||||
# load templates
|
||||
self.templates = {}
|
||||
templates_path = [path.join(path.dirname(__file__), 'templates')]
|
||||
templates_path.extend(self.config.templates_path)
|
||||
self.jinja_env = Environment(loader=SphinxFileSystemLoader(templates_path),
|
||||
# disable traceback, more likely that something
|
||||
# in the application is broken than in the templates
|
||||
friendly_traceback=False)
|
||||
|
||||
def get_template(self, name):
|
||||
if name in self.templates:
|
||||
return self.templates[name]
|
||||
template = self.templates[name] = self.jinja_env.get_template(name)
|
||||
return template
|
||||
|
||||
def get_target_uri(self, source_filename, typ=None):
|
||||
"""Return the target URI for a source filename."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_relative_uri(self, from_, to, typ=None):
|
||||
"""Return a relative URI between two source filenames."""
|
||||
"""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))
|
||||
|
||||
@@ -196,7 +182,9 @@ class Builder(object):
|
||||
warnings = []
|
||||
self.env.set_warnfunc(warnings.append)
|
||||
self.msg('reading, updating environment:', nonl=1)
|
||||
iterator = self.env.update(self.config)
|
||||
iterator = self.env.update(
|
||||
self.config,
|
||||
hook=lambda doctree: self.events.emit('doctree-read', doctree))
|
||||
self.msg(iterator.next(), nonl=1, nobold=1)
|
||||
for filename in iterator:
|
||||
if not updated_filenames:
|
||||
@@ -274,25 +262,14 @@ class StandaloneHTMLBuilder(Builder):
|
||||
|
||||
def init(self):
|
||||
"""Load templates."""
|
||||
# lazily import this, maybe other builders won't need it
|
||||
from sphinx._jinja import Environment, SphinxFileSystemLoader
|
||||
|
||||
# load templates
|
||||
self.templates = {}
|
||||
templates_path = path.join(path.dirname(__file__), 'templates')
|
||||
self.jinja_env = Environment(loader=SphinxFileSystemLoader([templates_path]),
|
||||
# disable traceback, more likely that something
|
||||
# in the application is broken than in the templates
|
||||
friendly_traceback=False)
|
||||
# pre-load built-in templates
|
||||
for fname in os.listdir(templates_path):
|
||||
if fname.endswith('.html'):
|
||||
self.templates[fname] = self.jinja_env.get_template(fname)
|
||||
|
||||
def get_template(self, name):
|
||||
if name in self.templates:
|
||||
return self.templates[name]
|
||||
return self.jinja_env.get_template(name)
|
||||
self.init_templates()
|
||||
if self.config.html_translator_class:
|
||||
self.translator_class = 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."""
|
||||
@@ -317,17 +294,17 @@ class StandaloneHTMLBuilder(Builder):
|
||||
|
||||
# format the "last updated on" string, only once is enough since it
|
||||
# typically doesn't include the time of day
|
||||
lufmt = self.config.get('html_last_updated_fmt')
|
||||
lufmt = self.config.html_last_updated_fmt
|
||||
if lufmt:
|
||||
self.last_updated = time.strftime(lufmt)
|
||||
else:
|
||||
self.last_updated = None
|
||||
|
||||
self.globalcontext = dict(
|
||||
project = self.config.get('project', 'Python'),
|
||||
copyright = self.config.get('copyright', ''),
|
||||
release = self.config['release'],
|
||||
version = self.config['version'],
|
||||
project = self.config.project,
|
||||
copyright = self.config.copyright,
|
||||
release = self.config.release,
|
||||
version = self.config.version,
|
||||
last_updated = self.last_updated,
|
||||
builder = self.name,
|
||||
parents = [],
|
||||
@@ -336,6 +313,7 @@ class StandaloneHTMLBuilder(Builder):
|
||||
)
|
||||
|
||||
def write_file(self, filename, doctree):
|
||||
pagename = filename[:-4]
|
||||
destination = StringOutput(encoding='utf-8')
|
||||
doctree.settings = self.docsettings
|
||||
|
||||
@@ -366,7 +344,7 @@ class StandaloneHTMLBuilder(Builder):
|
||||
else:
|
||||
title = ''
|
||||
self.globalcontext['titles'][filename] = title
|
||||
sourcename = filename[:-4] + '.txt'
|
||||
sourcename = pagename + '.txt'
|
||||
context = dict(
|
||||
title = title,
|
||||
sourcename = sourcename,
|
||||
@@ -379,8 +357,8 @@ class StandaloneHTMLBuilder(Builder):
|
||||
next = next,
|
||||
)
|
||||
|
||||
self.index_file(filename, doctree, title)
|
||||
self.handle_page(filename[:-4], context)
|
||||
self.index_page(pagename, doctree, title)
|
||||
self.handle_page(pagename, context)
|
||||
|
||||
def finish(self):
|
||||
self.msg('writing additional files...')
|
||||
@@ -446,12 +424,12 @@ class StandaloneHTMLBuilder(Builder):
|
||||
self.handle_page('search', {}, 'search.html')
|
||||
|
||||
# additional pages from conf.py
|
||||
for pagename, template in self.config.get('html_additional_pages', {}).items():
|
||||
for pagename, template in self.config.html_additional_pages.items():
|
||||
template = path.join(self.srcdir, template)
|
||||
self.handle_page(pagename, {}, template)
|
||||
|
||||
# the index page
|
||||
indextemplate = self.config.get('html_index')
|
||||
indextemplate = self.config.html_index
|
||||
if indextemplate:
|
||||
indextemplate = path.join(self.srcdir, indextemplate)
|
||||
self.handle_page('index', {'indextemplate': indextemplate}, 'index.html')
|
||||
@@ -480,7 +458,7 @@ class StandaloneHTMLBuilder(Builder):
|
||||
|
||||
def get_outdated_files(self):
|
||||
for filename in get_matching_files(
|
||||
self.srcdir, '*.rst', exclude=set(self.config.get('unused_files', ()))):
|
||||
self.srcdir, '*.rst', exclude=set(self.config.unused_files)):
|
||||
try:
|
||||
rstname = path.join(self.outdir, os_path(filename))
|
||||
targetmtime = path.getmtime(rstname[:-4] + '.html')
|
||||
@@ -504,21 +482,26 @@ class StandaloneHTMLBuilder(Builder):
|
||||
# delete all entries for files that will be rebuilt
|
||||
self.indexer.prune([fn[:-4] for fn in set(self.env.all_files) - set(filenames)])
|
||||
|
||||
def index_file(self, filename, doctree, title):
|
||||
def index_page(self, pagename, doctree, title):
|
||||
# only index pages with title
|
||||
if self.indexer is not None and title:
|
||||
self.indexer.feed(self.get_target_uri(filename)[:-5], # strip '.html'
|
||||
title, doctree)
|
||||
self.indexer.feed(pagename, title, doctree)
|
||||
|
||||
def handle_page(self, pagename, context, templatename='page.html'):
|
||||
def handle_page(self, pagename, addctx, templatename='page.html'):
|
||||
ctx = self.globalcontext.copy()
|
||||
ctx['current_page_name'] = pagename
|
||||
ctx['pathto'] = relpath_to(self, self.get_target_uri(pagename+'.rst'))
|
||||
|
||||
def pathto(otheruri, resource=False,
|
||||
baseuri=self.get_target_uri(pagename+'.rst')):
|
||||
if not resource:
|
||||
otheruri = self.get_target_uri(otheruri+'.rst')
|
||||
return relative_uri(baseuri, otheruri)
|
||||
ctx['pathto'] = pathto
|
||||
ctx['hasdoc'] = lambda name: name+'.rst' in self.env.all_files
|
||||
sidebarfile = self.config.get('html_sidebars', {}).get(pagename)
|
||||
sidebarfile = self.config.html_sidebars.get(pagename)
|
||||
if sidebarfile:
|
||||
ctx['customsidebar'] = path.join(self.srcdir, sidebarfile)
|
||||
ctx.update(context)
|
||||
ctx.update(addctx)
|
||||
|
||||
output = self.get_template(templatename).render(ctx)
|
||||
outfilename = path.join(self.outdir, os_path(pagename) + '.html')
|
||||
@@ -531,14 +514,14 @@ class StandaloneHTMLBuilder(Builder):
|
||||
f.close()
|
||||
except (IOError, OSError), err:
|
||||
self.warn("Error writing file %s: %s" % (outfilename, err))
|
||||
if self.copysource and context.get('sourcename'):
|
||||
if self.copysource and ctx.get('sourcename'):
|
||||
# copy the source file for the "show source" link
|
||||
shutil.copyfile(path.join(self.srcdir, os_path(pagename+'.rst')),
|
||||
path.join(self.outdir, os_path(context['sourcename'])))
|
||||
path.join(self.outdir, os_path(ctx['sourcename'])))
|
||||
|
||||
def handle_finish(self):
|
||||
self.msg('dumping search index...')
|
||||
self.indexer.prune([self.get_target_uri(fn)[:-5] for fn in self.env.all_files])
|
||||
self.indexer.prune([fn[:-4] for fn in self.env.all_files])
|
||||
f = open(path.join(self.outdir, 'searchindex.json'), 'w')
|
||||
try:
|
||||
self.indexer.dump(f, 'json')
|
||||
@@ -558,7 +541,7 @@ class WebHTMLBuilder(StandaloneHTMLBuilder):
|
||||
|
||||
def get_outdated_files(self):
|
||||
for filename in get_matching_files(
|
||||
self.srcdir, '*.rst', exclude=set(self.config.get('unused_files', ()))):
|
||||
self.srcdir, '*.rst', exclude=set(self.config.unused_files)):
|
||||
try:
|
||||
targetmtime = path.getmtime(
|
||||
path.join(self.outdir, os_path(filename)[:-4] + '.fpickle'))
|
||||
@@ -587,14 +570,14 @@ class WebHTMLBuilder(StandaloneHTMLBuilder):
|
||||
# delete all entries for files that will be rebuilt
|
||||
self.indexer.prune(set(self.env.all_files) - set(filenames))
|
||||
|
||||
def index_file(self, filename, doctree, title):
|
||||
def index_page(self, pagename, doctree, title):
|
||||
# only index pages with title
|
||||
if self.indexer is not None and title:
|
||||
self.indexer.feed(filename, title, doctree)
|
||||
self.indexer.feed(pagename+'.rst', title, doctree)
|
||||
|
||||
def handle_page(self, pagename, context, templatename='page.html'):
|
||||
context['current_page_name'] = pagename
|
||||
sidebarfile = self.config.get('html_sidebars', {}).get(pagename, '')
|
||||
sidebarfile = self.confightml_sidebars.get(pagename, '')
|
||||
if sidebarfile:
|
||||
context['customsidebar'] = path.join(self.srcdir, sidebarfile)
|
||||
outfilename = path.join(self.outdir, os_path(pagename) + '.fpickle')
|
||||
@@ -654,7 +637,7 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder):
|
||||
copysource = False
|
||||
|
||||
def handle_finish(self):
|
||||
build_hhx(self, self.outdir, self.config.get('htmlhelp_basename', 'pydoc'))
|
||||
build_hhx(self, self.outdir, self.config.htmlhelp_basename)
|
||||
|
||||
|
||||
class LaTeXBuilder(Builder):
|
||||
@@ -665,13 +648,12 @@ class LaTeXBuilder(Builder):
|
||||
|
||||
def init(self):
|
||||
self.filenames = []
|
||||
self.document_data = map(list, self.config.get('latex_documents', ()))
|
||||
self.document_data = map(list, self.config.latex_documents)
|
||||
|
||||
# assign subdirs to titles
|
||||
self.titles = []
|
||||
for entry in self.document_data:
|
||||
# replace version with real version
|
||||
entry[0] = entry[0].replace('<auto>', self.config['version'])
|
||||
sourcename = entry[0]
|
||||
if sourcename.endswith('/index.rst'):
|
||||
sourcename = sourcename[:-9]
|
||||
@@ -693,7 +675,7 @@ class LaTeXBuilder(Builder):
|
||||
def write(self, *ignored):
|
||||
# first, assemble the "appendix" docs that are in every PDF
|
||||
appendices = []
|
||||
for fname in self.config.get('latex_appendices', []):
|
||||
for fname in self.config.latex_appendices:
|
||||
appendices.append(self.env.get_doctree(fname))
|
||||
|
||||
docwriter = LaTeXWriter(self)
|
||||
@@ -780,15 +762,10 @@ class ChangesBuilder(Builder):
|
||||
name = 'changes'
|
||||
|
||||
def init(self):
|
||||
from sphinx._jinja import Environment, FileSystemLoader
|
||||
templates_path = path.join(path.dirname(__file__), 'templates')
|
||||
jinja_env = Environment(loader=SphinxFileSystemLoader([templates_path]),
|
||||
# disable traceback, more likely that something in the
|
||||
# application is broken than in the templates
|
||||
friendly_traceback=False)
|
||||
self.ftemplate = jinja_env.get_template('changes/frameset.html')
|
||||
self.vtemplate = jinja_env.get_template('changes/versionchanges.html')
|
||||
self.stemplate = jinja_env.get_template('changes/rstsource.html')
|
||||
self.init_templates()
|
||||
self.ftemplate = self.get_template('changes/frameset.html')
|
||||
self.vtemplate = self.get_template('changes/versionchanges.html')
|
||||
self.stemplate = self.get_template('changes/rstsource.html')
|
||||
|
||||
def get_outdated_files(self):
|
||||
return self.outdir
|
||||
@@ -800,13 +777,13 @@ class ChangesBuilder(Builder):
|
||||
}
|
||||
|
||||
def write(self, *ignored):
|
||||
ver = self.config['version']
|
||||
version = self.config.version
|
||||
libchanges = {}
|
||||
apichanges = []
|
||||
otherchanges = {}
|
||||
self.msg('writing summary file...')
|
||||
for type, filename, lineno, module, descname, content in \
|
||||
self.env.versionchanges[ver]:
|
||||
self.env.versionchanges[version]:
|
||||
ttext = self.typemap[type]
|
||||
context = content.replace('\n', ' ')
|
||||
if descname and filename.startswith('c-api'):
|
||||
@@ -836,8 +813,8 @@ class ChangesBuilder(Builder):
|
||||
(entry, filename, lineno))
|
||||
|
||||
ctx = {
|
||||
'project': self.config.get('project', 'Python'),
|
||||
'version': ver,
|
||||
'project': self.config.project,
|
||||
'version': version,
|
||||
'libchanges': sorted(libchanges.iteritems()),
|
||||
'apichanges': sorted(apichanges),
|
||||
'otherchanges': sorted(otherchanges.iteritems()),
|
||||
@@ -853,9 +830,9 @@ class ChangesBuilder(Builder):
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
hltext = ['.. versionadded:: %s' % ver,
|
||||
'.. versionchanged:: %s' % ver,
|
||||
'.. deprecated:: %s' % ver]
|
||||
hltext = ['.. versionadded:: %s' % version,
|
||||
'.. versionchanged:: %s' % version,
|
||||
'.. deprecated:: %s' % version]
|
||||
|
||||
def hl(no, line):
|
||||
line = '<a name="L%s"> </a>' % no + escape(line)
|
||||
@@ -881,11 +858,11 @@ class ChangesBuilder(Builder):
|
||||
shutil.copyfile(path.join(path.dirname(__file__), 'style', 'default.css'),
|
||||
path.join(self.outdir, 'default.css'))
|
||||
|
||||
def hl(self, text, ver):
|
||||
def hl(self, text, version):
|
||||
text = escape(text)
|
||||
for directive in ['versionchanged', 'versionadded', 'deprecated']:
|
||||
text = text.replace('.. %s:: %s' % (directive, ver),
|
||||
'<b>.. %s:: %s</b>' % (directive, ver))
|
||||
text = text.replace('.. %s:: %s' % (directive, version),
|
||||
'<b>.. %s:: %s</b>' % (directive, version))
|
||||
return text
|
||||
|
||||
def finish(self):
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinx.config
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Build configuration file handling.
|
||||
|
||||
:copyright: 2008 by Georg Brandl.
|
||||
:license: BSD license.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import types
|
||||
from os import path
|
||||
|
||||
|
||||
class ConfigError(Exception):
|
||||
"""Raised if something's wrong with the configuration."""
|
||||
|
||||
def __init__(self, message, orig_exc=None):
|
||||
self.message = message
|
||||
self.orig_exc = orig_exc
|
||||
|
||||
def __repr__(self):
|
||||
if self.orig_exc:
|
||||
return 'ConfigError(%r, %r)' % (self.message, self.orig_exc)
|
||||
return 'ConfigError(%r)' % self.message
|
||||
|
||||
def __str__(self):
|
||||
if self.orig_exc:
|
||||
return '%s (exception: %s)' % (self.message, self.orig_exc)
|
||||
return self.message
|
||||
|
||||
|
||||
class Config(object):
|
||||
"""Configuration file abstraction."""
|
||||
|
||||
# the values are: (default, needs fresh doctrees if changed)
|
||||
|
||||
config_values = dict(
|
||||
# general substitutions
|
||||
project = ('Python', True),
|
||||
copyright = ('', False),
|
||||
version = ('', True),
|
||||
release = ('', True),
|
||||
today = ('', True),
|
||||
today_fmt = ('%B %d, %Y', True),
|
||||
|
||||
# extensibility
|
||||
templates_path = ([], False),
|
||||
extensions = ([], True),
|
||||
|
||||
# general reading options
|
||||
unused_files = ([], True),
|
||||
refcount_file = ('', True),
|
||||
add_function_parentheses = (True, True),
|
||||
add_module_names = (True, True),
|
||||
|
||||
# HTML options
|
||||
html_last_updated_fmt = ('%b %d, %Y', False),
|
||||
html_use_smartypants = (True, False),
|
||||
html_translator_class = (None, False),
|
||||
html_index = ('', False),
|
||||
html_sidebars = ({}, False),
|
||||
html_additional_pages = ({}, False),
|
||||
|
||||
# HTML help options
|
||||
htmlhelp_basename = ('pydoc', False),
|
||||
|
||||
# LaTeX options
|
||||
latex_paper_size = ('letter', False),
|
||||
latex_font_size = ('10pt', False),
|
||||
latex_documents = ([], False),
|
||||
latex_preamble = ('', False),
|
||||
latex_appendices = ([], False),
|
||||
)
|
||||
|
||||
def __init__(self, dirname, filename):
|
||||
config = {}
|
||||
olddir = os.getcwd()
|
||||
try:
|
||||
os.chdir(dirname)
|
||||
execfile(path.join(dirname, filename), config)
|
||||
finally:
|
||||
os.chdir(olddir)
|
||||
# remove potentially pickling-problematic values
|
||||
for key, val in config.items():
|
||||
if key.startswith('_') or isinstance(val, types.ModuleType):
|
||||
del config[key]
|
||||
self.__dict__.update(config)
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in self.config_values:
|
||||
defval = self.config_values[name][0]
|
||||
setattr(self, name, defval)
|
||||
return defval
|
||||
if name[0:1] == '_':
|
||||
return object.__getattr__(self, name)
|
||||
raise AttributeError('no configuration value named %r' % name)
|
||||
|
||||
def __getitem__(self, name):
|
||||
return getattr(self, name)
|
||||
@@ -147,7 +147,7 @@ def parse_py_signature(signode, sig, desctype, env):
|
||||
signode += addnodes.desc_classname(classname, classname)
|
||||
# exceptions are a special case, since they are documented in the
|
||||
# 'exceptions' module.
|
||||
elif env.config.get('add_module_names', True) and \
|
||||
elif env.config.add_module_names and \
|
||||
env.currmodule and env.currmodule != 'exceptions':
|
||||
nodetext = env.currmodule + '.'
|
||||
signode += addnodes.desc_classname(nodetext, nodetext)
|
||||
|
||||
+34
-26
@@ -22,6 +22,7 @@ try:
|
||||
import hashlib
|
||||
md5 = hashlib.md5
|
||||
except:
|
||||
# 2.4 compatibility
|
||||
import md5
|
||||
md5 = md5.new
|
||||
|
||||
@@ -102,10 +103,10 @@ class DefaultSubstitutions(Transform):
|
||||
for ref in self.document.traverse(nodes.substitution_reference):
|
||||
refname = ref['refname']
|
||||
if refname in to_handle:
|
||||
text = config.get(refname, '')
|
||||
text = config[refname]
|
||||
if refname == 'today' and not text:
|
||||
# special handling: can also specify a strftime format
|
||||
text = time.strftime(config.get('today_fmt', '%B %d, %Y'))
|
||||
text = time.strftime(config.today_fmt)
|
||||
ref.replace_self(nodes.Text(text, text))
|
||||
|
||||
|
||||
@@ -186,7 +187,7 @@ class BuildEnvironment:
|
||||
def __init__(self, srcdir, doctreedir):
|
||||
self.doctreedir = doctreedir
|
||||
self.srcdir = srcdir
|
||||
self.config = {}
|
||||
self.config = None
|
||||
|
||||
# refcount data if present
|
||||
self.refcounts = {}
|
||||
@@ -203,6 +204,7 @@ class BuildEnvironment:
|
||||
|
||||
# Build times -- to determine changed files
|
||||
# Also use this as an inventory of all existing and built filenames.
|
||||
# All "filenames" here are /-separated and relative and include '.rst'.
|
||||
self.all_files = {} # filename -> (mtime, md5sum) at the time of build
|
||||
|
||||
# File metadata
|
||||
@@ -279,7 +281,7 @@ class BuildEnvironment:
|
||||
Return (added, changed, removed) iterables.
|
||||
"""
|
||||
all_source_files = list(get_matching_files(
|
||||
self.srcdir, '*.rst', exclude=set(config.get('unused_files', ()))))
|
||||
self.srcdir, '*.rst', exclude=set(config.unused_files)))
|
||||
|
||||
# clear all files no longer present
|
||||
removed = set(self.all_files) - set(all_source_files)
|
||||
@@ -312,27 +314,26 @@ class BuildEnvironment:
|
||||
|
||||
return added, changed, removed
|
||||
|
||||
# If one of these config values changes, all files need to be re-read.
|
||||
influential_config_values = [
|
||||
'version', 'release', 'today', 'today_fmt', 'unused_files',
|
||||
'project', 'refcount_file', 'add_function_parentheses', 'add_module_names'
|
||||
]
|
||||
|
||||
def update(self, config):
|
||||
"""
|
||||
(Re-)read all files new or changed since last update.
|
||||
Yields a summary and then filenames as it processes them.
|
||||
Store all environment filenames in the canonical format
|
||||
(ie using SEP as a separator in place of os.path.sep).
|
||||
"""
|
||||
def update(self, config, hook=None):
|
||||
"""(Re-)read all files new or changed since last update. Yields a summary
|
||||
and then filenames as it processes them. Store all environment filenames
|
||||
in the canonical format (ie using SEP as a separator in place of
|
||||
os.path.sep)."""
|
||||
config_changed = False
|
||||
for val in self.influential_config_values:
|
||||
if self.config.get(val) != config.get(val):
|
||||
msg = '[config changed] '
|
||||
config_changed = True
|
||||
break
|
||||
if self.config is None:
|
||||
msg = '[new config] '
|
||||
config_changed = True
|
||||
else:
|
||||
msg = ''
|
||||
# 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
|
||||
if self.config[key] != config[key]:
|
||||
msg = '[config changed] '
|
||||
config_changed = True
|
||||
break
|
||||
else:
|
||||
msg = ''
|
||||
added, changed, removed = self.get_outdated_files(config, config_changed)
|
||||
msg += '%s added, %s changed, %s removed' % (len(added), len(changed),
|
||||
len(removed))
|
||||
@@ -341,9 +342,9 @@ class BuildEnvironment:
|
||||
self.config = config
|
||||
|
||||
# read the refcounts file
|
||||
if self.config.get('refcount_file'):
|
||||
if self.config.refcount_file:
|
||||
self.refcounts = Refcounts.fromfile(
|
||||
path.join(self.srcdir, self.config['refcount_file']))
|
||||
path.join(self.srcdir, self.config.refcount_file))
|
||||
|
||||
# clear all files no longer present
|
||||
for filename in removed:
|
||||
@@ -354,9 +355,12 @@ class BuildEnvironment:
|
||||
yield filename
|
||||
self.read_file(filename)
|
||||
|
||||
if 'contents.rst' not in self.all_files:
|
||||
self._warnfunc('no master file contents.rst found')
|
||||
|
||||
# --------- SINGLE FILE BUILDING -------------------------------------------
|
||||
|
||||
def read_file(self, filename, src_path=None, save_parsed=True):
|
||||
def read_file(self, filename, src_path=None, save_parsed=True, hook=None):
|
||||
"""Parse a file and add/update inventory entries for the doctree.
|
||||
If srcpath is given, read from a different source file."""
|
||||
# remove all inventory entries for that file
|
||||
@@ -382,6 +386,10 @@ class BuildEnvironment:
|
||||
f.close()
|
||||
self.all_files[filename] = (path.getmtime(src_path), md5sum)
|
||||
|
||||
# run post-read hook
|
||||
if hook:
|
||||
hook(doctree)
|
||||
|
||||
# make it picklable
|
||||
doctree.reporter = None
|
||||
doctree.transformer = None
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinx.extension
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Gracefully adapted from the TextPress event system by Armin.
|
||||
|
||||
:copyright: 2008 by Georg Brandl, Armin Ronacher.
|
||||
:license: BSD.
|
||||
"""
|
||||
|
||||
from sphinx.config import ConfigError
|
||||
|
||||
|
||||
def import_object(objname, source=None):
|
||||
"""Import an object from a 'module.name' string."""
|
||||
try:
|
||||
module, name = objname.rsplit('.', 1)
|
||||
except ValueError, err:
|
||||
raise ConfigError('Invalid full object name %s' % objname +
|
||||
(source and ' (needed for %s)' % source or ''), err)
|
||||
try:
|
||||
return getattr(__import__(module, None, None, [name]), name)
|
||||
except ImportError, err:
|
||||
raise ConfigError('Could not import %s' % module +
|
||||
(source and ' (needed for %s)' % source or ''), err)
|
||||
except AttributeError, err:
|
||||
raise ConfigError('Could not find %s' % objname +
|
||||
(source and ' (needed for %s)' % source or ''), err)
|
||||
|
||||
|
||||
# List of all known events. Maps name to arguments description.
|
||||
events = {
|
||||
'builder-created' : 'builder instance',
|
||||
'doctree-read' : 'the doctree before being pickled',
|
||||
}
|
||||
|
||||
class EventManager(object):
|
||||
"""
|
||||
Helper class that handles event listeners and events.
|
||||
|
||||
This is *not* a public interface. Always use the emit_event()
|
||||
functions to access it or the connect_event() / disconnect_event()
|
||||
functions on the application.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.next_listener_id = 0
|
||||
self._listeners = {}
|
||||
|
||||
def _validate(self, event):
|
||||
event = intern(event)
|
||||
if event not in events:
|
||||
raise RuntimeError('unknown event name: %s' % event)
|
||||
|
||||
def connect(self, event, callback):
|
||||
self._validate(event)
|
||||
listener_id = self.next_listener_id
|
||||
if event not in self._listeners:
|
||||
self._listeners[event] = {listener_id: callback}
|
||||
else:
|
||||
self._listeners[event][listener_id] = callback
|
||||
self.next_listener_id += 1
|
||||
return listener_id
|
||||
|
||||
def remove(self, listener_id):
|
||||
for event in self._listeners:
|
||||
event.pop(listener_id, None)
|
||||
|
||||
def emit(self, event, *args):
|
||||
self._validate(event)
|
||||
if event in self._listeners:
|
||||
for listener_id, callback in self._listeners[event].iteritems():
|
||||
yield listener_id, callback(*args)
|
||||
|
||||
|
||||
class DummyEventManager(EventManager):
|
||||
def connect(self, event, callback):
|
||||
self._validate(event)
|
||||
def remove(self, listener_id):
|
||||
pass
|
||||
def emit(self, event, *args):
|
||||
self._validate(event)
|
||||
+2
-2
@@ -129,8 +129,8 @@ def build_hhx(builder, outdir, outname):
|
||||
f = open(path.join(outdir, outname+'.hhp'), 'w')
|
||||
try:
|
||||
f.write(project_template % {'outname': outname,
|
||||
'version': builder.config['version'],
|
||||
'project': builder.config['project']})
|
||||
'version': builder.config.version,
|
||||
'project': builder.config.project})
|
||||
if not outdir.endswith(os.sep):
|
||||
outdir += os.sep
|
||||
olen = len(outdir)
|
||||
|
||||
+252
-246
@@ -18,7 +18,18 @@ from sphinx.util.smartypants import sphinx_smarty_pants
|
||||
class HTMLWriter(Writer):
|
||||
def __init__(self, builder):
|
||||
Writer.__init__(self)
|
||||
self.translator_class = translator_class(builder)
|
||||
self.builder = builder
|
||||
|
||||
def translate(self):
|
||||
# sadly, this is mostly copied from parent class
|
||||
self.visitor = visitor = self.builder.translator_class(self.builder,
|
||||
self.document)
|
||||
self.document.walkabout(visitor)
|
||||
self.output = visitor.astext()
|
||||
for attr in ('head_prefix', 'stylesheet', 'head', 'body_prefix',
|
||||
'body_pre_docinfo', 'docinfo', 'body', 'fragment',
|
||||
'body_suffix'):
|
||||
setattr(self, attr, getattr(visitor, attr))
|
||||
|
||||
|
||||
version_text = {
|
||||
@@ -27,283 +38,278 @@ version_text = {
|
||||
'versionadded': 'New in version %s',
|
||||
}
|
||||
|
||||
def translator_class(builder):
|
||||
class HTMLTranslator(BaseTranslator):
|
||||
"""
|
||||
Our custom HTML translator.
|
||||
"""
|
||||
class HTMLTranslator(BaseTranslator):
|
||||
"""
|
||||
Our custom HTML translator.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
self.no_smarty = 0
|
||||
BaseTranslator.__init__(self, *args, **kwds)
|
||||
self.highlightlang = 'python'
|
||||
self.language.labels['warning'] = 'Caveat'
|
||||
def __init__(self, builder, *args, **kwds):
|
||||
BaseTranslator.__init__(self, *args, **kwds)
|
||||
self.no_smarty = 0
|
||||
self.builder = builder
|
||||
self.highlightlang = 'python'
|
||||
self.language.labels['warning'] = 'Caveat'
|
||||
|
||||
def visit_desc(self, node):
|
||||
self.body.append(self.starttag(node, 'dl', CLASS=node['desctype']))
|
||||
def depart_desc(self, node):
|
||||
self.body.append('</dl>\n\n')
|
||||
def visit_desc(self, node):
|
||||
self.body.append(self.starttag(node, 'dl', CLASS=node['desctype']))
|
||||
def depart_desc(self, node):
|
||||
self.body.append('</dl>\n\n')
|
||||
|
||||
def visit_desc_signature(self, node):
|
||||
# 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']:
|
||||
self.body.append('<!--#%s#-->' % 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 builder.name != 'htmlhelp':
|
||||
self.body.append(u'<a class="headerlink" href="#%s" ' % node['ids'][0] +
|
||||
u'title="Permalink to this definition">\u00B6</a>')
|
||||
self.body.append('</dt>\n')
|
||||
def visit_desc_signature(self, node):
|
||||
# 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']:
|
||||
self.body.append('<!--#%s#-->' % 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.builder.name != 'htmlhelp':
|
||||
self.body.append(u'<a class="headerlink" href="#%s" ' % node['ids'][0] +
|
||||
u'title="Permalink to this definition">\u00B6</a>')
|
||||
self.body.append('</dt>\n')
|
||||
|
||||
def visit_desc_classname(self, node):
|
||||
self.body.append(self.starttag(node, 'tt', '', CLASS='descclassname'))
|
||||
def depart_desc_classname(self, node):
|
||||
self.body.append('</tt>')
|
||||
def visit_desc_classname(self, node):
|
||||
self.body.append(self.starttag(node, 'tt', '', CLASS='descclassname'))
|
||||
def depart_desc_classname(self, node):
|
||||
self.body.append('</tt>')
|
||||
|
||||
def visit_desc_type(self, node):
|
||||
# return type of C functions -- nothing to do here
|
||||
pass
|
||||
def depart_desc_type(self, node):
|
||||
pass
|
||||
def visit_desc_type(self, node):
|
||||
# return type of C functions -- nothing to do here
|
||||
pass
|
||||
def depart_desc_type(self, node):
|
||||
pass
|
||||
|
||||
def visit_desc_name(self, node):
|
||||
self.body.append(self.starttag(node, 'tt', '', CLASS='descname'))
|
||||
def depart_desc_name(self, node):
|
||||
self.body.append('</tt>')
|
||||
def visit_desc_name(self, node):
|
||||
self.body.append(self.starttag(node, 'tt', '', CLASS='descname'))
|
||||
def depart_desc_name(self, node):
|
||||
self.body.append('</tt>')
|
||||
|
||||
def visit_desc_parameterlist(self, node):
|
||||
self.body.append('<big>(</big>')
|
||||
self.first_param = 1
|
||||
def depart_desc_parameterlist(self, node):
|
||||
self.body.append('<big>)</big>')
|
||||
def visit_desc_parameterlist(self, node):
|
||||
self.body.append('<big>(</big>')
|
||||
self.first_param = 1
|
||||
def depart_desc_parameterlist(self, node):
|
||||
self.body.append('<big>)</big>')
|
||||
|
||||
def visit_desc_parameter(self, node):
|
||||
if not self.first_param:
|
||||
self.body.append(', ')
|
||||
else:
|
||||
self.first_param = 0
|
||||
if not node.hasattr('noemph'):
|
||||
self.body.append('<em>')
|
||||
def depart_desc_parameter(self, node):
|
||||
if not node.hasattr('noemph'):
|
||||
self.body.append('</em>')
|
||||
|
||||
def visit_desc_optional(self, node):
|
||||
self.body.append('<span class="optional">[</span>')
|
||||
def depart_desc_optional(self, node):
|
||||
self.body.append('<span class="optional">]</span>')
|
||||
|
||||
def visit_desc_content(self, node):
|
||||
self.body.append(self.starttag(node, 'dd', ''))
|
||||
def depart_desc_content(self, node):
|
||||
self.body.append('</dd>')
|
||||
|
||||
def visit_refcount(self, node):
|
||||
self.body.append(self.starttag(node, 'em', '', CLASS='refcount'))
|
||||
def depart_refcount(self, node):
|
||||
def visit_desc_parameter(self, node):
|
||||
if not self.first_param:
|
||||
self.body.append(', ')
|
||||
else:
|
||||
self.first_param = 0
|
||||
if not node.hasattr('noemph'):
|
||||
self.body.append('<em>')
|
||||
def depart_desc_parameter(self, node):
|
||||
if not node.hasattr('noemph'):
|
||||
self.body.append('</em>')
|
||||
|
||||
def visit_versionmodified(self, node):
|
||||
self.body.append(self.starttag(node, 'p'))
|
||||
text = version_text[node['type']] % node['version']
|
||||
if len(node):
|
||||
text += ': '
|
||||
def visit_desc_optional(self, node):
|
||||
self.body.append('<span class="optional">[</span>')
|
||||
def depart_desc_optional(self, node):
|
||||
self.body.append('<span class="optional">]</span>')
|
||||
|
||||
def visit_desc_content(self, node):
|
||||
self.body.append(self.starttag(node, 'dd', ''))
|
||||
def depart_desc_content(self, node):
|
||||
self.body.append('</dd>')
|
||||
|
||||
def visit_refcount(self, node):
|
||||
self.body.append(self.starttag(node, 'em', '', CLASS='refcount'))
|
||||
def depart_refcount(self, node):
|
||||
self.body.append('</em>')
|
||||
|
||||
def visit_versionmodified(self, node):
|
||||
self.body.append(self.starttag(node, 'p'))
|
||||
text = version_text[node['type']] % node['version']
|
||||
if len(node):
|
||||
text += ': '
|
||||
else:
|
||||
text += '.'
|
||||
self.body.append('<span class="versionmodified">%s</span>' % text)
|
||||
def depart_versionmodified(self, node):
|
||||
self.body.append('</p>\n')
|
||||
|
||||
# overwritten
|
||||
def visit_reference(self, node):
|
||||
BaseTranslator.visit_reference(self, node)
|
||||
if node.hasattr('reftitle'):
|
||||
# ugly hack to add a title attribute
|
||||
starttag = self.body[-1]
|
||||
if not starttag.startswith('<a '):
|
||||
return
|
||||
self.body[-1] = '<a title="%s"' % self.attval(node['reftitle']) + \
|
||||
starttag[2:]
|
||||
|
||||
# overwritten -- we don't want source comments to show up in the HTML
|
||||
def visit_comment(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
# overwritten
|
||||
def visit_admonition(self, node, name=''):
|
||||
self.body.append(self.start_tag_with_title(
|
||||
node, 'div', CLASS=('admonition ' + name)))
|
||||
if name and name != 'seealso':
|
||||
node.insert(0, nodes.title(name, self.language.labels[name]))
|
||||
self.set_first_last(node)
|
||||
|
||||
def visit_seealso(self, node):
|
||||
self.visit_admonition(node, 'seealso')
|
||||
def depart_seealso(self, node):
|
||||
self.depart_admonition(node)
|
||||
|
||||
# overwritten
|
||||
def visit_title(self, node, move_ids=1):
|
||||
# if we have a section we do our own processing in order
|
||||
# to have ids in the hN-tags and not in additional a-tags
|
||||
if isinstance(node.parent, nodes.section):
|
||||
h_level = self.section_level + self.initial_header_level - 1
|
||||
if node.parent.get('ids'):
|
||||
attrs = {'ids': node.parent['ids']}
|
||||
else:
|
||||
text += '.'
|
||||
self.body.append('<span class="versionmodified">%s</span>' % text)
|
||||
def depart_versionmodified(self, node):
|
||||
self.body.append('</p>\n')
|
||||
attrs = {}
|
||||
self.body.append(self.starttag(node, 'h%d' % h_level, '', **attrs))
|
||||
self.context.append('</h%d>\n' % h_level)
|
||||
else:
|
||||
BaseTranslator.visit_title(self, node, move_ids)
|
||||
|
||||
# overwritten
|
||||
def visit_reference(self, node):
|
||||
BaseTranslator.visit_reference(self, node)
|
||||
if node.hasattr('reftitle'):
|
||||
# ugly hack to add a title attribute
|
||||
starttag = self.body[-1]
|
||||
if not starttag.startswith('<a '):
|
||||
return
|
||||
self.body[-1] = '<a title="%s"' % self.attval(node['reftitle']) + \
|
||||
starttag[2:]
|
||||
# overwritten
|
||||
def visit_literal_block(self, node):
|
||||
from sphinx.highlighting import highlight_block
|
||||
self.body.append(highlight_block(node.rawsource, self.highlightlang))
|
||||
raise nodes.SkipNode
|
||||
|
||||
# overwritten -- we don't want source comments to show up in the HTML
|
||||
def visit_comment(self, node):
|
||||
raise nodes.SkipNode
|
||||
# overwritten
|
||||
def visit_literal(self, node):
|
||||
if len(node.children) == 1 and \
|
||||
node.children[0] in ('None', 'True', 'False'):
|
||||
node['classes'].append('xref')
|
||||
BaseTranslator.visit_literal(self, node)
|
||||
|
||||
# overwritten
|
||||
def visit_admonition(self, node, name=''):
|
||||
self.body.append(self.start_tag_with_title(
|
||||
node, 'div', CLASS=('admonition ' + name)))
|
||||
if name and name != 'seealso':
|
||||
node.insert(0, nodes.title(name, self.language.labels[name]))
|
||||
self.set_first_last(node)
|
||||
|
||||
def visit_seealso(self, node):
|
||||
self.visit_admonition(node, 'seealso')
|
||||
def depart_seealso(self, node):
|
||||
self.depart_admonition(node)
|
||||
|
||||
# overwritten
|
||||
def visit_title(self, node, move_ids=1):
|
||||
# if we have a section we do our own processing in order
|
||||
# to have ids in the hN-tags and not in additional a-tags
|
||||
if isinstance(node.parent, nodes.section):
|
||||
h_level = self.section_level + self.initial_header_level - 1
|
||||
if node.parent.get('ids'):
|
||||
attrs = {'ids': node.parent['ids']}
|
||||
else:
|
||||
attrs = {}
|
||||
self.body.append(self.starttag(node, 'h%d' % h_level, '', **attrs))
|
||||
self.context.append('</h%d>\n' % h_level)
|
||||
def visit_productionlist(self, node):
|
||||
self.body.append(self.starttag(node, 'pre'))
|
||||
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.body.append(self.starttag(production, 'strong', ''))
|
||||
self.body.append(production['tokenname'].ljust(maxlen) +
|
||||
'</strong> ::= ')
|
||||
lastname = production['tokenname']
|
||||
else:
|
||||
BaseTranslator.visit_title(self, node, move_ids)
|
||||
self.body.append('%s ' % (' '*len(lastname)))
|
||||
production.walkabout(self)
|
||||
self.body.append('\n')
|
||||
self.body.append('</pre>\n')
|
||||
raise nodes.SkipNode
|
||||
def depart_productionlist(self, node):
|
||||
pass
|
||||
|
||||
# overwritten
|
||||
def visit_literal_block(self, node):
|
||||
from sphinx.highlighting import highlight_block
|
||||
self.body.append(highlight_block(node.rawsource, self.highlightlang))
|
||||
raise nodes.SkipNode
|
||||
def visit_production(self, node):
|
||||
pass
|
||||
def depart_production(self, node):
|
||||
pass
|
||||
|
||||
# overwritten
|
||||
def visit_literal(self, node):
|
||||
if len(node.children) == 1 and \
|
||||
node.children[0] in ('None', 'True', 'False'):
|
||||
node['classes'].append('xref')
|
||||
BaseTranslator.visit_literal(self, node)
|
||||
def visit_centered(self, node):
|
||||
self.body.append(self.starttag(node, 'p', CLASS="centered") + '<strong>')
|
||||
def depart_centered(self, node):
|
||||
self.body.append('</strong></p>')
|
||||
|
||||
def visit_productionlist(self, node):
|
||||
self.body.append(self.starttag(node, 'pre'))
|
||||
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.body.append(self.starttag(production, 'strong', ''))
|
||||
self.body.append(production['tokenname'].ljust(maxlen) +
|
||||
'</strong> ::= ')
|
||||
lastname = production['tokenname']
|
||||
else:
|
||||
self.body.append('%s ' % (' '*len(lastname)))
|
||||
production.walkabout(self)
|
||||
self.body.append('\n')
|
||||
self.body.append('</pre>\n')
|
||||
raise nodes.SkipNode
|
||||
def depart_productionlist(self, node):
|
||||
pass
|
||||
def visit_compact_paragraph(self, node):
|
||||
pass
|
||||
def depart_compact_paragraph(self, node):
|
||||
pass
|
||||
|
||||
def visit_production(self, node):
|
||||
pass
|
||||
def depart_production(self, node):
|
||||
pass
|
||||
def visit_highlightlang(self, node):
|
||||
self.highlightlang = node['lang']
|
||||
def depart_highlightlang(self, node):
|
||||
pass
|
||||
|
||||
def visit_centered(self, node):
|
||||
self.body.append(self.starttag(node, 'p', CLASS="centered") + '<strong>')
|
||||
def depart_centered(self, node):
|
||||
self.body.append('</strong></p>')
|
||||
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_compact_paragraph(self, node):
|
||||
pass
|
||||
def depart_compact_paragraph(self, node):
|
||||
pass
|
||||
def visit_index(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_highlightlang(self, node):
|
||||
self.highlightlang = node['lang']
|
||||
def depart_highlightlang(self, node):
|
||||
pass
|
||||
def visit_glossary(self, node):
|
||||
pass
|
||||
def depart_glossary(self, node):
|
||||
pass
|
||||
|
||||
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_acks(self, node):
|
||||
pass
|
||||
def depart_acks(self, node):
|
||||
pass
|
||||
|
||||
def visit_index(self, node):
|
||||
raise nodes.SkipNode
|
||||
def visit_module(self, node):
|
||||
pass
|
||||
def depart_module(self, node):
|
||||
pass
|
||||
|
||||
def visit_glossary(self, node):
|
||||
pass
|
||||
def depart_glossary(self, node):
|
||||
pass
|
||||
# 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 visit_acks(self, node):
|
||||
pass
|
||||
def depart_acks(self, node):
|
||||
pass
|
||||
|
||||
def visit_module(self, node):
|
||||
pass
|
||||
def depart_module(self, node):
|
||||
pass
|
||||
|
||||
# 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 builder.name != 'htmlhelp' and \
|
||||
(close_tag.startswith('</h') or
|
||||
close_tag.startswith('</a></h')) and \
|
||||
node.parent.hasattr('ids') and node.parent['ids']:
|
||||
aname = node.parent['ids'][0]
|
||||
# add permalink anchor
|
||||
self.body.append(u'<a class="headerlink" href="#%s" ' % aname +
|
||||
u'title="Permalink to this headline">\u00B6</a>')
|
||||
BaseTranslator.depart_title(self, node)
|
||||
def depart_title(self, node):
|
||||
close_tag = self.context[-1]
|
||||
if self.builder.name != 'htmlhelp' and \
|
||||
(close_tag.startswith('</h') or
|
||||
close_tag.startswith('</a></h')) and \
|
||||
node.parent.hasattr('ids') and node.parent['ids']:
|
||||
aname = node.parent['ids'][0]
|
||||
# add permalink anchor
|
||||
self.body.append(u'<a class="headerlink" href="#%s" ' % aname +
|
||||
u'title="Permalink to this headline">\u00B6</a>')
|
||||
BaseTranslator.depart_title(self, node)
|
||||
|
||||
|
||||
class SmartyPantsHTMLTranslator(HTMLTranslator):
|
||||
"""
|
||||
Handle ordinary text via smartypants, converting quotes and dashes
|
||||
to the correct entities.
|
||||
"""
|
||||
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 __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)
|
||||
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_desc_signature(self, node):
|
||||
self.no_smarty += 1
|
||||
HTMLTranslator.visit_desc_signature(self, node)
|
||||
def visit_literal_emphasis(self, node):
|
||||
self.no_smarty += 1
|
||||
self.visit_emphasis(node)
|
||||
|
||||
def depart_desc_signature(self, 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
|
||||
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 encode(self, text):
|
||||
text = HTMLTranslator.encode(self, text)
|
||||
if self.no_smarty <= 0:
|
||||
text = sphinx_smarty_pants(text)
|
||||
return text
|
||||
|
||||
if builder.config.get('html_use_smartypants', False):
|
||||
return SmartyPantsHTMLTranslator
|
||||
else:
|
||||
return HTMLTranslator
|
||||
def encode(self, text):
|
||||
text = HTMLTranslator.encode(self, text)
|
||||
if self.no_smarty <= 0:
|
||||
text = sphinx_smarty_pants(text)
|
||||
return text
|
||||
|
||||
@@ -21,7 +21,7 @@ from docutils import frontend, nodes, languages, writers, utils
|
||||
from sphinx import addnodes
|
||||
from sphinx import highlighting
|
||||
|
||||
|
||||
# Move to a template?
|
||||
HEADER = r'''%% Generated by Sphinx.
|
||||
\documentclass[%(papersize)s,%(pointsize)s]{%(docclass)s}
|
||||
\usepackage[utf8]{inputenc}
|
||||
@@ -92,18 +92,18 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
self.builder = builder
|
||||
self.body = []
|
||||
docclass = document.settings.docclass
|
||||
paper = builder.config.get('latex_paper_size', 'letter') + 'paper'
|
||||
paper = builder.config.latex_paper_size + 'paper'
|
||||
if paper == 'paper': # e.g. command line "-D latex_paper_size="
|
||||
paper = 'letterpaper'
|
||||
date = time.strftime(builder.config.get('today_fmt', '%B %d, %Y'))
|
||||
date = time.strftime(builder.config.today_fmt)
|
||||
self.options = {'docclass': docclass,
|
||||
'papersize': paper,
|
||||
'pointsize': builder.config.get('latex_font_size', '10pt'),
|
||||
'preamble': builder.config['latex_preamble'],
|
||||
'pointsize': builder.config.latex_font_size,
|
||||
'preamble': builder.config.latex_preamble,
|
||||
'author': document.settings.author,
|
||||
'filename': document.settings.filename,
|
||||
'title': None, # is determined later
|
||||
'release': builder.config['release'],
|
||||
'release': builder.config.release,
|
||||
'date': date,
|
||||
}
|
||||
self.context = []
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinx.patchlevel
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Extract version info from Include/patchlevel.h.
|
||||
Adapted from Doc/tools/getversioninfo.
|
||||
|
||||
XXX Python specific
|
||||
|
||||
:copyright: 2007-2008 by Georg Brandl.
|
||||
:license: BSD.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
def get_version_info(srcdir):
|
||||
patchlevel_h = os.path.join(srcdir, '..', "Include", "patchlevel.h")
|
||||
|
||||
# This won't pick out all #defines, but it will pick up the ones we
|
||||
# care about.
|
||||
rx = re.compile(r"\s*#define\s+([a-zA-Z][a-zA-Z_0-9]*)\s+([a-zA-Z_0-9]+)")
|
||||
|
||||
d = {}
|
||||
f = open(patchlevel_h)
|
||||
try:
|
||||
for line in f:
|
||||
m = rx.match(line)
|
||||
if m is not None:
|
||||
name, value = m.group(1, 2)
|
||||
d[name] = value
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
release = version = "%s.%s" % (d["PY_MAJOR_VERSION"], d["PY_MINOR_VERSION"])
|
||||
micro = int(d["PY_MICRO_VERSION"])
|
||||
if micro != 0:
|
||||
release += "." + str(micro)
|
||||
|
||||
level = d["PY_RELEASE_LEVEL"]
|
||||
suffixes = {
|
||||
"PY_RELEASE_LEVEL_ALPHA": "a",
|
||||
"PY_RELEASE_LEVEL_BETA": "b",
|
||||
"PY_RELEASE_LEVEL_GAMMA": "c",
|
||||
}
|
||||
if level != "PY_RELEASE_LEVEL_FINAL":
|
||||
release += suffixes[level] + str(int(d["PY_RELEASE_SERIAL"]))
|
||||
return version, release
|
||||
|
||||
|
||||
def get_sys_version_info():
|
||||
major, minor, micro, level, serial = sys.version_info
|
||||
release = version = '%s.%s' % (major, minor)
|
||||
if micro:
|
||||
release += '.%s' % micro
|
||||
if level != 'final':
|
||||
release += '%s%s' % (level[0], serial)
|
||||
return version, release
|
||||
+1
-1
@@ -109,7 +109,7 @@ def xfileref_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
if text.endswith('()'):
|
||||
# remove parentheses
|
||||
text = text[:-2]
|
||||
if env.config.get('add_function_parentheses', True):
|
||||
if env.config.add_function_parentheses:
|
||||
# add them back to all occurrences if configured
|
||||
text += '()'
|
||||
# if the first character is a bang, don't cross-reference at all
|
||||
|
||||
@@ -113,7 +113,8 @@
|
||||
{% if customsidebar %}
|
||||
{{ rendertemplate(customsidebar) }}
|
||||
{% endif %}
|
||||
{% if current_page_name != "search" %}
|
||||
{% if current_page_name != "search" and builder == 'web' %}
|
||||
{# HTML builder search is disabled for now #}
|
||||
<h3>{{ builder == 'web' and 'Keyword' or 'Quick' }} search</h3>
|
||||
<form class="search" action="{{ pathto('search') }}" method="get">
|
||||
<input type="text" name="q" size="18"> <input type="submit" value="Go">
|
||||
|
||||
Reference in New Issue
Block a user