mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Add "env-updated" and "missing-reference" events.
Write inventory file as part of the HTML build.
This commit is contained in:
parent
3481f4ae61
commit
97558dcc06
2
CHANGES
2
CHANGES
@ -27,6 +27,8 @@ New features added
|
|||||||
* sphinx.doc.autodoc has a new event ``autodoc-process-signature`` that
|
* sphinx.doc.autodoc has a new event ``autodoc-process-signature`` that
|
||||||
allows tuning function signature introspection.
|
allows tuning function signature introspection.
|
||||||
|
|
||||||
|
* Added new events: ``env-updated`` and ``missing-reference``.
|
||||||
|
|
||||||
|
|
||||||
Release 0.4.2 (Jul 29, 2008)
|
Release 0.4.2 (Jul 29, 2008)
|
||||||
============================
|
============================
|
||||||
|
6
TODO
6
TODO
@ -1,14 +1,16 @@
|
|||||||
Sphinx TODO
|
Sphinx TODO
|
||||||
===========
|
===========
|
||||||
|
|
||||||
- literal search mode
|
- RSS generation
|
||||||
|
- files for downloading
|
||||||
- specify node visit functions when adding nodes to app
|
- specify node visit functions when adding nodes to app
|
||||||
- allow extensions to add static files
|
- allow extensions to add static files
|
||||||
- decide which static files to include
|
- decide which static files to include
|
||||||
- verbose option
|
- verbose option
|
||||||
- remove redundant <ul>s in tocs
|
- remove redundant <ul>s in tocs
|
||||||
- autoattribute in autodoc
|
- autoattribute in autodoc
|
||||||
- range and object options for literalinclude
|
- section, range and object options for literalinclude
|
||||||
|
- literal search mode
|
||||||
- option for compact module index
|
- option for compact module index
|
||||||
- HTML section numbers?
|
- HTML section numbers?
|
||||||
- "seealso" links to external examples, see http://svn.python.org/projects/sandbox/trunk/seealso/ and http://effbot.org/zone/idea-seealso.htm
|
- "seealso" links to external examples, see http://svn.python.org/projects/sandbox/trunk/seealso/ and http://effbot.org/zone/idea-seealso.htm
|
||||||
|
@ -144,8 +144,17 @@ the following public API:
|
|||||||
|
|
||||||
.. method:: Sphinx.emit(event, *arguments)
|
.. method:: Sphinx.emit(event, *arguments)
|
||||||
|
|
||||||
Emit *event* and pass *arguments* to the callback functions. Do not emit
|
Emit *event* and pass *arguments* to the callback functions. Return the
|
||||||
core Sphinx events in extensions!
|
return values of all callbacks as a list. Do not emit core Sphinx events
|
||||||
|
in extensions!
|
||||||
|
|
||||||
|
.. method:: Sphinx.emit_firstresult(event, *arguments)
|
||||||
|
|
||||||
|
Emit *event* and pass *arguments* to the callback functions. Return the
|
||||||
|
result of the first callback that doesn't return ``None`` (and don't call
|
||||||
|
the rest of the callbacks).
|
||||||
|
|
||||||
|
.. versionadded:: 0.5
|
||||||
|
|
||||||
|
|
||||||
.. exception:: ExtensionError
|
.. exception:: ExtensionError
|
||||||
@ -167,18 +176,43 @@ registered event handlers.
|
|||||||
|
|
||||||
.. event:: builder-inited (app)
|
.. event:: builder-inited (app)
|
||||||
|
|
||||||
Emitted the builder object has been created.
|
Emitted when the builder object has been created. It is available as
|
||||||
|
``app.builder``.
|
||||||
|
|
||||||
.. event:: doctree-read (app, doctree)
|
.. event:: doctree-read (app, doctree)
|
||||||
|
|
||||||
Emitted when a doctree has been parsed and read by the environment, and is
|
Emitted when a doctree has been parsed and read by the environment, and is
|
||||||
about to be pickled.
|
about to be pickled. The *doctree* can be modified in-place.
|
||||||
|
|
||||||
|
.. event:: missing-reference (app, env, node, contnode)
|
||||||
|
|
||||||
|
Emitted when a cross-reference to a Python module or object cannot be
|
||||||
|
resolved. If the event handler can resolve the reference, it should return a
|
||||||
|
new docutils node to be inserted in the document tree in place of the node
|
||||||
|
*node*. Usually this node is a :class:`reference` node containing *contnode*
|
||||||
|
as a child.
|
||||||
|
|
||||||
|
:param env: The build environment (``app.builder.env``).
|
||||||
|
:param node: The :class:`pending_xref` node to be resolved. Its attributes
|
||||||
|
``reftype``, ``reftarget``, ``modname`` and ``classname`` attributes
|
||||||
|
determine the type and target of the reference.
|
||||||
|
:param contnode: The node that carries the text and formatting inside the
|
||||||
|
future reference and should be a child of the returned reference node.
|
||||||
|
|
||||||
|
.. versionadded:: 0.5
|
||||||
|
|
||||||
.. event:: doctree-resolved (app, doctree, docname)
|
.. event:: doctree-resolved (app, doctree, docname)
|
||||||
|
|
||||||
Emitted when a doctree has been "resolved" by the environment, that is, all
|
Emitted when a doctree has been "resolved" by the environment, that is, all
|
||||||
references and TOCs have been inserted.
|
references have been resolved and TOCs have been inserted.
|
||||||
|
|
||||||
|
.. event:: env-updated (app, env)
|
||||||
|
|
||||||
|
Emitted when the :meth:`update` method of the build environment has
|
||||||
|
completed, that is, the environment and all doctrees are now up-to-date.
|
||||||
|
|
||||||
|
.. versionadded:: 0.5
|
||||||
|
|
||||||
.. event:: page-context (app, pagename, templatename, context, doctree)
|
.. event:: page-context (app, pagename, templatename, context, doctree)
|
||||||
|
|
||||||
Emitted when the HTML builder has created a context dictionary to render a
|
Emitted when the HTML builder has created a context dictionary to render a
|
||||||
|
@ -167,6 +167,12 @@ class Sphinx(object):
|
|||||||
result.append(callback(self, *args))
|
result.append(callback(self, *args))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def emit_firstresult(self, event, *args):
|
||||||
|
for result in self.emit(event, *args):
|
||||||
|
if result is not None:
|
||||||
|
return result
|
||||||
|
return None
|
||||||
|
|
||||||
# registering addon parts
|
# registering addon parts
|
||||||
|
|
||||||
def add_builder(self, builder):
|
def add_builder(self, builder):
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
import gzip
|
||||||
import codecs
|
import codecs
|
||||||
import shutil
|
import shutil
|
||||||
import cPickle as pickle
|
import cPickle as pickle
|
||||||
@ -40,6 +41,8 @@ from sphinx import directives
|
|||||||
|
|
||||||
ENV_PICKLE_FILENAME = 'environment.pickle'
|
ENV_PICKLE_FILENAME = 'environment.pickle'
|
||||||
LAST_BUILD_FILENAME = 'last_build'
|
LAST_BUILD_FILENAME = 'last_build'
|
||||||
|
INVENTORY_FILENAME = 'inventory.txt.gz'
|
||||||
|
|
||||||
|
|
||||||
class Builder(object):
|
class Builder(object):
|
||||||
"""
|
"""
|
||||||
@ -153,7 +156,7 @@ class Builder(object):
|
|||||||
return
|
return
|
||||||
if not self.freshenv:
|
if not self.freshenv:
|
||||||
try:
|
try:
|
||||||
self.info(bold('trying to load pickled env... '), nonl=True)
|
self.info(bold('loading pickled environment... '), nonl=True)
|
||||||
self.env = BuildEnvironment.frompickle(self.config,
|
self.env = BuildEnvironment.frompickle(self.config,
|
||||||
path.join(self.doctreedir, ENV_PICKLE_FILENAME))
|
path.join(self.doctreedir, ENV_PICKLE_FILENAME))
|
||||||
self.info('done')
|
self.info('done')
|
||||||
@ -214,7 +217,7 @@ class Builder(object):
|
|||||||
iterator = self.env.update(self.config, self.srcdir, self.doctreedir, self.app)
|
iterator = self.env.update(self.config, self.srcdir, self.doctreedir, self.app)
|
||||||
# the first item in the iterator is a summary message
|
# the first item in the iterator is a summary message
|
||||||
self.info(iterator.next())
|
self.info(iterator.next())
|
||||||
for docname in self.status_iterator(iterator, 'reading... ', purple):
|
for docname in self.status_iterator(iterator, 'reading sources... ', purple):
|
||||||
updated_docnames.append(docname)
|
updated_docnames.append(docname)
|
||||||
# nothing further to do, the environment has already done the reading
|
# nothing further to do, the environment has already done the reading
|
||||||
for warning in warnings:
|
for warning in warnings:
|
||||||
@ -224,13 +227,14 @@ class Builder(object):
|
|||||||
|
|
||||||
if updated_docnames:
|
if updated_docnames:
|
||||||
# save the environment
|
# save the environment
|
||||||
self.info(bold('pickling the env... '), nonl=True)
|
self.info(bold('pickling environment... '), nonl=True)
|
||||||
self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME))
|
self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME))
|
||||||
self.info('done')
|
self.info('done')
|
||||||
|
|
||||||
# global actions
|
# global actions
|
||||||
self.info(bold('checking consistency...'))
|
self.info(bold('checking consistency... '), nonl=True)
|
||||||
self.env.check_consistency()
|
self.env.check_consistency()
|
||||||
|
self.info('done')
|
||||||
else:
|
else:
|
||||||
if method == 'update' and not docnames:
|
if method == 'update' and not docnames:
|
||||||
self.info(bold('no targets are out of date.'))
|
self.info(bold('no targets are out of date.'))
|
||||||
@ -241,7 +245,6 @@ class Builder(object):
|
|||||||
self.write(docnames, updated_docnames, method)
|
self.write(docnames, updated_docnames, method)
|
||||||
|
|
||||||
# finish (write static files etc.)
|
# finish (write static files etc.)
|
||||||
self.info(bold('finishing... '))
|
|
||||||
self.finish()
|
self.finish()
|
||||||
if self.app._warncount:
|
if self.app._warncount:
|
||||||
self.info(bold('build succeeded, %s warning%s.' %
|
self.info(bold('build succeeded, %s warning%s.' %
|
||||||
@ -266,7 +269,9 @@ class Builder(object):
|
|||||||
docnames.add(tocdocname)
|
docnames.add(tocdocname)
|
||||||
docnames.add(self.config.master_doc)
|
docnames.add(self.config.master_doc)
|
||||||
|
|
||||||
|
self.info(bold('preparing documents... '), nonl=True)
|
||||||
self.prepare_writing(docnames)
|
self.prepare_writing(docnames)
|
||||||
|
self.info('done')
|
||||||
|
|
||||||
# write target files
|
# write target files
|
||||||
warnings = []
|
warnings = []
|
||||||
@ -571,7 +576,7 @@ class StandaloneHTMLBuilder(Builder):
|
|||||||
|
|
||||||
# copy image files
|
# copy image files
|
||||||
if self.images:
|
if self.images:
|
||||||
self.info(bold('copying images...'), nonl=1)
|
self.info(bold('copying images...'), nonl=True)
|
||||||
ensuredir(path.join(self.outdir, '_images'))
|
ensuredir(path.join(self.outdir, '_images'))
|
||||||
for src, dest in self.images.iteritems():
|
for src, dest in self.images.iteritems():
|
||||||
self.info(' '+src, nonl=1)
|
self.info(' '+src, nonl=1)
|
||||||
@ -580,7 +585,7 @@ class StandaloneHTMLBuilder(Builder):
|
|||||||
self.info()
|
self.info()
|
||||||
|
|
||||||
# copy static files
|
# copy static files
|
||||||
self.info(bold('copying static files...'))
|
self.info(bold('copying static files... '), nonl=True)
|
||||||
ensuredir(path.join(self.outdir, '_static'))
|
ensuredir(path.join(self.outdir, '_static'))
|
||||||
staticdirnames = [path.join(path.dirname(__file__), 'static')] + \
|
staticdirnames = [path.join(path.dirname(__file__), 'static')] + \
|
||||||
[path.join(self.confdir, spath)
|
[path.join(self.confdir, spath)
|
||||||
@ -605,6 +610,7 @@ class StandaloneHTMLBuilder(Builder):
|
|||||||
f = open(path.join(self.outdir, '_static', 'pygments.css'), 'w')
|
f = open(path.join(self.outdir, '_static', 'pygments.css'), 'w')
|
||||||
f.write(PygmentsBridge('html', self.config.pygments_style).get_stylesheet())
|
f.write(PygmentsBridge('html', self.config.pygments_style).get_stylesheet())
|
||||||
f.close()
|
f.close()
|
||||||
|
self.info('done')
|
||||||
|
|
||||||
# dump the search index
|
# dump the search index
|
||||||
self.handle_finish()
|
self.handle_finish()
|
||||||
@ -693,13 +699,25 @@ class StandaloneHTMLBuilder(Builder):
|
|||||||
shutil.copyfile(self.env.doc2path(pagename), source_name)
|
shutil.copyfile(self.env.doc2path(pagename), source_name)
|
||||||
|
|
||||||
def handle_finish(self):
|
def handle_finish(self):
|
||||||
self.info(bold('dumping search index...'))
|
self.info(bold('dumping search index... '), nonl=True)
|
||||||
self.indexer.prune(self.env.all_docs)
|
self.indexer.prune(self.env.all_docs)
|
||||||
f = open(path.join(self.outdir, self.searchindex_filename), 'wb')
|
f = open(path.join(self.outdir, self.searchindex_filename), 'wb')
|
||||||
try:
|
try:
|
||||||
self.indexer.dump(f, self.indexer_format)
|
self.indexer.dump(f, self.indexer_format)
|
||||||
finally:
|
finally:
|
||||||
f.close()
|
f.close()
|
||||||
|
self.info('done')
|
||||||
|
|
||||||
|
self.info(bold('dumping object inventory... '), nonl=True)
|
||||||
|
f = gzip.open(path.join(self.outdir, INVENTORY_FILENAME), 'w')
|
||||||
|
try:
|
||||||
|
for modname, info in self.env.modules.iteritems():
|
||||||
|
f.write('%s mod %s\n' % (modname, self.get_target_uri(info[0])))
|
||||||
|
for refname, (docname, desctype) in self.env.descrefs.iteritems():
|
||||||
|
f.write('%s %s %s\n' % (refname, desctype, self.get_target_uri(docname)))
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
self.info('done')
|
||||||
|
|
||||||
|
|
||||||
class SerializingHTMLBuilder(StandaloneHTMLBuilder):
|
class SerializingHTMLBuilder(StandaloneHTMLBuilder):
|
||||||
@ -974,12 +992,13 @@ class LaTeXBuilder(Builder):
|
|||||||
shutil.copyfile(path.join(self.confdir, self.config.latex_logo),
|
shutil.copyfile(path.join(self.confdir, self.config.latex_logo),
|
||||||
path.join(self.outdir, logobase))
|
path.join(self.outdir, logobase))
|
||||||
|
|
||||||
self.info(bold('copying TeX support files...'))
|
self.info(bold('copying TeX support files... '), nonl=True)
|
||||||
staticdirname = path.join(path.dirname(__file__), 'texinputs')
|
staticdirname = path.join(path.dirname(__file__), 'texinputs')
|
||||||
for filename in os.listdir(staticdirname):
|
for filename in os.listdir(staticdirname):
|
||||||
if not filename.startswith('.'):
|
if not filename.startswith('.'):
|
||||||
shutil.copyfile(path.join(staticdirname, filename),
|
shutil.copyfile(path.join(staticdirname, filename),
|
||||||
path.join(self.outdir, filename))
|
path.join(self.outdir, filename))
|
||||||
|
self.info('done')
|
||||||
|
|
||||||
|
|
||||||
class ChangesBuilder(Builder):
|
class ChangesBuilder(Builder):
|
||||||
|
@ -457,6 +457,9 @@ class BuildEnvironment:
|
|||||||
if not os.access(path.join(self.srcdir, imgsrc), os.R_OK):
|
if not os.access(path.join(self.srcdir, imgsrc), os.R_OK):
|
||||||
del self.images[imgsrc]
|
del self.images[imgsrc]
|
||||||
|
|
||||||
|
if app:
|
||||||
|
app.emit('env-updated', self)
|
||||||
|
|
||||||
|
|
||||||
# --------- SINGLE FILE READING --------------------------------------------
|
# --------- SINGLE FILE READING --------------------------------------------
|
||||||
|
|
||||||
@ -880,6 +883,10 @@ class BuildEnvironment:
|
|||||||
'meth', 'cfunc', 'cdata', 'ctype', 'cmacro', 'obj'))
|
'meth', 'cfunc', 'cdata', 'ctype', 'cmacro', 'obj'))
|
||||||
|
|
||||||
def resolve_references(self, doctree, fromdocname, builder):
|
def resolve_references(self, doctree, fromdocname, builder):
|
||||||
|
reftarget_roles = set(('token', 'term', 'option'))
|
||||||
|
# add all custom xref types too
|
||||||
|
reftarget_roles.update(i[0] for i in additional_xref_types.values())
|
||||||
|
|
||||||
for node in doctree.traverse(addnodes.pending_xref):
|
for node in doctree.traverse(addnodes.pending_xref):
|
||||||
contnode = node[0].deepcopy()
|
contnode = node[0].deepcopy()
|
||||||
newnode = None
|
newnode = None
|
||||||
@ -887,10 +894,6 @@ class BuildEnvironment:
|
|||||||
typ = node['reftype']
|
typ = node['reftype']
|
||||||
target = node['reftarget']
|
target = node['reftarget']
|
||||||
|
|
||||||
reftarget_roles = set(('token', 'term', 'option'))
|
|
||||||
# add all custom xref types too
|
|
||||||
reftarget_roles.update(i[0] for i in additional_xref_types.values())
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if typ == 'ref':
|
if typ == 'ref':
|
||||||
if node['refcaption']:
|
if node['refcaption']:
|
||||||
@ -962,7 +965,12 @@ class BuildEnvironment:
|
|||||||
# because the anchor is generally below the heading which is ugly
|
# because the anchor is generally below the heading which is ugly
|
||||||
# but can't be helped easily
|
# but can't be helped easily
|
||||||
anchor = ''
|
anchor = ''
|
||||||
if not docname or docname == fromdocname:
|
if not docname:
|
||||||
|
newnode = builder.app.emit_firstresult('missing-reference',
|
||||||
|
self, node, contnode)
|
||||||
|
if not newnode:
|
||||||
|
newnode = contnode
|
||||||
|
elif docname == fromdocname:
|
||||||
# don't link to self
|
# don't link to self
|
||||||
newnode = contnode
|
newnode = contnode
|
||||||
else:
|
else:
|
||||||
@ -983,7 +991,10 @@ class BuildEnvironment:
|
|||||||
name, desc = self.find_desc(modname, clsname,
|
name, desc = self.find_desc(modname, clsname,
|
||||||
target, typ, searchorder)
|
target, typ, searchorder)
|
||||||
if not desc:
|
if not desc:
|
||||||
newnode = contnode
|
newnode = builder.app.emit_firstresult('missing-reference',
|
||||||
|
self, node, contnode)
|
||||||
|
if not newnode:
|
||||||
|
newnode = contnode
|
||||||
else:
|
else:
|
||||||
newnode = nodes.reference('', '')
|
newnode = nodes.reference('', '')
|
||||||
if desc[0] == fromdocname:
|
if desc[0] == fromdocname:
|
||||||
|
@ -350,11 +350,10 @@ class RstGenerator(object):
|
|||||||
args = None
|
args = None
|
||||||
err = e
|
err = e
|
||||||
|
|
||||||
results = self.env.app.emit('autodoc-process-signature',
|
result = self.env.app.emit_firstresult('autodoc-process-signature', what,
|
||||||
what, name, obj, self.options, args, retann)
|
name, obj, self.options, args, retann)
|
||||||
for result in results:
|
if result:
|
||||||
if result:
|
args, retann = result
|
||||||
args, retann = result
|
|
||||||
|
|
||||||
if args is not None:
|
if args is not None:
|
||||||
return '%s%s' % (args, retann or '')
|
return '%s%s' % (args, retann or '')
|
||||||
|
Loading…
Reference in New Issue
Block a user