summaryrefslogtreecommitdiffstats
path: root/common/sphinxcontrib/bibtex/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'common/sphinxcontrib/bibtex/__init__.py')
-rw-r--r--common/sphinxcontrib/bibtex/__init__.py148
1 files changed, 148 insertions, 0 deletions
diff --git a/common/sphinxcontrib/bibtex/__init__.py b/common/sphinxcontrib/bibtex/__init__.py
new file mode 100644
index 0000000..d79e688
--- /dev/null
+++ b/common/sphinxcontrib/bibtex/__init__.py
@@ -0,0 +1,148 @@
+# -*- coding: utf-8 -*-
+"""
+ Sphinx Interface
+ ~~~~~~~~~~~~~~~~
+
+ .. autofunction:: setup
+ .. autofunction:: init_bibtex_cache
+ .. autofunction:: purge_bibtex_cache
+ .. autofunction:: process_citations
+ .. autofunction:: process_citation_references
+ .. autofunction:: check_duplicate_labels
+"""
+
+import docutils.nodes
+import docutils.parsers.rst
+from sphinxcontrib.bibtex.cache import Cache
+from sphinxcontrib.bibtex.nodes import bibliography
+from sphinxcontrib.bibtex.roles import CiteRole
+from sphinxcontrib.bibtex.directives import BibliographyDirective
+from sphinxcontrib.bibtex.transforms import BibliographyTransform
+import six
+
+
+def init_bibtex_cache(app):
+ """Create ``app.env.bibtex_cache`` if it does not exist yet.
+
+ :param app: The sphinx application.
+ :type app: :class:`sphinx.application.Sphinx`
+ """
+ if not hasattr(app.env, "bibtex_cache"):
+ app.env.bibtex_cache = Cache()
+
+
+def purge_bibtex_cache(app, env, docname):
+ """Remove all information related to *docname* from the cache.
+
+ :param app: The sphinx application.
+ :type app: :class:`sphinx.application.Sphinx`
+ :param env: The sphinx build environment.
+ :type env: :class:`sphinx.environment.BuildEnvironment`
+ """
+ env.bibtex_cache.purge(docname)
+
+
+def process_citations(app, doctree, docname):
+ """Replace labels of citation nodes by actual labels.
+
+ :param app: The sphinx application.
+ :type app: :class:`sphinx.application.Sphinx`
+ :param doctree: The document tree.
+ :type doctree: :class:`docutils.nodes.document`
+ :param docname: The document name.
+ :type docname: :class:`str`
+ """
+ for node in doctree.traverse(docutils.nodes.citation):
+ key = node[0].astext()
+ try:
+ label = app.env.bibtex_cache.get_label_from_key(key)
+ except KeyError:
+ app.warn("could not relabel citation [%s]" % key)
+ else:
+ node[0] = docutils.nodes.label('', label)
+
+
+def process_citation_references(app, doctree, docname):
+ """Replace text of citation reference nodes by actual labels.
+
+ :param app: The sphinx application.
+ :type app: :class:`sphinx.application.Sphinx`
+ :param doctree: The document tree.
+ :type doctree: :class:`docutils.nodes.document`
+ :param docname: The document name.
+ :type docname: :class:`str`
+ """
+ # sphinx has already turned citation_reference nodes
+ # into reference nodes, so iterate over reference nodes
+ for node in doctree.traverse(docutils.nodes.reference):
+ # exclude sphinx [source] labels
+ if isinstance(node[0], docutils.nodes.Element):
+ if 'viewcode-link' in node[0]['classes']:
+ continue
+ text = node[0].astext()
+ if text.startswith('[') and text.endswith(']'):
+ key = text[1:-1]
+ try:
+ label = app.env.bibtex_cache.get_label_from_key(key)
+ except KeyError:
+ app.warn("could not relabel citation reference [%s]" % key)
+ else:
+ node[0] = docutils.nodes.Text('[' + label + ']')
+
+
+def check_duplicate_labels(app, env):
+ """Check and warn about duplicate citation labels.
+
+ :param app: The sphinx application.
+ :type app: :class:`sphinx.application.Sphinx`
+ :param env: The sphinx build environment.
+ :type env: :class:`sphinx.environment.BuildEnvironment`
+ """
+ label_to_key = {}
+ for info in env.bibtex_cache.get_all_bibliography_caches():
+ for key, label in six.iteritems(info.labels):
+ if label in label_to_key:
+ app.warn(
+ "duplicate label for keys %s and %s"
+ % (key, label_to_key[label]))
+ else:
+ label_to_key[label] = key
+
+
+def setup(app):
+ """Set up the bibtex extension:
+
+ * register config values
+ * register directives
+ * register nodes
+ * register roles
+ * register transforms
+ * connect events to functions
+
+ :param app: The sphinx application.
+ :type app: :class:`sphinx.application.Sphinx`
+ """
+
+ app.add_config_value("bibtex_default_style", "alpha", "html")
+ app.connect("builder-inited", init_bibtex_cache)
+ app.connect("doctree-resolved", process_citations)
+ app.connect("doctree-resolved", process_citation_references)
+ app.connect("env-purge-doc", purge_bibtex_cache)
+ app.connect("env-updated", check_duplicate_labels)
+
+ # docutils keeps state around during testing, so to avoid spurious
+ # warnings, we detect here whether the directives have already been
+ # registered... very ugly hack but no better solution so far
+ _directives = docutils.parsers.rst.directives._directives
+ if "bibliography" not in _directives:
+ app.add_directive("bibliography", BibliographyDirective)
+ app.add_role("cite", CiteRole())
+ app.add_node(bibliography)
+ app.add_transform(BibliographyTransform)
+ else:
+ assert _directives["bibliography"] is BibliographyDirective
+
+ # Parallel read is not safe at the moment: in the current design,
+ # the document that contains references must be read last for all
+ # references to be resolved.
+ return {'parallel_read_safe': False}