[Python-checkins] r65500 - in doctools/trunk: CHANGES doc/ext/intersphinx.rst doc/extensions.rst sphinx/builder.py sphinx/ext/intersphinx.py

georg.brandl python-checkins at python.org
Mon Aug 4 19:31:26 CEST 2008


Author: georg.brandl
Date: Mon Aug  4 19:31:25 2008
New Revision: 65500

Log:
Add intersphinx extension.


Added:
   doctools/trunk/doc/ext/intersphinx.rst   (contents, props changed)
   doctools/trunk/sphinx/ext/intersphinx.py
Modified:
   doctools/trunk/CHANGES
   doctools/trunk/doc/extensions.rst
   doctools/trunk/sphinx/builder.py

Modified: doctools/trunk/CHANGES
==============================================================================
--- doctools/trunk/CHANGES	(original)
+++ doctools/trunk/CHANGES	Mon Aug  4 19:31:25 2008
@@ -4,6 +4,10 @@
 New features added
 ------------------
 
+* The new extension ``sphinx.ext.intersphinx`` half-automatically
+  creates links to Sphinx documentation of Python objects in other
+  projects.
+
 * Added a distutils command `build_sphinx`: When Sphinx is installed,
   you can call ``python setup.py build_sphinx`` for projects that
   have Sphinx documentation, which will build the docs and place them

Added: doctools/trunk/doc/ext/intersphinx.rst
==============================================================================
--- (empty file)
+++ doctools/trunk/doc/ext/intersphinx.rst	Mon Aug  4 19:31:25 2008
@@ -0,0 +1,67 @@
+:mod:`sphinx.ext.intersphinx` -- Link to other projects' documentation
+======================================================================
+
+.. module:: sphinx.ext.intersphinx
+   :synopsis: Link to other Sphinx documentation.
+
+.. index:: pair: automatic; linking
+
+.. versionadded:: 0.5
+
+This extension can generate automatic links to the documentation of Python
+objects in other projects.  This works as follows:
+
+* Each Sphinx HTML build creates a file named :file:`objects.inv` that
+  contains a mapping from Python identifiers to URIs relative to the HTML set's
+  root.
+
+* Projects using the Intersphinx extension can specify the location of such
+  mapping files in the :confval:`intersphinx_mapping` config value.  The mapping
+  will then be used to resolve otherwise missing references to Python objects
+  into links to the other documentation.
+
+* By default, the mapping file is assumed to be at the same location as the rest
+  of the documentation; however, the location of the mapping file can also be
+  specified individually, e.g. if the docs should be buildable without Internet
+  access.
+
+To use intersphinx linking, add ``'sphinx.ext.intersphinx'`` to your
+:confval:`extensions` config value, and use these new config values to activate
+linking:
+
+.. confval:: intersphinx_mapping
+
+   A dictionary mapping URIs to either ``None`` or an URI.  The keys are the
+   base URI of the foreign Sphinx documentation sets and can be local paths or
+   HTTP URIs.  The values indicate where the inventory file can be found: they
+   can be ``None`` (at the same location as the base URI) or another local or
+   HTTP URI.
+
+   Relative local paths in the keys are taken as relative to the base of the
+   built documentation, while relative local paths in the values are taken as
+   relative to the source directory.
+
+   An example, to add links to modules and objects in the Python standard
+   library documentation::
+
+      intersphinx_mapping = {'http://docs.python.org/dev': None}
+
+   This will download the corresponding :file:`objects.inv` file from the
+   Internet and generate links to the pages under the given URI.  The downloaded
+   inventory is cached in the Sphinx environment, so it must be redownloaded
+   whenever you do a full rebuild. 
+
+   A second example, showing the meaning of a non-``None`` value::
+
+      intersphinx_mapping = {'http://docs.python.org/dev': 'python-inv.txt'}
+
+   This will read the inventory from :file:`python.inv` in the source
+   directory, but still generate links to the pages under
+   ``http://docs.python.org/dev``.  It is up to you to update the inventory file
+   as new objects are added to the Python documentation.
+
+.. confval:: intersphinx_cache_limit
+
+   The maximum number of days to cache remote inventories.  The default is
+   ``5``, meaning five days.  Set this to a negative value to cache inventories
+   for unlimited time.

Modified: doctools/trunk/doc/extensions.rst
==============================================================================
--- doctools/trunk/doc/extensions.rst	(original)
+++ doctools/trunk/doc/extensions.rst	Mon Aug  4 19:31:25 2008
@@ -34,6 +34,7 @@
 
    ext/autodoc
    ext/doctest
+   ext/intersphinx
    ext/refcounting
    ext/ifconfig
    ext/coverage

Modified: doctools/trunk/sphinx/builder.py
==============================================================================
--- doctools/trunk/sphinx/builder.py	(original)
+++ doctools/trunk/sphinx/builder.py	Mon Aug  4 19:31:25 2008
@@ -40,7 +40,7 @@
 
 ENV_PICKLE_FILENAME = 'environment.pickle'
 LAST_BUILD_FILENAME = 'last_build'
-INVENTORY_FILENAME = 'inventory.txt'
+INVENTORY_FILENAME = 'objects.inv'
 
 
 class Builder(object):

