mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
More refactoring, this time allowing different file extensions
and a different master file. Also fix environment warning reporting and improve handling of error conditions.
This commit is contained in:
parent
8ca7c91443
commit
b2ec05e690
2
HACKING
2
HACKING
@ -6,6 +6,8 @@ Coding overview
|
||||
|
||||
This document tries to give you a cursory overview of the doctools code.
|
||||
|
||||
TODO: update this.
|
||||
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
2
README
2
README
@ -4,6 +4,8 @@ doctools README
|
||||
FIXME: This is already outdated since the conversion has happened and the
|
||||
reST sources are in the Python tree now.
|
||||
|
||||
TODO: update this.
|
||||
|
||||
|
||||
What you need to know
|
||||
---------------------
|
||||
|
@ -11,10 +11,4 @@ import sys
|
||||
|
||||
if __name__ == '__main__':
|
||||
from sphinx import main
|
||||
try:
|
||||
sys.exit(main(sys.argv))
|
||||
except Exception:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
import pdb
|
||||
pdb.post_mortem(sys.exc_traceback)
|
||||
sys.exit(main(sys.argv))
|
||||
|
@ -34,7 +34,7 @@ def ifconfig_directive(name, arguments, options, content, lineno,
|
||||
return [node]
|
||||
|
||||
|
||||
def process_ifconfig_nodes(app, doctree, docfilename):
|
||||
def process_ifconfig_nodes(app, doctree, docname):
|
||||
ns = app.config.__dict__.copy()
|
||||
ns['builder'] = app.builder.name
|
||||
for node in doctree.traverse(ifconfig):
|
||||
|
@ -42,9 +42,9 @@ class ExtensionError(Exception):
|
||||
|
||||
# List of all known events. Maps name to arguments description.
|
||||
events = {
|
||||
'builder-inited': 'builder instance',
|
||||
'builder-inited': '',
|
||||
'doctree-read' : 'the doctree before being pickled',
|
||||
'doctree-resolved' : 'the doctree, the filename, the builder',
|
||||
'doctree-resolved' : 'the doctree, the docname',
|
||||
}
|
||||
|
||||
class Application(object):
|
||||
|
@ -27,13 +27,13 @@ from docutils.frontend import OptionParser
|
||||
from docutils.readers.doctree import Reader as DoctreeReader
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.util import (get_matching_files, ensuredir, relative_uri, os_path, SEP)
|
||||
from sphinx.util import (get_matching_docs, ensuredir, relative_uri, SEP, os_path)
|
||||
from sphinx.htmlhelp import build_hhx
|
||||
from sphinx.htmlwriter import HTMLWriter, HTMLTranslator, SmartyPantsHTMLTranslator
|
||||
from sphinx.latexwriter import LaTeXWriter
|
||||
from sphinx.environment import BuildEnvironment, NoUri
|
||||
from sphinx.highlighting import pygments, get_stylesheet
|
||||
from sphinx.util.console import bold, purple, green, red, darkgreen
|
||||
from sphinx.util.console import bold, purple, red, darkgreen
|
||||
|
||||
# side effect: registers roles and directives
|
||||
from sphinx import roles
|
||||
@ -64,6 +64,7 @@ class Builder(object):
|
||||
self.freshenv = freshenv
|
||||
|
||||
self.init()
|
||||
self.load_env()
|
||||
|
||||
# helper methods
|
||||
|
||||
@ -91,8 +92,11 @@ class Builder(object):
|
||||
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."""
|
||||
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):
|
||||
@ -102,7 +106,7 @@ class Builder(object):
|
||||
return relative_uri(self.get_target_uri(from_),
|
||||
self.get_target_uri(to, typ))
|
||||
|
||||
def get_outdated_files(self):
|
||||
def get_outdated_docs(self):
|
||||
"""Return a list of output files that are outdated."""
|
||||
raise NotImplementedError
|
||||
|
||||
@ -132,29 +136,33 @@ class Builder(object):
|
||||
self.info('done')
|
||||
except Exception, err:
|
||||
self.info('failed: %s' % err)
|
||||
self.env = BuildEnvironment(self.srcdir, self.doctreedir)
|
||||
self.env = BuildEnvironment(self.srcdir, self.doctreedir, self.config)
|
||||
else:
|
||||
self.env = BuildEnvironment(self.srcdir, self.doctreedir)
|
||||
self.env = BuildEnvironment(self.srcdir, self.doctreedir, self.config)
|
||||
self.env.set_warnfunc(self.warn)
|
||||
|
||||
def build_all(self):
|
||||
"""Build all source files."""
|
||||
self.load_env()
|
||||
self.build(None, summary='all source files')
|
||||
|
||||
def build_specific(self, source_filenames):
|
||||
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.
|
||||
# relative to the source directory and without source_suffix.
|
||||
dirlen = len(self.srcdir) + 1
|
||||
to_write = [path.abspath(filename)[dirlen:] for filename in source_filenames]
|
||||
self.load_env()
|
||||
to_write = []
|
||||
suffix = self.config.source_suffix
|
||||
for filename in filenames:
|
||||
filename = path.abspath(filename)[dirlen:]
|
||||
if filename.endswith(suffix):
|
||||
filename = filename[:-len(suffix)]
|
||||
to_write.append(filename)
|
||||
self.build(to_write,
|
||||
summary='%d source files given on command line' % len(to_write))
|
||||
|
||||
def build_update(self):
|
||||
"""Only rebuild files changed or added since last build."""
|
||||
self.load_env()
|
||||
to_build = self.get_outdated_files()
|
||||
to_build = self.get_outdated_docs()
|
||||
if not to_build:
|
||||
self.info(bold('no target files are out of date, exiting.'))
|
||||
return
|
||||
@ -166,12 +174,12 @@ class Builder(object):
|
||||
summary='targets for %d source files that are '
|
||||
'out of date' % len(to_build))
|
||||
|
||||
def build(self, filenames, summary=None):
|
||||
def build(self, docnames, summary=None):
|
||||
if summary:
|
||||
self.info(bold('building [%s]: ' % self.name), nonl=1)
|
||||
self.info(summary)
|
||||
|
||||
updated_filenames = []
|
||||
updated_docnames = []
|
||||
# while reading, collect all warnings from docutils
|
||||
warnings = []
|
||||
self.env.set_warnfunc(warnings.append)
|
||||
@ -179,14 +187,15 @@ class Builder(object):
|
||||
iterator = self.env.update(self.config, self.app)
|
||||
# the first item in the iterator is a summary message
|
||||
self.info(iterator.next())
|
||||
for filename in self.status_iterator(iterator, 'reading... ', purple):
|
||||
updated_filenames.append(filename)
|
||||
for docname in self.status_iterator(iterator, 'reading... ', purple):
|
||||
updated_docnames.append(docname)
|
||||
# nothing further to do, the environment has already done the reading
|
||||
for warning in warnings:
|
||||
self.warn(warning)
|
||||
if warning.strip():
|
||||
self.warn(warning)
|
||||
self.env.set_warnfunc(self.warn)
|
||||
|
||||
if updated_filenames:
|
||||
if updated_docnames:
|
||||
# save the environment
|
||||
self.info(bold('pickling the env... '), nonl=True)
|
||||
self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME))
|
||||
@ -198,43 +207,44 @@ class Builder(object):
|
||||
|
||||
# another indirection to support methods which don't build files
|
||||
# individually
|
||||
self.write(filenames, updated_filenames)
|
||||
self.write(docnames, updated_docnames)
|
||||
|
||||
# finish (write style files etc.)
|
||||
self.info(bold('finishing... '))
|
||||
self.finish()
|
||||
self.info(bold('build succeeded.'))
|
||||
|
||||
def write(self, build_filenames, updated_filenames):
|
||||
if build_filenames is None: # build_all
|
||||
build_filenames = self.env.all_files
|
||||
filenames = set(build_filenames) | set(updated_filenames)
|
||||
def write(self, build_docnames, updated_docnames):
|
||||
if build_docnames is None: # build_all
|
||||
build_docnames = self.env.all_docs
|
||||
docnames = set(build_docnames) | set(updated_docnames)
|
||||
|
||||
# add all toctree-containing files that may have changed
|
||||
for filename in list(filenames):
|
||||
for tocfilename in self.env.files_to_rebuild.get(filename, []):
|
||||
filenames.add(tocfilename)
|
||||
filenames.add('contents.rst')
|
||||
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('creating index...'))
|
||||
self.env.create_index(self)
|
||||
self.prepare_writing(filenames)
|
||||
self.prepare_writing(docnames)
|
||||
|
||||
# write target files
|
||||
warnings = []
|
||||
self.env.set_warnfunc(warnings.append)
|
||||
for filename in self.status_iterator(sorted(filenames),
|
||||
'writing output... ', green):
|
||||
doctree = self.env.get_and_resolve_doctree(filename, self)
|
||||
self.write_file(filename, doctree)
|
||||
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:
|
||||
self.warn(warning)
|
||||
if warning.strip():
|
||||
self.warn(warning)
|
||||
self.env.set_warnfunc(self.warn)
|
||||
|
||||
def prepare_writing(self, filenames):
|
||||
def prepare_writing(self, docnames):
|
||||
raise NotImplementedError
|
||||
|
||||
def write_file(self, filename, doctree):
|
||||
def write_doc(self, docname, doctree):
|
||||
raise NotImplementedError
|
||||
|
||||
def finish(self):
|
||||
@ -252,6 +262,9 @@ class StandaloneHTMLBuilder(Builder):
|
||||
def init(self):
|
||||
"""Load templates."""
|
||||
self.init_templates()
|
||||
self.init_translator_class()
|
||||
|
||||
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')
|
||||
@ -272,10 +285,10 @@ class StandaloneHTMLBuilder(Builder):
|
||||
settings_overrides={'output_encoding': 'unicode'}
|
||||
)
|
||||
|
||||
def prepare_writing(self, filenames):
|
||||
def prepare_writing(self, docnames):
|
||||
from sphinx.search import IndexBuilder
|
||||
self.indexer = IndexBuilder()
|
||||
self.load_indexer(filenames)
|
||||
self.load_indexer(docnames)
|
||||
self.docwriter = HTMLWriter(self)
|
||||
self.docsettings = OptionParser(
|
||||
defaults=self.env.settings,
|
||||
@ -301,8 +314,7 @@ class StandaloneHTMLBuilder(Builder):
|
||||
len = len, # the built-in
|
||||
)
|
||||
|
||||
def write_file(self, filename, doctree):
|
||||
pagename = filename[:-4]
|
||||
def write_doc(self, docname, doctree):
|
||||
destination = StringOutput(encoding='utf-8')
|
||||
doctree.settings = self.docsettings
|
||||
|
||||
@ -311,43 +323,43 @@ class StandaloneHTMLBuilder(Builder):
|
||||
|
||||
prev = next = None
|
||||
parents = []
|
||||
related = self.env.toctree_relations.get(filename)
|
||||
related = self.env.toctree_relations.get(docname)
|
||||
if related:
|
||||
prev = {'link': self.get_relative_uri(filename, related[1]),
|
||||
prev = {'link': self.get_relative_uri(docname, related[1]),
|
||||
'title': self.render_partial(self.env.titles[related[1]])['title']}
|
||||
next = {'link': self.get_relative_uri(filename, related[2]),
|
||||
next = {'link': self.get_relative_uri(docname, related[2]),
|
||||
'title': self.render_partial(self.env.titles[related[2]])['title']}
|
||||
while related:
|
||||
parents.append(
|
||||
{'link': self.get_relative_uri(filename, related[0]),
|
||||
{'link': self.get_relative_uri(docname, related[0]),
|
||||
'title': self.render_partial(self.env.titles[related[0]])['title']})
|
||||
related = self.env.toctree_relations.get(related[0])
|
||||
if parents:
|
||||
parents.pop() # remove link to "contents.rst"; we have a generic
|
||||
parents.pop() # remove link to the master file; we have a generic
|
||||
# "back to index" link already
|
||||
parents.reverse()
|
||||
|
||||
title = self.env.titles.get(filename)
|
||||
title = self.env.titles.get(docname)
|
||||
if title:
|
||||
title = self.render_partial(title)['title']
|
||||
else:
|
||||
title = ''
|
||||
self.globalcontext['titles'][filename] = title
|
||||
sourcename = pagename + '.txt'
|
||||
context = dict(
|
||||
self.globalcontext['titles'][docname] = title
|
||||
sourcename = self.config.html_copy_source and docname + '.txt' or ''
|
||||
ctx = dict(
|
||||
title = title,
|
||||
sourcename = sourcename,
|
||||
body = self.docwriter.parts['fragment'],
|
||||
toc = self.render_partial(self.env.get_toc_for(filename))['fragment'],
|
||||
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[filename] > 1),
|
||||
display_toc = (self.env.toc_num_entries[docname] > 1),
|
||||
parents = parents,
|
||||
prev = prev,
|
||||
next = next,
|
||||
)
|
||||
|
||||
self.index_page(pagename, doctree, title)
|
||||
self.handle_page(pagename, context)
|
||||
self.index_page(docname, doctree, title)
|
||||
self.handle_page(docname, ctx)
|
||||
|
||||
def finish(self):
|
||||
self.info(bold('writing additional files...'))
|
||||
@ -369,7 +381,7 @@ class StandaloneHTMLBuilder(Builder):
|
||||
# the global module index
|
||||
|
||||
# the sorted list of all modules, for the global module index
|
||||
modules = sorted(((mn, (self.get_relative_uri('modindex.rst', fn) +
|
||||
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())
|
||||
@ -442,24 +454,25 @@ class StandaloneHTMLBuilder(Builder):
|
||||
|
||||
# --------- these are overwritten by the Web builder
|
||||
|
||||
def get_target_uri(self, source_filename, typ=None):
|
||||
return source_filename[:-4] + '.html'
|
||||
def get_target_uri(self, docname, typ=None):
|
||||
return docname + '.html'
|
||||
|
||||
def get_outdated_files(self):
|
||||
for filename in get_matching_files(
|
||||
self.srcdir, '*.rst', exclude=set(self.config.unused_files)):
|
||||
def get_outdated_docs(self):
|
||||
for docname in get_matching_docs(
|
||||
self.srcdir, self.config.source_suffix,
|
||||
exclude=set(self.config.unused_files)):
|
||||
targetname = self.env.doc2path(docname, self.outdir, '.html')
|
||||
try:
|
||||
rstname = path.join(self.outdir, os_path(filename))
|
||||
targetmtime = path.getmtime(rstname[:-4] + '.html')
|
||||
targetmtime = path.getmtime(targetname)
|
||||
except:
|
||||
targetmtime = 0
|
||||
if filename not in self.env.all_files:
|
||||
yield filename
|
||||
elif path.getmtime(path.join(self.srcdir, os_path(filename))) > targetmtime:
|
||||
yield filename
|
||||
if docname not in self.env.all_docs:
|
||||
yield docname
|
||||
elif path.getmtime(self.env.doc2path(docname)) > targetmtime:
|
||||
yield docname
|
||||
|
||||
|
||||
def load_indexer(self, filenames):
|
||||
def load_indexer(self, docnames):
|
||||
try:
|
||||
f = open(path.join(self.outdir, 'searchindex.json'), 'r')
|
||||
try:
|
||||
@ -469,7 +482,7 @@ class StandaloneHTMLBuilder(Builder):
|
||||
except (IOError, OSError):
|
||||
pass
|
||||
# delete all entries for files that will be rebuilt
|
||||
self.indexer.prune([fn[:-4] for fn in set(self.env.all_files) - set(filenames)])
|
||||
self.indexer.prune(set(self.env.all_docs) - set(docnames))
|
||||
|
||||
def index_page(self, pagename, doctree, title):
|
||||
# only index pages with title
|
||||
@ -481,12 +494,12 @@ class StandaloneHTMLBuilder(Builder):
|
||||
ctx['current_page_name'] = pagename
|
||||
|
||||
def pathto(otheruri, resource=False,
|
||||
baseuri=self.get_target_uri(pagename+'.rst')):
|
||||
baseuri=self.get_target_uri(pagename)):
|
||||
if not resource:
|
||||
otheruri = self.get_target_uri(otheruri+'.rst')
|
||||
otheruri = self.get_target_uri(otheruri)
|
||||
return relative_uri(baseuri, otheruri)
|
||||
ctx['pathto'] = pathto
|
||||
ctx['hasdoc'] = lambda name: name+'.rst' in self.env.all_files
|
||||
ctx['hasdoc'] = lambda name: name in self.env.all_docs
|
||||
sidebarfile = self.config.html_sidebars.get(pagename)
|
||||
if sidebarfile:
|
||||
ctx['customsidebar'] = path.join(self.srcdir, sidebarfile)
|
||||
@ -505,12 +518,13 @@ class StandaloneHTMLBuilder(Builder):
|
||||
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
|
||||
shutil.copyfile(path.join(self.srcdir, os_path(pagename+'.rst')),
|
||||
path.join(self.outdir, os_path(ctx['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):
|
||||
self.info(bold('dumping search index...'))
|
||||
self.indexer.prune([fn[:-4] for fn in self.env.all_files])
|
||||
self.indexer.prune(self.env.all_docs)
|
||||
f = open(path.join(self.outdir, 'searchindex.json'), 'w')
|
||||
try:
|
||||
self.indexer.dump(f, 'json')
|
||||
@ -525,29 +539,28 @@ class WebHTMLBuilder(StandaloneHTMLBuilder):
|
||||
name = 'web'
|
||||
|
||||
def init(self):
|
||||
# Nothing to do here.
|
||||
pass
|
||||
self.init_translator_class()
|
||||
|
||||
def get_outdated_files(self):
|
||||
for filename in get_matching_files(
|
||||
self.srcdir, '*.rst', exclude=set(self.config.unused_files)):
|
||||
def get_outdated_docs(self):
|
||||
for docname in get_matching_docs(
|
||||
self.srcdir, self.config.source_suffix,
|
||||
exclude=set(self.config.unused_files)):
|
||||
targetname = self.env.doc2path(docname, self.outdir, '.fpickle')
|
||||
try:
|
||||
targetmtime = path.getmtime(
|
||||
path.join(self.outdir, os_path(filename)[:-4] + '.fpickle'))
|
||||
targetmtime = path.getmtime(targetname)
|
||||
except:
|
||||
targetmtime = 0
|
||||
if path.getmtime(path.join(self.srcdir,
|
||||
os_path(filename))) > targetmtime:
|
||||
yield filename
|
||||
if path.getmtime(self.env.doc2path(docname)) > targetmtime:
|
||||
yield docname
|
||||
|
||||
def get_target_uri(self, source_filename, typ=None):
|
||||
if source_filename == 'index.rst':
|
||||
def get_target_uri(self, docname, typ=None):
|
||||
if docname == 'index':
|
||||
return ''
|
||||
if source_filename.endswith(SEP+'index.rst'):
|
||||
return source_filename[:-9] # up to sep
|
||||
return source_filename[:-4] + SEP
|
||||
if docname.endswith(SEP + 'index'):
|
||||
return docname[:-5] # up to sep
|
||||
return docname + SEP
|
||||
|
||||
def load_indexer(self, filenames):
|
||||
def load_indexer(self, docnames):
|
||||
try:
|
||||
f = open(path.join(self.outdir, 'searchindex.pickle'), 'r')
|
||||
try:
|
||||
@ -557,32 +570,32 @@ class WebHTMLBuilder(StandaloneHTMLBuilder):
|
||||
except (IOError, OSError):
|
||||
pass
|
||||
# delete all entries for files that will be rebuilt
|
||||
self.indexer.prune(set(self.env.all_files) - set(filenames))
|
||||
self.indexer.prune(set(self.env.all_docs) - set(docnames))
|
||||
|
||||
def index_page(self, pagename, doctree, title):
|
||||
# only index pages with title
|
||||
if self.indexer is not None and title:
|
||||
self.indexer.feed(pagename+'.rst', title, doctree)
|
||||
self.indexer.feed(pagename, title, doctree)
|
||||
|
||||
def handle_page(self, pagename, context, templatename='page.html'):
|
||||
context['current_page_name'] = pagename
|
||||
def handle_page(self, pagename, ctx, templatename='page.html'):
|
||||
ctx['current_page_name'] = pagename
|
||||
sidebarfile = self.config.html_sidebars.get(pagename, '')
|
||||
if sidebarfile:
|
||||
context['customsidebar'] = path.join(self.srcdir, sidebarfile)
|
||||
ctx['customsidebar'] = path.join(self.srcdir, sidebarfile)
|
||||
outfilename = path.join(self.outdir, os_path(pagename) + '.fpickle')
|
||||
ensuredir(path.dirname(outfilename))
|
||||
f = open(outfilename, 'wb')
|
||||
try:
|
||||
pickle.dump(context, f, 2)
|
||||
pickle.dump(ctx, f, 2)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
# if there is a source file, copy the source file for the "show source" link
|
||||
if context.get('sourcename'):
|
||||
if ctx.get('sourcename'):
|
||||
source_name = path.join(self.outdir, 'sources',
|
||||
os_path(context['sourcename']))
|
||||
os_path(ctx['sourcename']))
|
||||
ensuredir(path.dirname(source_name))
|
||||
shutil.copyfile(path.join(self.srcdir, os_path(pagename)+'.rst'), source_name)
|
||||
shutil.copyfile(self.env.doc2path(pagename), source_name)
|
||||
|
||||
def handle_finish(self):
|
||||
# dump the global context
|
||||
@ -594,7 +607,7 @@ class WebHTMLBuilder(StandaloneHTMLBuilder):
|
||||
f.close()
|
||||
|
||||
self.info(bold('dumping search index...'))
|
||||
self.indexer.prune(self.env.all_files)
|
||||
self.indexer.prune(self.env.all_docs)
|
||||
f = open(path.join(self.outdir, 'searchindex.pickle'), 'wb')
|
||||
try:
|
||||
self.indexer.dump(f, 'pickle')
|
||||
@ -636,31 +649,41 @@ class LaTeXBuilder(Builder):
|
||||
name = 'latex'
|
||||
|
||||
def init(self):
|
||||
self.filenames = []
|
||||
self.document_data = map(list, self.config.latex_documents)
|
||||
self.docnames = []
|
||||
self.document_data = []
|
||||
|
||||
# assign subdirs to titles
|
||||
self.titles = []
|
||||
for entry in self.document_data:
|
||||
# replace version with real version
|
||||
sourcename = entry[0]
|
||||
if sourcename.endswith('/index.rst'):
|
||||
sourcename = sourcename[:-9]
|
||||
self.titles.append((sourcename, entry[2]))
|
||||
|
||||
def get_outdated_files(self):
|
||||
def get_outdated_docs(self):
|
||||
return 'all documents' # for now
|
||||
|
||||
def get_target_uri(self, source_filename, typ=None):
|
||||
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 source_filename not in self.filenames:
|
||||
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 = []
|
||||
@ -672,43 +695,40 @@ class LaTeXBuilder(Builder):
|
||||
defaults=self.env.settings,
|
||||
components=(docwriter,)).get_default_values()
|
||||
|
||||
if not self.document_data:
|
||||
self.warn('No "latex_documents" config setting found; no documents '
|
||||
'will be written.')
|
||||
self.init_document_data()
|
||||
|
||||
for sourcename, targetname, title, author, docclass in self.document_data:
|
||||
for docname, targetname, title, author, docclass in self.document_data:
|
||||
destination = FileOutput(
|
||||
destination_path=path.join(self.outdir, targetname),
|
||||
encoding='utf-8')
|
||||
self.info("processing " + targetname + "... ", nonl=1)
|
||||
doctree = self.assemble_doctree(
|
||||
sourcename, appendices=(docclass == 'manual') and appendices or [])
|
||||
docname, appendices=(docclass == 'manual') and appendices or [])
|
||||
self.info("writing... ", nonl=1)
|
||||
doctree.settings = docsettings
|
||||
doctree.settings.author = author
|
||||
doctree.settings.filename = sourcename
|
||||
doctree.settings.docname = docname
|
||||
doctree.settings.docclass = docclass
|
||||
docwriter.write(doctree, destination)
|
||||
self.info("done")
|
||||
|
||||
def assemble_doctree(self, indexfile, appendices):
|
||||
self.filenames = set([indexfile, 'glossary.rst', 'about.rst',
|
||||
'license.rst', 'copyright.rst'])
|
||||
self.info(green(indexfile) + " ", nonl=1)
|
||||
def process_tree(filename, tree):
|
||||
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(green(includefile) + " ", nonl=1)
|
||||
self.info(darkgreen(includefile) + " ", nonl=1)
|
||||
subtree = process_tree(includefile,
|
||||
self.env.get_doctree(includefile))
|
||||
self.filenames.add(includefile)
|
||||
self.docnames.add(includefile)
|
||||
except:
|
||||
self.warn('%s: toctree contains ref to nonexisting file %r' %
|
||||
(filename, includefile))
|
||||
(docname, includefile))
|
||||
else:
|
||||
newnodes.extend(subtree.children)
|
||||
toctreenode.parent.replace(toctreenode, newnodes)
|
||||
@ -721,11 +741,11 @@ class LaTeXBuilder(Builder):
|
||||
# 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):
|
||||
filename = pendingnode['reffilename']
|
||||
docname = pendingnode['refdocname']
|
||||
sectname = pendingnode['refsectname']
|
||||
newnodes = [nodes.emphasis(sectname, sectname)]
|
||||
for subdir, title in self.titles:
|
||||
if filename.startswith(subdir):
|
||||
if docname.startswith(subdir):
|
||||
newnodes.append(nodes.Text(' (in ', ' (in '))
|
||||
newnodes.append(nodes.emphasis(title, title))
|
||||
newnodes.append(nodes.Text(')', ')'))
|
||||
@ -756,7 +776,7 @@ class ChangesBuilder(Builder):
|
||||
self.vtemplate = self.get_template('changes/versionchanges.html')
|
||||
self.stemplate = self.get_template('changes/rstsource.html')
|
||||
|
||||
def get_outdated_files(self):
|
||||
def get_outdated_docs(self):
|
||||
return self.outdir
|
||||
|
||||
typemap = {
|
||||
@ -771,18 +791,18 @@ class ChangesBuilder(Builder):
|
||||
apichanges = []
|
||||
otherchanges = {}
|
||||
self.info(bold('writing summary file...'))
|
||||
for type, filename, lineno, module, descname, content in \
|
||||
for type, docname, lineno, module, descname, content in \
|
||||
self.env.versionchanges[version]:
|
||||
ttext = self.typemap[type]
|
||||
context = content.replace('\n', ' ')
|
||||
if descname and filename.startswith('c-api'):
|
||||
if descname and docname.startswith('c-api'):
|
||||
if not descname:
|
||||
continue
|
||||
if context:
|
||||
entry = '<b>%s</b>: <i>%s:</i> %s' % (descname, ttext, context)
|
||||
else:
|
||||
entry = '<b>%s</b>: <i>%s</i>.' % (descname, ttext)
|
||||
apichanges.append((entry, filename, lineno))
|
||||
apichanges.append((entry, docname, lineno))
|
||||
elif descname or module:
|
||||
if not module:
|
||||
module = 'Builtins'
|
||||
@ -792,14 +812,14 @@ class ChangesBuilder(Builder):
|
||||
entry = '<b>%s</b>: <i>%s:</i> %s' % (descname, ttext, context)
|
||||
else:
|
||||
entry = '<b>%s</b>: <i>%s</i>.' % (descname, ttext)
|
||||
libchanges.setdefault(module, []).append((entry, filename, lineno))
|
||||
libchanges.setdefault(module, []).append((entry, docname, lineno))
|
||||
else:
|
||||
if not context:
|
||||
continue
|
||||
entry = '<i>%s:</i> %s' % (ttext.capitalize(), context)
|
||||
title = self.env.titles[filename].astext()
|
||||
otherchanges.setdefault((filename, title), []).append(
|
||||
(entry, filename, lineno))
|
||||
title = self.env.titles[docname].astext()
|
||||
otherchanges.setdefault((docname, title), []).append(
|
||||
(entry, docname, lineno))
|
||||
|
||||
ctx = {
|
||||
'project': self.config.project,
|
||||
@ -832,15 +852,15 @@ class ChangesBuilder(Builder):
|
||||
return line
|
||||
|
||||
self.info(bold('copying source files...'))
|
||||
for filename in self.env.all_files:
|
||||
f = open(path.join(self.srcdir, os_path(filename)))
|
||||
for docname in self.env.all_docs:
|
||||
f = open(self.env.doc2path(docname))
|
||||
lines = f.readlines()
|
||||
targetfn = path.join(self.outdir, 'rst', os_path(filename)) + '.html'
|
||||
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': filename, 'text': text}
|
||||
ctx = {'filename': self.env.doc2path(docname, None), 'text': text}
|
||||
f.write(self.stemplate.render(ctx))
|
||||
finally:
|
||||
f.close()
|
||||
@ -873,25 +893,25 @@ class CheckExternalLinksBuilder(Builder):
|
||||
# create output file
|
||||
open(path.join(self.outdir, 'output.txt'), 'w').close()
|
||||
|
||||
def get_target_uri(self, source_filename, typ=None):
|
||||
def get_target_uri(self, docname, typ=None):
|
||||
return ''
|
||||
|
||||
def get_outdated_files(self):
|
||||
return self.env.all_files
|
||||
def get_outdated_docs(self):
|
||||
return self.env.all_docs
|
||||
|
||||
def prepare_writing(self, filenames):
|
||||
def prepare_writing(self, docnames):
|
||||
return
|
||||
|
||||
def write_file(self, filename, doctree):
|
||||
def write_doc(self, docname, doctree):
|
||||
self.info()
|
||||
for node in doctree.traverse(nodes.reference):
|
||||
try:
|
||||
self.check(node, filename)
|
||||
self.check(node, docname)
|
||||
except KeyError:
|
||||
continue
|
||||
return
|
||||
|
||||
def check(self, node, filename):
|
||||
def check(self, node, docname):
|
||||
uri = node['refuri']
|
||||
|
||||
if '#' in uri:
|
||||
@ -920,23 +940,24 @@ class CheckExternalLinksBuilder(Builder):
|
||||
elif r == 2:
|
||||
self.info(' - ' + red('broken: ') + s)
|
||||
self.broken[uri] = (r, s)
|
||||
self.write_entry('broken', filename, lineno, uri + ': ' + s)
|
||||
self.write_entry('broken', docname, lineno, uri + ': ' + s)
|
||||
else:
|
||||
self.info(' - ' + purple('redirected') + ' to ' + s)
|
||||
self.redirected[uri] = (r, s)
|
||||
self.write_entry('redirected', filename, lineno, uri + ' to ' + s)
|
||||
self.write_entry('redirected', docname, lineno, uri + ' to ' + s)
|
||||
|
||||
elif len(uri) == 0 or uri[0:7] == 'mailto:' or uri[0:4] == 'ftp:':
|
||||
return
|
||||
else:
|
||||
self.info(uri + ' - ' + red('malformed!'))
|
||||
self.write_entry('malformed', filename, lineno, uri)
|
||||
self.write_entry('malformed', docname, lineno, uri)
|
||||
|
||||
return
|
||||
|
||||
def write_entry(self, what, filename, line, uri):
|
||||
def write_entry(self, what, docname, line, uri):
|
||||
output = open(path.join(self.outdir, 'output.txt'), 'a')
|
||||
output.write("%s:%s [%s] %s\n" % (filename, line, what, uri))
|
||||
output.write("%s:%s [%s] %s\n" % (self.env.doc2path(docname, None),
|
||||
line, what, uri))
|
||||
output.close()
|
||||
|
||||
def resolve(self, uri):
|
||||
|
@ -33,6 +33,8 @@ class Config(object):
|
||||
extensions = ([], True),
|
||||
|
||||
# general reading options
|
||||
master_doc = ('contents', True),
|
||||
source_suffix = ('.rst', True),
|
||||
unused_files = ([], True),
|
||||
add_function_parentheses = (True, True),
|
||||
add_module_names = (True, True),
|
||||
@ -44,6 +46,7 @@ class Config(object):
|
||||
html_index = ('', False),
|
||||
html_sidebars = ({}, False),
|
||||
html_additional_pages = ({}, False),
|
||||
html_copy_source = (True, False),
|
||||
|
||||
# HTML help options
|
||||
htmlhelp_basename = ('pydoc', False),
|
||||
|
@ -539,15 +539,28 @@ directives.register_directive('moduleauthor', author_directive)
|
||||
def toctree_directive(name, arguments, options, content, lineno,
|
||||
content_offset, block_text, state, state_machine):
|
||||
env = state.document.settings.env
|
||||
dirname = posixpath.dirname(env.filename)
|
||||
suffix = env.config.source_suffix
|
||||
dirname = posixpath.dirname(env.docname)
|
||||
|
||||
ret = []
|
||||
subnode = addnodes.toctree()
|
||||
includefiles = filter(None, content)
|
||||
# absolutize filenames
|
||||
includefiles = [posixpath.normpath(posixpath.join(dirname, x)) for x in includefiles]
|
||||
includefiles = []
|
||||
for docname in content:
|
||||
if not docname:
|
||||
continue
|
||||
# absolutize filenames, remove suffixes
|
||||
if docname.endswith(suffix):
|
||||
docname = docname[:-len(suffix)]
|
||||
docname = posixpath.normpath(posixpath.join(dirname, docname))
|
||||
if docname not in env.found_docs:
|
||||
ret.append(state.document.reporter.warning(
|
||||
'toctree references unknown document %s' % docname, line=lineno))
|
||||
else:
|
||||
includefiles.append(docname)
|
||||
subnode['includefiles'] = includefiles
|
||||
subnode['maxdepth'] = options.get('maxdepth', -1)
|
||||
return [subnode]
|
||||
ret.append(subnode)
|
||||
return ret
|
||||
|
||||
toctree_directive.content = 1
|
||||
toctree_directive.options = {'maxdepth': int}
|
||||
|
@ -43,7 +43,7 @@ Body.enum.converters['loweralpha'] = \
|
||||
Body.enum.converters['upperroman'] = lambda x: None
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.util import get_matching_files, os_path, SEP
|
||||
from sphinx.util import get_matching_docs, SEP
|
||||
|
||||
default_settings = {
|
||||
'embed_stylesheet': False,
|
||||
@ -56,7 +56,7 @@ default_settings = {
|
||||
|
||||
# This is increased every time a new environment attribute is added
|
||||
# to properly invalidate pickle files.
|
||||
ENV_VERSION = 15
|
||||
ENV_VERSION = 16
|
||||
|
||||
|
||||
def walk_depth(node, depth, maxdepth):
|
||||
@ -79,8 +79,11 @@ default_substitutions = set([
|
||||
|
||||
|
||||
class RedirStream(object):
|
||||
def __init__(self, write):
|
||||
self.write = write
|
||||
def __init__(self, writefunc):
|
||||
self.writefunc = writefunc
|
||||
def write(self, text):
|
||||
if text.strip():
|
||||
self.writefunc(text)
|
||||
|
||||
|
||||
class NoUri(Exception):
|
||||
@ -183,10 +186,10 @@ class BuildEnvironment:
|
||||
|
||||
# --------- ENVIRONMENT INITIALIZATION -------------------------------------
|
||||
|
||||
def __init__(self, srcdir, doctreedir):
|
||||
def __init__(self, srcdir, doctreedir, config):
|
||||
self.doctreedir = doctreedir
|
||||
self.srcdir = srcdir
|
||||
self.config = None
|
||||
self.config = config
|
||||
|
||||
# the docutils settings for building
|
||||
self.settings = default_settings.copy()
|
||||
@ -198,41 +201,42 @@ class BuildEnvironment:
|
||||
# this is to invalidate old pickles
|
||||
self.version = ENV_VERSION
|
||||
|
||||
# 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
|
||||
# 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, md5sum) at the time of build
|
||||
# contains all built docnames
|
||||
|
||||
# File metadata
|
||||
self.metadata = {} # filename -> dict of metadata items
|
||||
self.metadata = {} # docname -> dict of metadata items
|
||||
|
||||
# TOC inventory
|
||||
self.titles = {} # filename -> title node
|
||||
self.tocs = {} # filename -> table of contents nodetree
|
||||
self.toc_num_entries = {} # filename -> number of real entries
|
||||
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)
|
||||
self.toctree_relations = {} # filename -> ["parent", "previous", "next"] filename
|
||||
self.toctree_relations = {} # docname -> ["parent", "previous", "next"] docname
|
||||
# for navigating in the toctree
|
||||
self.files_to_rebuild = {} # filename -> set of files (containing its TOCs)
|
||||
self.files_to_rebuild = {} # docname -> set of files (containing its TOCs)
|
||||
# to rebuild too
|
||||
|
||||
# X-ref target inventory
|
||||
self.descrefs = {} # fullname -> filename, desctype
|
||||
self.filemodules = {} # filename -> [modules]
|
||||
self.modules = {} # modname -> filename, synopsis, platform, deprecated
|
||||
self.labels = {} # labelname -> filename, labelid, sectionname
|
||||
self.reftargets = {} # (type, name) -> filename, labelid
|
||||
self.descrefs = {} # fullname -> docname, desctype
|
||||
self.filemodules = {} # docname -> [modules]
|
||||
self.modules = {} # modname -> docname, synopsis, platform, deprecated
|
||||
self.labels = {} # labelname -> docname, labelid, sectionname
|
||||
self.reftargets = {} # (type, name) -> docname, labelid
|
||||
# where type is term, token, option, envvar
|
||||
|
||||
# Other inventories
|
||||
self.indexentries = {} # filename -> list of
|
||||
self.indexentries = {} # docname -> list of
|
||||
# (type, string, target, aliasname)
|
||||
self.versionchanges = {} # version -> list of
|
||||
# (type, filename, lineno, module, descname, content)
|
||||
# (type, docname, lineno, module, descname, content)
|
||||
|
||||
# These are set while parsing a file
|
||||
self.filename = None # current file name
|
||||
self.docname = None # current document name
|
||||
self.currmodule = None # current module name
|
||||
self.currclass = None # current class name
|
||||
self.currdesc = None # current descref name
|
||||
@ -241,78 +245,95 @@ class BuildEnvironment:
|
||||
|
||||
def set_warnfunc(self, func):
|
||||
self._warnfunc = func
|
||||
self.settings['warnfunc'] = func
|
||||
self.settings['warning_stream'] = RedirStream(func)
|
||||
|
||||
def clear_file(self, filename):
|
||||
def warn(self, docname, msg):
|
||||
if docname:
|
||||
self._warnfunc(self.doc2path(docname) + ':: ' + msg)
|
||||
else:
|
||||
self._warnfunc('GLOBAL:: ' + msg)
|
||||
|
||||
def clear_doc(self, docname):
|
||||
"""Remove all traces of a source file in the inventory."""
|
||||
if filename in self.all_files:
|
||||
self.all_files.pop(filename, None)
|
||||
self.metadata.pop(filename, None)
|
||||
self.titles.pop(filename, None)
|
||||
self.tocs.pop(filename, None)
|
||||
self.toc_num_entries.pop(filename, None)
|
||||
if docname in self.all_docs:
|
||||
self.all_docs.pop(docname, None)
|
||||
self.metadata.pop(docname, None)
|
||||
self.titles.pop(docname, None)
|
||||
self.tocs.pop(docname, None)
|
||||
self.toc_num_entries.pop(docname, None)
|
||||
|
||||
for subfn, fnset in self.files_to_rebuild.iteritems():
|
||||
fnset.discard(filename)
|
||||
fnset.discard(docname)
|
||||
for fullname, (fn, _) in self.descrefs.items():
|
||||
if fn == filename:
|
||||
if fn == docname:
|
||||
del self.descrefs[fullname]
|
||||
self.filemodules.pop(filename, None)
|
||||
self.filemodules.pop(docname, None)
|
||||
for modname, (fn, _, _, _) in self.modules.items():
|
||||
if fn == filename:
|
||||
if fn == docname:
|
||||
del self.modules[modname]
|
||||
for labelname, (fn, _, _) in self.labels.items():
|
||||
if fn == filename:
|
||||
if fn == docname:
|
||||
del self.labels[labelname]
|
||||
for key, (fn, _) in self.reftargets.items():
|
||||
if fn == filename:
|
||||
if fn == docname:
|
||||
del self.reftargets[key]
|
||||
self.indexentries.pop(filename, None)
|
||||
self.indexentries.pop(docname, None)
|
||||
for version, changes in self.versionchanges.items():
|
||||
new = [change for change in changes if change[1] != filename]
|
||||
new = [change for change in changes if change[1] != docname]
|
||||
changes[:] = new
|
||||
|
||||
def doc2path(self, docname, base=True, suffix=None):
|
||||
"""
|
||||
Return the filename for the document name.
|
||||
If base is True, return absolute path under self.srcdir.
|
||||
If base is None, return relative path to self.srcdir.
|
||||
If base is a path string, return absolute path under that.
|
||||
If suffix is not None, add it instead of config.source_suffix.
|
||||
"""
|
||||
suffix = suffix or self.config.source_suffix
|
||||
if base is True:
|
||||
return path.join(self.srcdir, docname.replace(SEP, path.sep)) + suffix
|
||||
elif base is None:
|
||||
return docname.replace(SEP, path.sep) + suffix
|
||||
else:
|
||||
return path.join(base, docname.replace(SEP, path.sep)) + suffix
|
||||
|
||||
def get_outdated_files(self, config, config_changed):
|
||||
"""
|
||||
Return (added, changed, removed) iterables.
|
||||
Return (added, changed, removed) sets.
|
||||
"""
|
||||
all_source_files = list(get_matching_files(
|
||||
self.srcdir, '*.rst', exclude=set(config.unused_files)))
|
||||
self.found_docs = set(get_matching_docs(self.srcdir, config.source_suffix,
|
||||
exclude=set(config.unused_files)))
|
||||
|
||||
# clear all files no longer present
|
||||
removed = set(self.all_files) - set(all_source_files)
|
||||
removed = set(self.all_docs) - self.found_docs
|
||||
|
||||
added = []
|
||||
changed = []
|
||||
added = set()
|
||||
changed = set()
|
||||
|
||||
if config_changed:
|
||||
# config values affect e.g. substitutions
|
||||
added = all_source_files
|
||||
added = self.found_docs
|
||||
else:
|
||||
for filename in all_source_files:
|
||||
if filename not in self.all_files:
|
||||
added.append(filename)
|
||||
for docname in self.found_docs:
|
||||
if docname not in self.all_docs:
|
||||
added.add(docname)
|
||||
else:
|
||||
# if the doctree file is not there, rebuild
|
||||
if not path.isfile(path.join(self.doctreedir,
|
||||
os_path(filename)[:-3] + 'doctree')):
|
||||
changed.append(filename)
|
||||
if not path.isfile(self.doc2path(docname, self.doctreedir, '.doctree')):
|
||||
changed.add(docname)
|
||||
continue
|
||||
mtime, md5sum = self.all_files[filename]
|
||||
newmtime = path.getmtime(path.join(self.srcdir, os_path(filename)))
|
||||
mtime, md5sum = self.all_docs[docname]
|
||||
newmtime = path.getmtime(self.doc2path(docname))
|
||||
if newmtime == mtime:
|
||||
continue
|
||||
# check the MD5
|
||||
#with file(path.join(self.srcdir, filename), 'rb') as f:
|
||||
# newmd5sum = md5(f.read()).digest()
|
||||
#if newmd5sum != md5sum:
|
||||
changed.append(filename)
|
||||
changed.add(docname)
|
||||
|
||||
return added, changed, removed
|
||||
|
||||
def update(self, config, app=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
|
||||
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
|
||||
@ -338,36 +359,36 @@ class BuildEnvironment:
|
||||
self.config = config
|
||||
|
||||
# clear all files no longer present
|
||||
for filename in removed:
|
||||
self.clear_file(filename)
|
||||
for docname in removed:
|
||||
self.clear_doc(docname)
|
||||
|
||||
# read all new and changed files
|
||||
for filename in added + changed:
|
||||
yield filename
|
||||
self.read_file(filename, app=app)
|
||||
for docname in sorted(added | changed):
|
||||
yield docname
|
||||
self.read_doc(docname, app=app)
|
||||
|
||||
if 'contents.rst' not in self.all_files:
|
||||
self._warnfunc('no master file contents.rst found')
|
||||
if config.master_doc not in self.all_docs:
|
||||
self.warn(None, 'no master file %s found' % self.doc2path(config.master_doc))
|
||||
|
||||
# --------- SINGLE FILE BUILDING -------------------------------------------
|
||||
|
||||
def read_file(self, filename, src_path=None, save_parsed=True, app=None):
|
||||
def read_doc(self, docname, src_path=None, save_parsed=True, app=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
|
||||
self.clear_file(filename)
|
||||
self.clear_doc(docname)
|
||||
|
||||
if src_path is None:
|
||||
src_path = path.join(self.srcdir, os_path(filename))
|
||||
src_path = self.doc2path(docname)
|
||||
|
||||
self.filename = filename
|
||||
self.docname = docname
|
||||
doctree = publish_doctree(None, src_path, FileInput,
|
||||
settings_overrides=self.settings,
|
||||
reader=MyStandaloneReader())
|
||||
self.process_metadata(filename, doctree)
|
||||
self.create_title_from(filename, doctree)
|
||||
self.note_labels_from(filename, doctree)
|
||||
self.build_toc_from(filename, doctree)
|
||||
self.process_metadata(docname, doctree)
|
||||
self.create_title_from(docname, doctree)
|
||||
self.note_labels_from(docname, doctree)
|
||||
self.build_toc_from(docname, doctree)
|
||||
|
||||
# calculate the MD5 of the file at time of build
|
||||
f = open(src_path, 'rb')
|
||||
@ -375,7 +396,7 @@ class BuildEnvironment:
|
||||
md5sum = md5(f.read()).digest()
|
||||
finally:
|
||||
f.close()
|
||||
self.all_files[filename] = (path.getmtime(src_path), md5sum)
|
||||
self.all_docs[docname] = (path.getmtime(src_path), md5sum)
|
||||
|
||||
if app:
|
||||
app.emit('doctree-read', doctree)
|
||||
@ -383,11 +404,11 @@ class BuildEnvironment:
|
||||
# make it picklable
|
||||
doctree.reporter = None
|
||||
doctree.transformer = None
|
||||
doctree.settings.warning_stream = None
|
||||
doctree.settings.env = None
|
||||
doctree.settings.warnfunc = None
|
||||
|
||||
# cleanup
|
||||
self.filename = None
|
||||
self.docname = None
|
||||
self.currmodule = None
|
||||
self.currclass = None
|
||||
self.indexnum = 0
|
||||
@ -395,8 +416,7 @@ class BuildEnvironment:
|
||||
|
||||
if save_parsed:
|
||||
# save the parsed doctree
|
||||
doctree_filename = path.join(self.doctreedir,
|
||||
os_path(filename)[:-3] + 'doctree')
|
||||
doctree_filename = self.doc2path(docname, self.doctreedir, '.doctree')
|
||||
dirname = path.dirname(doctree_filename)
|
||||
if not path.isdir(dirname):
|
||||
os.makedirs(dirname)
|
||||
@ -408,11 +428,11 @@ class BuildEnvironment:
|
||||
else:
|
||||
return doctree
|
||||
|
||||
def process_metadata(self, filename, doctree):
|
||||
def process_metadata(self, docname, doctree):
|
||||
"""
|
||||
Process the docinfo part of the doctree as metadata.
|
||||
"""
|
||||
self.metadata[filename] = md = {}
|
||||
self.metadata[docname] = md = {}
|
||||
docinfo = doctree[0]
|
||||
if docinfo.__class__ is not nodes.docinfo:
|
||||
# nothing to see here
|
||||
@ -426,7 +446,7 @@ class BuildEnvironment:
|
||||
md[name.astext()] = body.astext()
|
||||
del doctree[0]
|
||||
|
||||
def create_title_from(self, filename, document):
|
||||
def create_title_from(self, docname, document):
|
||||
"""
|
||||
Add a title node to the document (just copy the first section title),
|
||||
and store that title in the environment.
|
||||
@ -436,10 +456,10 @@ class BuildEnvironment:
|
||||
visitor = MyContentsFilter(document)
|
||||
node[0].walkabout(visitor)
|
||||
titlenode += visitor.get_entry_text()
|
||||
self.titles[filename] = titlenode
|
||||
self.titles[docname] = titlenode
|
||||
return
|
||||
|
||||
def note_labels_from(self, filename, document):
|
||||
def note_labels_from(self, docname, document):
|
||||
for name, explicit in document.nametypes.iteritems():
|
||||
if not explicit:
|
||||
continue
|
||||
@ -450,27 +470,27 @@ class BuildEnvironment:
|
||||
continue
|
||||
sectname = node[0].astext() # node[0] == title node
|
||||
if name in self.labels:
|
||||
self._warnfunc('duplicate label %s, ' % name +
|
||||
'in %s and %s' % (self.labels[name][0], filename))
|
||||
self.labels[name] = filename, labelid, sectname
|
||||
self.warn(docname, 'duplicate label %s, ' % name +
|
||||
'other instance in %s' % self.doc2path(self.labels[name][0]))
|
||||
self.labels[name] = docname, labelid, sectname
|
||||
|
||||
def note_toctree(self, filename, toctreenode):
|
||||
def note_toctree(self, docname, toctreenode):
|
||||
"""Note a TOC tree directive in a document and gather information about
|
||||
file relations from it."""
|
||||
includefiles = toctreenode['includefiles']
|
||||
includefiles_len = len(includefiles)
|
||||
for i, includefile in enumerate(includefiles):
|
||||
# the "previous" file for the first toctree item is the parent
|
||||
previous = i > 0 and includefiles[i-1] or filename
|
||||
previous = i > 0 and includefiles[i-1] or docname
|
||||
# the "next" file for the last toctree item is the parent again
|
||||
next = i < includefiles_len-1 and includefiles[i+1] or filename
|
||||
self.toctree_relations[includefile] = [filename, previous, next]
|
||||
next = i < includefiles_len-1 and includefiles[i+1] or docname
|
||||
self.toctree_relations[includefile] = [docname, previous, next]
|
||||
# note that if the included file is rebuilt, this one must be
|
||||
# too (since the TOC of the included file could have changed)
|
||||
self.files_to_rebuild.setdefault(includefile, set()).add(filename)
|
||||
self.files_to_rebuild.setdefault(includefile, set()).add(docname)
|
||||
|
||||
|
||||
def build_toc_from(self, filename, document):
|
||||
def build_toc_from(self, docname, document):
|
||||
"""Build a TOC from the doctree and store it in the inventory."""
|
||||
numentries = [0] # nonlocal again...
|
||||
|
||||
@ -483,7 +503,7 @@ class BuildEnvironment:
|
||||
item = subnode.copy()
|
||||
entries.append(item)
|
||||
# do the inventory stuff
|
||||
self.note_toctree(filename, subnode)
|
||||
self.note_toctree(docname, subnode)
|
||||
continue
|
||||
if not isinstance(subnode, nodes.section):
|
||||
continue
|
||||
@ -500,7 +520,7 @@ class BuildEnvironment:
|
||||
else:
|
||||
anchorname = '#' + subnode['ids'][0]
|
||||
numentries[0] += 1
|
||||
reference = nodes.reference('', '', refuri=filename,
|
||||
reference = nodes.reference('', '', refuri=docname,
|
||||
anchorname=anchorname,
|
||||
*nodetext)
|
||||
para = addnodes.compact_paragraph('', '', reference)
|
||||
@ -512,64 +532,66 @@ class BuildEnvironment:
|
||||
return []
|
||||
toc = build_toc(document)
|
||||
if toc:
|
||||
self.tocs[filename] = toc
|
||||
self.tocs[docname] = toc
|
||||
else:
|
||||
self.tocs[filename] = nodes.bullet_list('')
|
||||
self.toc_num_entries[filename] = numentries[0]
|
||||
self.tocs[docname] = nodes.bullet_list('')
|
||||
self.toc_num_entries[docname] = numentries[0]
|
||||
|
||||
def get_toc_for(self, filename):
|
||||
def get_toc_for(self, docname):
|
||||
"""Return a TOC nodetree -- for use on the same page only!"""
|
||||
toc = self.tocs[filename].deepcopy()
|
||||
toc = self.tocs[docname].deepcopy()
|
||||
for node in toc.traverse(nodes.reference):
|
||||
node['refuri'] = node['anchorname']
|
||||
return toc
|
||||
|
||||
# -------
|
||||
# these are called from docutils directives and therefore use self.filename
|
||||
# these are called from docutils directives and therefore use self.docname
|
||||
#
|
||||
def note_descref(self, fullname, desctype):
|
||||
if fullname in self.descrefs:
|
||||
self._warnfunc('duplicate canonical description name %s, ' % fullname +
|
||||
'in %s and %s' % (self.descrefs[fullname][0], self.filename))
|
||||
self.descrefs[fullname] = (self.filename, desctype)
|
||||
self.warn(self.docname,
|
||||
'duplicate canonical description name %s, ' % fullname +
|
||||
'other instance in %s' % self.doc2path(self.descrefs[fullname][0]))
|
||||
self.descrefs[fullname] = (self.docname, desctype)
|
||||
|
||||
def note_module(self, modname, synopsis, platform, deprecated):
|
||||
self.modules[modname] = (self.filename, synopsis, platform, deprecated)
|
||||
self.filemodules.setdefault(self.filename, []).append(modname)
|
||||
self.modules[modname] = (self.docname, synopsis, platform, deprecated)
|
||||
self.filemodules.setdefault(self.docname, []).append(modname)
|
||||
|
||||
def note_reftarget(self, type, name, labelid):
|
||||
self.reftargets[type, name] = (self.filename, labelid)
|
||||
self.reftargets[type, name] = (self.docname, labelid)
|
||||
|
||||
def note_index_entry(self, type, string, targetid, aliasname):
|
||||
self.indexentries.setdefault(self.filename, []).append(
|
||||
self.indexentries.setdefault(self.docname, []).append(
|
||||
(type, string, targetid, aliasname))
|
||||
|
||||
def note_versionchange(self, type, version, node, lineno):
|
||||
self.versionchanges.setdefault(version, []).append(
|
||||
(type, self.filename, lineno, self.currmodule, self.currdesc, node.astext()))
|
||||
(type, self.docname, lineno, self.currmodule, self.currdesc, node.astext()))
|
||||
# -------
|
||||
|
||||
# --------- RESOLVING REFERENCES AND TOCTREES ------------------------------
|
||||
|
||||
def get_doctree(self, filename):
|
||||
def get_doctree(self, docname):
|
||||
"""Read the doctree for a file from the pickle and return it."""
|
||||
doctree_filename = path.join(self.doctreedir, os_path(filename)[:-3] + 'doctree')
|
||||
doctree_filename = self.doc2path(docname, self.doctreedir, '.doctree')
|
||||
f = open(doctree_filename, 'rb')
|
||||
try:
|
||||
doctree = pickle.load(f)
|
||||
finally:
|
||||
f.close()
|
||||
doctree.reporter = Reporter(filename, 2, 4, stream=RedirStream(self._warnfunc))
|
||||
doctree.reporter = Reporter(self.doc2path(docname), 2, 4,
|
||||
stream=RedirStream(self._warnfunc))
|
||||
return doctree
|
||||
|
||||
def get_and_resolve_doctree(self, filename, builder, doctree=None):
|
||||
def get_and_resolve_doctree(self, docname, builder, doctree=None):
|
||||
"""Read the doctree from the pickle, resolve cross-references and
|
||||
toctrees and return it."""
|
||||
if doctree is None:
|
||||
doctree = self.get_doctree(filename)
|
||||
doctree = self.get_doctree(docname)
|
||||
|
||||
# resolve all pending cross-references
|
||||
self.resolve_references(doctree, filename, builder)
|
||||
self.resolve_references(doctree, docname, builder)
|
||||
|
||||
# now, resolve all toctree nodes
|
||||
def _entries_from_toctree(toctreenode):
|
||||
@ -582,8 +604,8 @@ class BuildEnvironment:
|
||||
toc = self.tocs[includefile].deepcopy()
|
||||
except KeyError:
|
||||
# this is raised if the included file does not exist
|
||||
self._warnfunc('%s: toctree contains ref to nonexisting '
|
||||
'file %r' % (filename, includefile))
|
||||
self.warn(docname, 'toctree contains ref to nonexisting '
|
||||
'file %r' % includefile)
|
||||
else:
|
||||
for toctreenode in toc.traverse(addnodes.toctree):
|
||||
toctreenode.parent.replace_self(
|
||||
@ -607,7 +629,7 @@ class BuildEnvironment:
|
||||
if node.hasattr('anchorname'):
|
||||
# a TOC reference
|
||||
node['refuri'] = builder.get_relative_uri(
|
||||
filename, node['refuri']) + node['anchorname']
|
||||
docname, node['refuri']) + node['anchorname']
|
||||
|
||||
return doctree
|
||||
|
||||
@ -615,7 +637,7 @@ class BuildEnvironment:
|
||||
descroles = frozenset(('data', 'exc', 'func', 'class', 'const', 'attr',
|
||||
'meth', 'cfunc', 'cdata', 'ctype', 'cmacro'))
|
||||
|
||||
def resolve_references(self, doctree, docfilename, builder):
|
||||
def resolve_references(self, doctree, fromdocname, builder):
|
||||
for node in doctree.traverse(addnodes.pending_xref):
|
||||
contnode = node[0].deepcopy()
|
||||
newnode = None
|
||||
@ -627,70 +649,69 @@ class BuildEnvironment:
|
||||
if typ == 'ref':
|
||||
# reference to the named label; the final node will contain the
|
||||
# section name after the label
|
||||
filename, labelid, sectname = self.labels.get(target, ('','',''))
|
||||
if not filename:
|
||||
docname, labelid, sectname = self.labels.get(target, ('','',''))
|
||||
if not docname:
|
||||
newnode = doctree.reporter.system_message(
|
||||
2, 'undefined label: %s' % target)
|
||||
self._warnfunc('%s: undefined label: %s' % (docfilename, target))
|
||||
#self.warn(fromdocname, 'undefined label: %s' % target)
|
||||
else:
|
||||
newnode = nodes.reference('', '')
|
||||
innernode = nodes.emphasis(sectname, sectname)
|
||||
if filename == docfilename:
|
||||
if docname == fromdocname:
|
||||
newnode['refid'] = labelid
|
||||
else:
|
||||
# set more info in contnode in case the following call
|
||||
# raises NoUri, the builder will have to resolve these
|
||||
contnode = addnodes.pending_xref('')
|
||||
contnode['reffilename'] = filename
|
||||
contnode['refdocname'] = docname
|
||||
contnode['refsectname'] = sectname
|
||||
newnode['refuri'] = builder.get_relative_uri(
|
||||
docfilename, filename) + '#' + labelid
|
||||
fromdocname, docname) + '#' + labelid
|
||||
newnode.append(innernode)
|
||||
elif typ == 'keyword':
|
||||
# keywords are referenced by named labels
|
||||
filename, labelid, _ = self.labels.get(target, ('','',''))
|
||||
if not filename:
|
||||
self._warnfunc('%s: unknown keyword: %s' % (docfilename, target))
|
||||
docname, labelid, _ = self.labels.get(target, ('','',''))
|
||||
if not docname:
|
||||
self.warn(fromdocname, 'unknown keyword: %s' % target)
|
||||
newnode = contnode
|
||||
else:
|
||||
newnode = nodes.reference('', '')
|
||||
if filename == docfilename:
|
||||
if docname == fromdocname:
|
||||
newnode['refid'] = labelid
|
||||
else:
|
||||
newnode['refuri'] = builder.get_relative_uri(
|
||||
docfilename, filename) + '#' + labelid
|
||||
fromdocname, docname) + '#' + labelid
|
||||
newnode.append(contnode)
|
||||
elif typ in ('token', 'term', 'envvar', 'option'):
|
||||
filename, labelid = self.reftargets.get((typ, target), ('', ''))
|
||||
if not filename:
|
||||
docname, labelid = self.reftargets.get((typ, target), ('', ''))
|
||||
if not docname:
|
||||
if typ == 'term':
|
||||
self._warnfunc('%s: term not in glossary: %s' %
|
||||
(docfilename, target))
|
||||
self.warn(fromdocname, 'term not in glossary: %s' % target)
|
||||
newnode = contnode
|
||||
else:
|
||||
newnode = nodes.reference('', '')
|
||||
if filename == docfilename:
|
||||
if docname == fromdocname:
|
||||
newnode['refid'] = labelid
|
||||
else:
|
||||
newnode['refuri'] = builder.get_relative_uri(
|
||||
docfilename, filename, typ) + '#' + labelid
|
||||
fromdocname, docname, typ) + '#' + labelid
|
||||
newnode.append(contnode)
|
||||
elif typ == 'mod':
|
||||
filename, synopsis, platform, deprecated = \
|
||||
docname, synopsis, platform, deprecated = \
|
||||
self.modules.get(target, ('','','', ''))
|
||||
# just link to an anchor if there are multiple modules in one file
|
||||
# because the anchor is generally below the heading which is ugly
|
||||
# but can't be helped easily
|
||||
anchor = ''
|
||||
if not filename or filename == docfilename:
|
||||
if not docname or docname == fromdocname:
|
||||
# don't link to self
|
||||
newnode = contnode
|
||||
else:
|
||||
if len(self.filemodules[filename]) > 1:
|
||||
if len(self.filemodules[docname]) > 1:
|
||||
anchor = '#' + 'module-' + target
|
||||
newnode = nodes.reference('', '')
|
||||
newnode['refuri'] = (
|
||||
builder.get_relative_uri(docfilename, filename) + anchor)
|
||||
builder.get_relative_uri(fromdocname, docname) + anchor)
|
||||
newnode['reftitle'] = '%s%s%s' % (
|
||||
(platform and '(%s) ' % platform),
|
||||
synopsis, (deprecated and ' (deprecated)' or ''))
|
||||
@ -706,11 +727,11 @@ class BuildEnvironment:
|
||||
newnode = contnode
|
||||
else:
|
||||
newnode = nodes.reference('', '')
|
||||
if desc[0] == docfilename:
|
||||
if desc[0] == fromdocname:
|
||||
newnode['refid'] = name
|
||||
else:
|
||||
newnode['refuri'] = (
|
||||
builder.get_relative_uri(docfilename, desc[0])
|
||||
builder.get_relative_uri(fromdocname, desc[0])
|
||||
+ '#' + name)
|
||||
newnode.append(contnode)
|
||||
else:
|
||||
@ -721,7 +742,7 @@ class BuildEnvironment:
|
||||
node.replace_self(newnode)
|
||||
|
||||
# allow custom references to be resolved
|
||||
builder.app.emit('doctree-resolved', doctree, docfilename)
|
||||
builder.app.emit('doctree-resolved', doctree, fromdocname)
|
||||
|
||||
def create_index(self, builder, _fixre=re.compile(r'(.*) ([(][^()]*[)])')):
|
||||
"""Create the real index from the collected index entries."""
|
||||
@ -735,7 +756,7 @@ class BuildEnvironment:
|
||||
add_entry(subword, '', dic=entry[1])
|
||||
else:
|
||||
try:
|
||||
entry[0].append(builder.get_relative_uri('genindex.rst', fn)
|
||||
entry[0].append(builder.get_relative_uri('genindex', fn)
|
||||
+ '#' + tid)
|
||||
except NoUri:
|
||||
pass
|
||||
@ -766,7 +787,7 @@ class BuildEnvironment:
|
||||
add_entry(string, 'built-in function')
|
||||
add_entry('built-in function', string)
|
||||
else:
|
||||
self._warnfunc("unknown index entry type %r in %s" % (type, fn))
|
||||
self.warn(fn, "unknown index entry type %r" % type)
|
||||
|
||||
newlist = new.items()
|
||||
newlist.sort(key=lambda t: t[0].lower())
|
||||
@ -815,12 +836,12 @@ class BuildEnvironment:
|
||||
def check_consistency(self):
|
||||
"""Do consistency checks."""
|
||||
|
||||
for filename in self.all_files:
|
||||
if filename not in self.toctree_relations:
|
||||
if filename == 'contents.rst':
|
||||
for docname in self.all_docs:
|
||||
if docname not in self.toctree_relations:
|
||||
if docname == self.config.master_doc:
|
||||
# the master file is not included anywhere ;)
|
||||
continue
|
||||
self._warnfunc('%s isn\'t included in any toctree' % filename)
|
||||
self.warn(docname, 'document isn\'t included in any toctree')
|
||||
|
||||
# --------- QUERYING -------------------------------------------------------
|
||||
|
||||
@ -879,26 +900,26 @@ class BuildEnvironment:
|
||||
Keywords searched are: first modules, then descrefs.
|
||||
|
||||
Returns: None if nothing found
|
||||
(type, filename, anchorname) if exact match found
|
||||
list of (quality, type, filename, anchorname, description) if fuzzy
|
||||
(type, docname, anchorname) if exact match found
|
||||
list of (quality, type, docname, anchorname, description) if fuzzy
|
||||
"""
|
||||
|
||||
if keyword in self.modules:
|
||||
filename, title, system, deprecated = self.modules[keyword]
|
||||
return 'module', filename, 'module-' + keyword
|
||||
docname, title, system, deprecated = self.modules[keyword]
|
||||
return 'module', docname, 'module-' + keyword
|
||||
if keyword in self.descrefs:
|
||||
filename, ref_type = self.descrefs[keyword]
|
||||
return ref_type, filename, keyword
|
||||
docname, ref_type = self.descrefs[keyword]
|
||||
return ref_type, docname, keyword
|
||||
# special cases
|
||||
if '.' not in keyword:
|
||||
# exceptions are documented in the exceptions module
|
||||
if 'exceptions.'+keyword in self.descrefs:
|
||||
filename, ref_type = self.descrefs['exceptions.'+keyword]
|
||||
return ref_type, filename, 'exceptions.'+keyword
|
||||
docname, ref_type = self.descrefs['exceptions.'+keyword]
|
||||
return ref_type, docname, 'exceptions.'+keyword
|
||||
# special methods are documented as object methods
|
||||
if 'object.'+keyword in self.descrefs:
|
||||
filename, ref_type = self.descrefs['object.'+keyword]
|
||||
return ref_type, filename, 'object.'+keyword
|
||||
docname, ref_type = self.descrefs['object.'+keyword]
|
||||
return ref_type, docname, 'object.'+keyword
|
||||
|
||||
if avoid_fuzzy:
|
||||
return
|
||||
@ -919,7 +940,7 @@ class BuildEnvironment:
|
||||
yield '.'.join(parts[idx:])
|
||||
|
||||
result = []
|
||||
for type, filename, title, desc in possibilities():
|
||||
for type, docname, title, desc in possibilities():
|
||||
best_res = 0
|
||||
for part in dotsearch(title):
|
||||
s.set_seq1(part)
|
||||
@ -929,16 +950,6 @@ class BuildEnvironment:
|
||||
s.ratio() > best_res:
|
||||
best_res = s.ratio()
|
||||
if best_res:
|
||||
result.append((best_res, type, filename, title, desc))
|
||||
result.append((best_res, type, docname, title, desc))
|
||||
|
||||
return heapq.nlargest(n, result)
|
||||
|
||||
def get_real_filename(self, filename):
|
||||
"""
|
||||
Pass this function a filename without .rst extension to get the real
|
||||
filename. This also resolves the special `index.rst` files. If the file
|
||||
does not exist the return value will be `None`.
|
||||
"""
|
||||
for rstname in filename + '.rst', filename + SEP + 'index.rst':
|
||||
if rstname in self.all_files:
|
||||
return rstname
|
||||
|
@ -149,7 +149,7 @@ def build_hhx(builder, outdir, outname):
|
||||
f.write('<LI> ' + object_sitemap % ('Main page', 'index.html'))
|
||||
f.write('<LI> ' + object_sitemap % ('Global Module Index', 'modindex.html'))
|
||||
# the TOC
|
||||
toc = builder.env.get_and_resolve_doctree('contents.rst', builder)
|
||||
toc = builder.env.get_and_resolve_doctree(builder.config.master_doc, builder)
|
||||
def write_toc(node, ullevel=0):
|
||||
if isinstance(node, nodes.list_item):
|
||||
f.write('<LI> ')
|
||||
|
@ -100,7 +100,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
'pointsize': builder.config.latex_font_size,
|
||||
'preamble': builder.config.latex_preamble,
|
||||
'author': document.settings.author,
|
||||
'filename': document.settings.filename,
|
||||
'docname': document.settings.docname,
|
||||
'title': None, # is determined later
|
||||
'release': builder.config.release,
|
||||
'date': date,
|
||||
@ -200,7 +200,7 @@ 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], 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')
|
||||
self.options['title'] = node.astext()
|
||||
self.this_is_the_title = 0
|
||||
@ -731,5 +731,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
def depart_Text(self, node):
|
||||
pass
|
||||
|
||||
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__)
|
||||
|
@ -1,6 +1,6 @@
|
||||
{% macro entries changes %}
|
||||
<ul>{% for entry, filename, lineno in changes %}
|
||||
<li><a href="rst/{{ filename }}.html#L{{ lineno-10 }}" target="src">{{ entry }}</a></li>
|
||||
<ul>{% for entry, docname, lineno in changes %}
|
||||
<li><a href="rst/{{ docname }}.html#L{{ lineno-10 }}" target="src">{{ entry }}</a></li>
|
||||
{% endfor %}</ul>
|
||||
{% endmacro -%}
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
|
@ -106,7 +106,7 @@
|
||||
<li><a href="{{ pathto('@edit/' + sourcename)|e }}">Suggest Change</a></li>
|
||||
<li><a href="{{ pathto('@source/' + sourcename)|e }}">Show Source</a></li>
|
||||
{% elif builder == 'html' %}
|
||||
<li><a href="{{ pathto(sourcename, true)|e }}">Show Source</a></li>
|
||||
<li><a href="{{ pathto('_sources/' + sourcename, true)|e }}">Show Source</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
@ -22,11 +22,8 @@ from os import path
|
||||
# hangover from more *nix-oriented origins.
|
||||
SEP = "/"
|
||||
|
||||
def canonical_path(ospath):
|
||||
return ospath.replace(os.path.sep, SEP)
|
||||
|
||||
def os_path(canpath):
|
||||
return canpath.replace(SEP, os.path.sep)
|
||||
def os_path(canonicalpath):
|
||||
return canonicalpath.replace(SEP, os.path.sep)
|
||||
|
||||
|
||||
def relative_uri(base, to):
|
||||
@ -51,8 +48,12 @@ def ensuredir(path):
|
||||
raise
|
||||
|
||||
|
||||
def get_matching_files(dirname, pattern, exclude=()):
|
||||
"""Get all files matching a pattern in a directory, recursively."""
|
||||
def get_matching_docs(dirname, suffix, exclude=()):
|
||||
"""
|
||||
Get all file names (without suffix) matching a suffix in a
|
||||
directory, recursively.
|
||||
"""
|
||||
pattern = '*' + suffix
|
||||
# dirname is a normalized absolute path.
|
||||
dirname = path.normpath(path.abspath(dirname))
|
||||
dirlen = len(dirname) + 1 # exclude slash
|
||||
@ -65,7 +66,7 @@ def get_matching_files(dirname, pattern, exclude=()):
|
||||
qualified_name = path.join(root[dirlen:], sfile)
|
||||
if qualified_name in exclude:
|
||||
continue
|
||||
yield canonical_path(qualified_name)
|
||||
yield qualified_name[:-len(suffix)].replace(os.path.sep, SEP)
|
||||
|
||||
|
||||
def shorten_result(text='', keywords=[], maxlen=240, fuzz=60):
|
||||
|
@ -225,7 +225,7 @@ class DocumentationApplication(object):
|
||||
builder = MockBuilder()
|
||||
builder.config = env2.config
|
||||
writer = HTMLWriter(builder)
|
||||
doctree = env2.read_file(page_id+'.rst', pathname, save_parsed=False)
|
||||
doctree = env2.read_doc(page_id, pathname, save_parsed=False)
|
||||
doctree = env2.get_and_resolve_doctree(page_id+'.rst', builder, doctree)
|
||||
doctree.settings = OptionParser(defaults=env2.settings,
|
||||
components=(writer,)).get_default_values()
|
||||
|
Loading…
Reference in New Issue
Block a user