Add intersphinx extension.

This commit is contained in:
Georg Brandl 2008-08-04 17:31:25 +00:00
parent b4f71aa642
commit b4c78df0a2
5 changed files with 214 additions and 1 deletions

View File

@ -4,6 +4,10 @@ Release 0.5 (in development)
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

67
doc/ext/intersphinx.rst Normal file
View File

@ -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.

View File

@ -34,6 +34,7 @@ These extensions are built in and can be activated by respective entries in the
ext/autodoc
ext/doctest
ext/intersphinx
ext/refcounting
ext/ifconfig
ext/coverage

View File

@ -40,7 +40,7 @@ from sphinx import directives
ENV_PICKLE_FILENAME = 'environment.pickle'
LAST_BUILD_FILENAME = 'last_build'
INVENTORY_FILENAME = 'inventory.txt'
INVENTORY_FILENAME = 'objects.inv'
class Builder(object):

141
sphinx/ext/intersphinx.py Normal file
View File

@ -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)