Added: doctools/trunk/sphinx/ext/intersphinx.py
==============================================================================
--- (empty file)
+++ doctools/trunk/sphinx/ext/intersphinx.py	Mon Aug  4 19:31:25 2008
@@ -0,0 +1,141 @@
+# -*- coding: utf-8 -*-
+"""
+    sphinx.ext.intersphinx
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    Insert links to Python objects documented in remote Sphinx documentation.
+
+    This works as follows:
+
+    * Each Sphinx HTML build creates a file named "objects.inv" that contains
+      a mapping from Python identifiers to URIs relative to the HTML set's root.
+
+    * Projects using the Intersphinx extension can specify links to such mapping
+      files in the `intersphinx_mapping` config value.  The mapping will then be
+      used to resolve otherwise missing references to Python objects into links
+      to the other documentation.
+
+    * By default, the mapping file is assumed to be at the same location as the
+      rest of the documentation; however, the location of the mapping file can
+      also be specified individually, e.g. if the docs should be buildable
+      without Internet access.
+
+    :copyright: 2008 by Georg Brandl.
+    :license: BSD.
+"""
+
+import time
+import urllib
+import posixpath
+from os import path
+
+from docutils import nodes
+
+from sphinx.builder import INVENTORY_FILENAME
+
+
+def fetch_inventory(app, uri, inv):
+    """Fetch, parse and return an intersphinx inventory file."""
+    invdata = {}
+    # both *uri* (base URI of the links to generate) and *inv* (actual
+    # location of the inventory file) can be local or remote URIs
+    localuri = uri.find('://') == -1
+    try:
+        if inv.find('://') != -1:
+            f = urllib.urlopen(inv)
+        else:
+            f = open(path.join(app.srcdir, inv))
+    except Exception, err:
+        app.warn('intersphinx inventory %r not fetchable due to '
+                 '%s: %s' % (inv, err.__class__, err))
+        return
+    try:
+        line = f.next()
+        if line.rstrip() != '# Sphinx inventory version 1':
+            raise ValueError('unknown or unsupported inventory version')
+        line = f.next()
+        projname = line.rstrip()[11:].decode('utf-8')
+        line = f.next()
+        version = line.rstrip()[11:]
+        for line in f:
+            name, type, location = line.rstrip().split(None, 2)
+            if localuri:
+                location = path.join(uri, location)
+            else:
+                location = posixpath.join(uri, location)
+            invdata[name] = (type, projname, version, location)
+        f.close()
+    except Exception, err:
+        app.warn('intersphinx inventory %r not readable due to '
+                 '%s: %s' % (inv, err.__class__, err))
+    else:
+        return invdata
+
+
+def load_mappings(app):
+    """Load all intersphinx mappings into the environment."""
+    now = int(time.time())
+    cache_time = now - app.config.intersphinx_cache_limit * 86400
+    env = app.builder.env
+    if not hasattr(env, 'intersphinx_cache'):
+        env.intersphinx_cache = {}
+    cache = env.intersphinx_cache
+    update = False
+    for uri, inv in app.config.intersphinx_mapping.iteritems():
+        # we can safely assume that the uri<->inv mapping is not changed
+        # during partial rebuilds since a changed intersphinx_mapping
+        # setting will cause a full environment reread
+        if not inv:
+            inv = posixpath.join(uri, INVENTORY_FILENAME)
+        # decide whether the inventory must be read: always read local
+        # files; remote ones only if the cache time is expired
+        if '://' not in inv or uri not in cache \
+               or cache[uri][0] < cache_time:
+            invdata = fetch_inventory(app, uri, inv)
+            cache[uri] = (now, invdata)
+            update = True
+    if update:
+        env.intersphinx_inventory = {}
+        for _, invdata in cache.itervalues():
+            if invdata:
+                env.intersphinx_inventory.update(invdata)
+
+
+def missing_reference(app, env, node, contnode):
+    """Attempt to resolve a missing reference via intersphinx references."""
+    type = node['reftype']
+    target = node['reftarget']
+    if type == 'mod':
+        type, proj, version, uri = env.intersphinx_inventory.get(target,
+                                                                 ('','','',''))
+        if type != 'mod':
+            return None
+        target = 'module-' + target   # for link anchor
+    else:
+        if target[-2:] == '()':
+            target = target[:-2]
+        target = target.rstrip(' *')
+        # special case: exceptions and object methods
+        if type == 'exc' and '.' not in target and \
+           'exceptions.' + target in env.intersphinx_inventory:
+            target = 'exceptions.' + target
+        elif type in ('func', 'meth') and '.' not in target and \
+           'object.' + target in env.intersphinx_inventory:
+            target = 'object.' + target
+        if target not in env.intersphinx_inventory:
+            return None
+        type, proj, version, uri = env.intersphinx_inventory[target]
+    print "Intersphinx hit:", target, uri
+    newnode = nodes.reference('', '')
+    newnode['refuri'] = uri + '#' + target
+    newnode['reftitle'] = '(in %s v%s)' % (proj, version)
+    newnode['class'] = 'external-xref'
+    newnode.append(contnode)
+    return newnode
+
+
+def setup(app):
+    app.add_config_value('intersphinx_mapping', {}, True)
+    app.add_config_value('intersphinx_cache_limit', 5, False)
+    app.connect('missing-reference', missing_reference)
+    app.connect('builder-inited', load_mappings)


More information about the Python-checkins mailing list