merge with trunk

This commit is contained in:
Georg Brandl 2010-01-02 21:03:56 +01:00
commit faeec5cbbb
67 changed files with 3903 additions and 2229 deletions

View File

@ -1,13 +1,14 @@
.*\.pyc .*\.pyc
.*\.egg .*\.egg
.*\.so .*\.so
build/ .dir-locals.el
dist/
tests/.coverage
sphinx/pycode/Grammar.*pickle
Sphinx.egg-info/
doc/_build/
TAGS
\.ropeproject/
^env/
\.DS_Store$ \.DS_Store$
^build/
^dist/
^tests/.coverage
^sphinx/pycode/Grammar.*pickle
^Sphinx.egg-info/
^doc/_build/
^TAGS
^\.ropeproject/
^env/

11
CHANGES
View File

@ -1,6 +1,12 @@
Release 1.0 (in development) Release 1.0 (in development)
============================ ============================
.. XXX add short info about domains
* Support for domains has been added.
* Support for docutils 0.4 has been removed.
* Added Epub builder. * Added Epub builder.
* #284: All docinfo metadata is now put into the document metadata, not * #284: All docinfo metadata is now put into the document metadata, not
@ -8,8 +14,13 @@ Release 1.0 (in development)
* Added new HTML theme ``agogo``, created by Andi Albrecht. * Added new HTML theme ``agogo``, created by Andi Albrecht.
* The ``toctree()`` callable in templates now has a ``maxdepth``
keyword argument to control the depth of the generated tree.
* Added new minimal theme called scrolls, created by Armin Ronacher. * Added new minimal theme called scrolls, created by Armin Ronacher.
* Added Catalan translation, thanks to Pau Fernández.
* Added ``html_output_encoding`` config value. * Added ``html_output_encoding`` config value.
* #200: Added ``Sphinx.add_stylesheet()``. * #200: Added ``Sphinx.add_stylesheet()``.

View File

@ -9,7 +9,7 @@ all: clean-pyc check test
check: check:
@$(PYTHON) utils/check_sources.py -i build -i dist -i sphinx/style/jquery.js \ @$(PYTHON) utils/check_sources.py -i build -i dist -i sphinx/style/jquery.js \
-i sphinx/pycode/pgen2 -i sphinx/util/smartypants.py \ -i sphinx/pycode/pgen2 -i sphinx/util/smartypants.py \
-i doc/_build -i ez_setup.py -i tests/path.py -i tests/coverage.py . -i doc/_build -i ez_setup.py -i tests/path.py -i tests/coverage.py -i env .
clean: clean-pyc clean-patchfiles clean: clean-pyc clean-patchfiles

View File

@ -166,7 +166,7 @@ The special document names (and pages generated for them) are:
respectively. respectively.
The general index is populated with entries from modules, all index-generating The general index is populated with entries from modules, all index-generating
:ref:`description units <desc-units>`, and from :dir:`index` directives. :ref:`object descriptions <desc-units>`, and from :dir:`index` directives.
The module index contains one entry per :dir:`module` directive. The module index contains one entry per :dir:`module` directive.

View File

@ -143,5 +143,6 @@ def setup(app):
parse_directive) parse_directive)
app.add_description_unit('role', 'role', 'pair: %s; role', parse_role) app.add_description_unit('role', 'role', 'pair: %s; role', parse_role)
app.add_description_unit('confval', 'confval', app.add_description_unit('confval', 'confval',
'pair: %s; configuration value') objname='configuration value',
indextemplate='pair: %s; configuration value')
app.add_description_unit('event', 'event', 'pair: %s; event', parse_event) app.add_description_unit('event', 'event', 'pair: %s; event', parse_event)

View File

@ -240,6 +240,7 @@ Project information
Currently supported languages are: Currently supported languages are:
* ``ca`` -- Catalan
* ``cs`` -- Czech * ``cs`` -- Czech
* ``de`` -- German * ``de`` -- German
* ``en`` -- English * ``en`` -- English
@ -297,8 +298,8 @@ Project information
.. confval:: add_module_names .. confval:: add_module_names
A boolean that decides whether module names are prepended to all A boolean that decides whether module names are prepended to all
:term:`description unit` titles, e.g. for :dir:`function` directives. :term:`object` names (for object types where a "module" of some kind is
Default is ``True``. defined), e.g. for :dir:`function` directives. Default is ``True``.
.. confval:: show_authors .. confval:: show_authors

View File

@ -123,26 +123,28 @@ the following public API:
.. versionadded:: 0.6 .. versionadded:: 0.6
.. method:: Sphinx.add_description_unit(directivename, rolename, indextemplate='', parse_node=None, ref_nodeclass=None) .. method:: Sphinx.add_object_type(directivename, rolename, indextemplate='', parse_node=None, ref_nodeclass=None, objname='')
This method is a very convenient way to add a new type of information that This method is a very convenient way to add a new :term:`object` type that
can be cross-referenced. It will do this: can be cross-referenced. It will do this:
* Create a new directive (called *directivename*) for a :term:`description * Create a new directive (called *directivename*) for documenting an object.
unit`. It will automatically add index entries if *indextemplate* is It will automatically add index entries if *indextemplate* is nonempty; if
nonempty; if given, it must contain exactly one instance of ``%s``. See given, it must contain exactly one instance of ``%s``. See the example
the example below for how the template will be interpreted. below for how the template will be interpreted.
* Create a new role (called *rolename*) to cross-reference to these * Create a new role (called *rolename*) to cross-reference to these
description units. object descriptions.
* If you provide *parse_node*, it must be a function that takes a string and * If you provide *parse_node*, it must be a function that takes a string and
a docutils node, and it must populate the node with children parsed from a docutils node, and it must populate the node with children parsed from
the string. It must then return the name of the item to be used in the string. It must then return the name of the item to be used in
cross-referencing and index entries. See the :file:`ext.py` file in the cross-referencing and index entries. See the :file:`ext.py` file in the
source for this documentation for an example. source for this documentation for an example.
* The *objname* (if not given, will default to *directivename*) names the
type of object. It is used when listing objects, e.g. in search results.
For example, if you have this call in a custom Sphinx extension:: For example, if you have this call in a custom Sphinx extension::
app.add_description_unit('directive', 'dir', 'pair: %s; directive') app.add_object_type('directive', 'dir', 'pair: %s; directive')
you can use this markup in your documents:: you can use this markup in your documents::
@ -164,12 +166,15 @@ the following public API:
``docutils.nodes.emphasis`` or ``docutils.nodes.strong`` -- you can also use ``docutils.nodes.emphasis`` or ``docutils.nodes.strong`` -- you can also use
``docutils.nodes.generated`` if you want no further text decoration). ``docutils.nodes.generated`` if you want no further text decoration).
For the role content, you have the same options as for standard Sphinx roles For the role content, you have the same syntactical possibilities as for
(see :ref:`xref-syntax`). standard Sphinx roles (see :ref:`xref-syntax`).
.. method:: Sphinx.add_crossref_type(directivename, rolename, indextemplate='', ref_nodeclass=None) This method is also available under the deprecated alias
:meth:`add_description_unit`.
This method is very similar to :meth:`add_description_unit` except that the .. method:: Sphinx.add_crossref_type(directivename, rolename, indextemplate='', ref_nodeclass=None, objname='')
This method is very similar to :meth:`add_object_type` except that the
directive it generates must be empty, and will produce no output. directive it generates must be empty, and will produce no output.
That means that you can add semantic targets to your sources, and refer to That means that you can add semantic targets to your sources, and refer to

View File

@ -13,7 +13,6 @@ Writing new builders
These methods are predefined and will be called from the application: These methods are predefined and will be called from the application:
.. automethod:: load_env
.. automethod:: get_relative_uri .. automethod:: get_relative_uri
.. automethod:: build_all .. automethod:: build_all
.. automethod:: build_specific .. automethod:: build_specific

View File

@ -201,8 +201,7 @@ The ``todo`` directive function looks like this::
def run(self): def run(self):
env = self.state.document.settings.env env = self.state.document.settings.env
targetid = "todo-%s" % env.index_num targetid = "todo-%s" % env.new_serialno('todo')
env.index_num += 1
targetnode = nodes.target('', '', ids=[targetid]) targetnode = nodes.target('', '', ids=[targetid])
ad = make_admonition(todo, self.name, [_('Todo')], self.options, ad = make_admonition(todo, self.name, [_('Todo')], self.options,
@ -225,9 +224,10 @@ to the build environment instance using ``self.state.document.settings.env``.
Then, to act as a link target (from the todolist), the todo directive needs to Then, to act as a link target (from the todolist), the todo directive needs to
return a target node in addition to the todo node. The target ID (in HTML, this return a target node in addition to the todo node. The target ID (in HTML, this
will be the anchor name) is generated by using ``env.index_num`` which is will be the anchor name) is generated by using ``env.new_serialno`` which is
persistent between directive calls and therefore leads to unique target names. returns a new integer directive on each call and therefore leads to unique
The target node is instantiated without any text (the first two arguments). target names. The target node is instantiated without any text (the first two
arguments).
An admonition is created using a standard docutils function (wrapped in Sphinx An admonition is created using a standard docutils function (wrapped in Sphinx
for docutils cross-version compatibility). The first argument gives the node for docutils cross-version compatibility). The first argument gives the node

View File

@ -24,6 +24,10 @@ How do I...
... add global substitutions or includes? ... add global substitutions or includes?
Add them in the :confval:`rst_epilog` config value. Add them in the :confval:`rst_epilog` config value.
... display the whole TOC tree in the sidebar?
Use the :data:`toctree` callable in a custom layout template, probably in the
``sidebartoc`` block.
... write my own extension? ... write my own extension?
See the :ref:`extension tutorial <exttut>`. See the :ref:`extension tutorial <exttut>`.
@ -48,6 +52,12 @@ SCons
Glenn Hutchings has written a SCons build script to build Sphinx Glenn Hutchings has written a SCons build script to build Sphinx
documentation; it is hosted here: http://bitbucket.org/zondo/sphinx-scons documentation; it is hosted here: http://bitbucket.org/zondo/sphinx-scons
PyPI
Jannis Leidel wrote a `setuptools command
<http://pypi.python.org/pypi/Sphinx-PyPI-upload>`_ that automatically uploads
Sphinx documentation to the PyPI package documentation area at
http://packages.python.org/.
github pages github pages
You can use `Michael Jones' sphinx-to-github tool You can use `Michael Jones' sphinx-to-github tool
<http://github.com/michaeljones/sphinx-to-github/tree/master>`_ to prepare <http://github.com/michaeljones/sphinx-to-github/tree/master>`_ to prepare

View File

@ -19,17 +19,17 @@ Glossary
the :term:`source directory`, but can be set differently with the **-c** the :term:`source directory`, but can be set differently with the **-c**
command-line option. command-line option.
description unit
The basic building block of Sphinx documentation. Every "description
directive" (e.g. :dir:`function` or :dir:`describe`) creates such a unit;
and most units can be cross-referenced to.
environment environment
A structure where information about all documents under the root is saved, A structure where information about all documents under the root is saved,
and used for cross-referencing. The environment is pickled after the and used for cross-referencing. The environment is pickled after the
parsing stage, so that successive runs only need to read and parse new and parsing stage, so that successive runs only need to read and parse new and
changed documents. changed documents.
object
The basic building block of Sphinx documentation. Every "object
directive" (e.g. :dir:`function` or :dir:`object`) creates such a block;
and most objects can be cross-referenced to.
source directory source directory
The directory which, including its subdirectories, contains all source The directory which, including its subdirectories, contains all source
files for one Sphinx project. files for one Sphinx project.

View File

@ -67,10 +67,12 @@ The directives you can use for module declarations are:
.. _desc-units: .. _desc-units:
Object description units Object descriptions
------------------------ -------------------
There are a number of directives used to describe specific features provided by .. XXX generalize for domains
There are a number of directives used to describe specific objects provided by
modules. Each directive requires one or more signatures to provide basic modules. Each directive requires one or more signatures to provide basic
information about what is being described, and the content should be the information about what is being described, and the content should be the
description. The basic version makes entries in the general index; if no index description. The basic version makes entries in the general index; if no index
@ -92,6 +94,8 @@ index entries more informative.
The directives are: The directives are:
.. XXX update this
.. directive:: .. cfunction:: type name(signature) .. directive:: .. cfunction:: type name(signature)
Describes a C function. The signature should be given as in C, e.g.:: Describes a C function. The signature should be given as in C, e.g.::
@ -119,8 +123,8 @@ The directives are:
Describes a "simple" C macro. Simple macros are macros which are used Describes a "simple" C macro. Simple macros are macros which are used
for code expansion, but which do not take arguments so cannot be described as for code expansion, but which do not take arguments so cannot be described as
functions. This is not to be used for simple constant definitions. Examples functions. This is not to be used for simple constant definitions. Examples
of its use in the Python documentation include :cmacro:`PyObject_HEAD` and of its use in the Python documentation include :c:macro:`PyObject_HEAD` and
:cmacro:`Py_BEGIN_ALLOW_THREADS`. :c:macro:`Py_BEGIN_ALLOW_THREADS`.
.. directive:: .. ctype:: name .. directive:: .. ctype:: name
@ -241,7 +245,7 @@ Info field lists
.. versionadded:: 0.4 .. versionadded:: 0.4
Inside description unit directives, reST field lists with these fields are Inside object description directives, reST field lists with these fields are
recognized and formatted nicely: recognized and formatted nicely:
* ``param``, ``parameter``, ``arg``, ``argument``, ``key``, ``keyword``: * ``param``, ``parameter``, ``arg``, ``argument``, ``key``, ``keyword``:
@ -338,8 +342,8 @@ There is a set of directives allowing documenting command-line programs:
.. versionadded:: 0.5 .. versionadded:: 0.5
Custom description units Custom object types
~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
There is also a generic version of these directives: There is also a generic version of these directives:
@ -354,4 +358,4 @@ There is also a generic version of these directives:
Describes a Python bytecode instruction. Describes a Python bytecode instruction.
Extensions may add more directives like that, using the Extensions may add more directives like that, using the
:func:`~sphinx.application.Sphinx.add_description_unit` method. :func:`~sphinx.application.Sphinx.add_object_type` method.

View File

@ -364,5 +364,10 @@ are in HTML form), these variables are also available:
.. data:: toctree .. data:: toctree
A callable yielding the global TOC tree containing the current page, rendered A callable yielding the global TOC tree containing the current page, rendered
as HTML bullet lists. If the optional keyword argument ``collapse`` is true, as HTML bullet lists. Optional keyword arguments:
all TOC entries that are not ancestors of the current page are collapsed.
* ``collapse`` (true by default): if true, all TOC entries that are not
ancestors of the current page are collapsed
* ``maxdepth`` (defaults to the max depth selected in the toctree directive):
the maximum depth of the tree; set it to ``-1`` to allow unlimited depth

View File

@ -8,6 +8,7 @@ release = egg_info -RDb ''
[extract_messages] [extract_messages]
mapping_file = babel.cfg mapping_file = babel.cfg
output_file = sphinx/locale/sphinx.pot output_file = sphinx/locale/sphinx.pot
keywords = _ l_ lazy_gettext
[update_catalog] [update_catalog]
input_file = sphinx/locale/sphinx.pot input_file = sphinx/locale/sphinx.pot

View File

@ -12,7 +12,7 @@
import sys import sys
from os import path from os import path
__version__ = '1.0' __version__ = '1.0pre'
__released__ = '1.0 (hg)' # used when Sphinx builds its own docs __released__ = '1.0 (hg)' # used when Sphinx builds its own docs
package_dir = path.abspath(path.dirname(__file__)) package_dir = path.abspath(path.dirname(__file__))

View File

@ -14,7 +14,7 @@ from docutils import nodes
# index markup # index markup
class index(nodes.Invisible, nodes.Inline, nodes.TextElement): pass class index(nodes.Invisible, nodes.Inline, nodes.TextElement): pass
# description units (classdesc, funcdesc etc.) # domain-specific object descriptions (class, function etc.)
# parent node for signature and content # parent node for signature and content
class desc(nodes.Admonition, nodes.Element): pass class desc(nodes.Admonition, nodes.Element): pass

View File

@ -14,21 +14,24 @@
import sys import sys
import types import types
import posixpath import posixpath
from os import path
from cStringIO import StringIO from cStringIO import StringIO
from docutils import nodes from docutils import nodes
from docutils.parsers.rst import directives, roles from docutils.parsers.rst import Directive, convert_directive_function, \
directives, roles
import sphinx import sphinx
from sphinx.roles import xfileref_role, innernodetypes from sphinx import package_dir, locale
from sphinx.roles import XRefRole
from sphinx.config import Config from sphinx.config import Config
from sphinx.errors import SphinxError, SphinxWarning, ExtensionError from sphinx.errors import SphinxError, SphinxWarning, ExtensionError
from sphinx.domains import ObjType, all_domains
from sphinx.domains.std import GenericObject, Target, StandardDomain
from sphinx.builders import BUILTIN_BUILDERS from sphinx.builders import BUILTIN_BUILDERS
from sphinx.directives import GenericDesc, Target, additional_xref_types from sphinx.environment import BuildEnvironment, SphinxStandaloneReader
from sphinx.environment import SphinxStandaloneReader from sphinx.util import ENOENT, pycompat # pycompat imported for side-effects
from sphinx.util import pycompat # imported for side-effects
from sphinx.util.tags import Tags from sphinx.util.tags import Tags
from sphinx.util.compat import Directive, directive_dwim
from sphinx.util.console import bold from sphinx.util.console import bold
@ -49,6 +52,7 @@ events = {
} }
CONFIG_FILENAME = 'conf.py' CONFIG_FILENAME = 'conf.py'
ENV_PICKLE_FILENAME = 'environment.pickle'
class Sphinx(object): class Sphinx(object):
@ -61,6 +65,7 @@ class Sphinx(object):
self._listeners = {} self._listeners = {}
self.builderclasses = BUILTIN_BUILDERS.copy() self.builderclasses = BUILTIN_BUILDERS.copy()
self.builder = None self.builder = None
self.env = None
self.srcdir = srcdir self.srcdir = srcdir
self.confdir = confdir self.confdir = confdir
@ -103,8 +108,62 @@ class Sphinx(object):
# now that we know all config values, collect them from conf.py # now that we know all config values, collect them from conf.py
self.config.init_values() self.config.init_values()
# set up translation infrastructure
self._init_i18n()
# set up the build environment
self._init_env(freshenv)
# set up the builder
self._init_builder(buildername)
def _init_i18n(self):
"""
Load translated strings from the configured localedirs if
enabled in the configuration.
"""
if self.config.language is not None:
self.info(bold('loading translations [%s]... ' %
self.config.language), nonl=True)
locale_dirs = [None, path.join(package_dir, 'locale')] + \
[path.join(self.srcdir, x) for x in self.config.locale_dirs]
else:
locale_dirs = []
self.translator, has_translation = locale.init(locale_dirs,
self.config.language)
if self.config.language is not None:
if has_translation:
self.info('done')
else:
self.info('locale not available')
def _init_env(self, freshenv):
if freshenv:
self.env = BuildEnvironment(self.srcdir, self.doctreedir,
self.config)
self.env.find_files(self.config)
for domain in all_domains.keys():
self.env.domains[domain] = all_domains[domain](self.env)
else:
try:
self.info(bold('loading pickled environment... '), nonl=True)
self.env = BuildEnvironment.frompickle(self.config,
path.join(self.doctreedir, ENV_PICKLE_FILENAME))
self.env.domains = {}
for domain in all_domains.keys():
# this can raise if the data version doesn't fit
self.env.domains[domain] = all_domains[domain](self.env)
self.info('done')
except Exception, err:
if type(err) is IOError and err.errno == ENOENT:
self.info('not yet created')
else:
self.info('failed: %s' % err)
return self._init_env(freshenv=True)
self.env.set_warnfunc(self.warn)
def _init_builder(self, buildername):
if buildername is None: if buildername is None:
print >>status, 'No builder selected, using default: html' print >>self._status, 'No builder selected, using default: html'
buildername = 'html' buildername = 'html'
if buildername not in self.builderclasses: if buildername not in self.builderclasses:
raise SphinxError('Builder name %s not registered' % buildername) raise SphinxError('Builder name %s not registered' % buildername)
@ -115,9 +174,7 @@ class Sphinx(object):
mod, cls = builderclass mod, cls = builderclass
builderclass = getattr( builderclass = getattr(
__import__('sphinx.builders.' + mod, None, None, [cls]), cls) __import__('sphinx.builders.' + mod, None, None, [cls]), cls)
self.builder = builderclass(self, freshenv=freshenv) self.builder = builderclass(self)
self.builder.tags = self.tags
self.builder.tags.add(self.builder.format)
self.emit('builder-inited') self.emit('builder-inited')
def build(self, all_files, filenames): def build(self, all_files, filenames):
@ -277,17 +334,21 @@ class Sphinx(object):
if depart: if depart:
setattr(translator, 'depart_'+node.__name__, depart) setattr(translator, 'depart_'+node.__name__, depart)
def add_directive(self, name, obj, content=None, arguments=None, **options): def _directive_helper(self, obj, content=None, arguments=None, **options):
if isinstance(obj, clstypes) and issubclass(obj, Directive): if isinstance(obj, clstypes) and issubclass(obj, Directive):
if content or arguments or options: if content or arguments or options:
raise ExtensionError('when adding directive classes, no ' raise ExtensionError('when adding directive classes, no '
'additional arguments may be given') 'additional arguments may be given')
directives.register_directive(name, directive_dwim(obj)) return obj
else: else:
obj.content = content obj.content = content
obj.arguments = arguments obj.arguments = arguments or (0, 0, False)
obj.options = options obj.options = options
directives.register_directive(name, obj) return convert_directive_function(obj)
def add_directive(self, name, obj, content=None, arguments=None, **options):
directives.register_directive(
name, self._directive_helper(obj, content, arguments, **options))
def add_role(self, name, role): def add_role(self, name, role):
roles.register_local_role(name, role) roles.register_local_role(name, role)
@ -298,23 +359,52 @@ class Sphinx(object):
role = roles.GenericRole(name, nodeclass) role = roles.GenericRole(name, nodeclass)
roles.register_local_role(name, role) roles.register_local_role(name, role)
def add_description_unit(self, directivename, rolename, indextemplate='', def add_domain(self, domain):
parse_node=None, ref_nodeclass=None): # XXX needs to be documented
additional_xref_types[directivename] = (rolename, indextemplate, # XXX what about subclassing and overriding?
parse_node) if domain.name in all_domains:
directives.register_directive(directivename, raise ExtensionError('domain %s already registered' % domain.name)
directive_dwim(GenericDesc)) all_domains[domain.name] = domain
roles.register_local_role(rolename, xfileref_role)
if ref_nodeclass is not None: def add_directive_to_domain(self, domain, name, obj,
innernodetypes[rolename] = ref_nodeclass content=None, arguments=None, **options):
# XXX needs to be documented
if domain not in all_domains:
raise ExtensionError('domain %s not yet registered' % domain)
all_domains[domain].directives[name] = \
self._directive_helper(obj, content, arguments, **options)
def add_role_to_domain(self, domain, name, role):
# XXX needs to be documented
if domain not in all_domains:
raise ExtensionError('domain %s not yet registered' % domain)
all_domains[domain].roles[name] = role
def add_object_type(self, directivename, rolename, indextemplate='',
parse_node=None, ref_nodeclass=None, objname=''):
StandardDomain.object_types[directivename] = \
ObjType(objname or directivename, rolename)
# create a subclass of GenericObject as the new directive
new_directive = type(directivename, (GenericObject, object),
{'indextemplate': indextemplate,
'parse_node': staticmethod(parse_node)})
StandardDomain.directives[directivename] = new_directive
# XXX support more options?
StandardDomain.roles[rolename] = XRefRole(innernodeclass=ref_nodeclass)
# backwards compatible alias
add_description_unit = add_object_type
def add_crossref_type(self, directivename, rolename, indextemplate='', def add_crossref_type(self, directivename, rolename, indextemplate='',
ref_nodeclass=None): ref_nodeclass=None, objname=''):
additional_xref_types[directivename] = (rolename, indextemplate, None) StandardDomain.object_types[directivename] = \
directives.register_directive(directivename, directive_dwim(Target)) ObjType(objname or directivename, rolename)
roles.register_local_role(rolename, xfileref_role) # create a subclass of Target as the new directive
if ref_nodeclass is not None: new_directive = type(directivename, (Target, object),
innernodetypes[rolename] = ref_nodeclass {'indextemplate': indextemplate})
StandardDomain.directives[directivename] = new_directive
# XXX support more options?
StandardDomain.roles[rolename] = XRefRole(innernodeclass=ref_nodeclass)
def add_transform(self, transform): def add_transform(self, transform):
SphinxStandaloneReader.transforms.append(transform) SphinxStandaloneReader.transforms.append(transform)

View File

@ -15,9 +15,7 @@ from os import path
from docutils import nodes from docutils import nodes
from sphinx import package_dir, locale from sphinx.util import SEP, relative_uri
from sphinx.util import SEP, ENOENT, relative_uri
from sphinx.environment import BuildEnvironment
from sphinx.util.console import bold, purple, darkgreen, term_width_line from sphinx.util.console import bold, purple, darkgreen, term_width_line
# side effect: registers roles and directives # side effect: registers roles and directives
@ -25,9 +23,6 @@ from sphinx import roles
from sphinx import directives from sphinx import directives
ENV_PICKLE_FILENAME = 'environment.pickle'
class Builder(object): class Builder(object):
""" """
Builds target formats from the reST sources. Builds target formats from the reST sources.
@ -38,7 +33,8 @@ class Builder(object):
# builder's output format, or '' if no document output is produced # builder's output format, or '' if no document output is produced
format = '' format = ''
def __init__(self, app, env=None, freshenv=False): def __init__(self, app):
self.env = app.env
self.srcdir = app.srcdir self.srcdir = app.srcdir
self.confdir = app.confdir self.confdir = app.confdir
self.outdir = app.outdir self.outdir = app.outdir
@ -50,21 +46,15 @@ class Builder(object):
self.warn = app.warn self.warn = app.warn
self.info = app.info self.info = app.info
self.config = app.config self.config = app.config
self.tags = app.tags
self.load_i18n() self.tags.add(self.format)
# images that need to be copied over (source -> dest) # images that need to be copied over (source -> dest)
self.images = {} self.images = {}
# if None, this is set in load_env()
self.env = env
self.freshenv = freshenv
self.init() self.init()
self.load_env()
# helper methods # helper methods
def init(self): def init(self):
""" """
Load necessary templates and perform initialization. The default Load necessary templates and perform initialization. The default
@ -167,62 +157,6 @@ class Builder(object):
# build methods # build methods
def load_i18n(self):
"""
Load translated strings from the configured localedirs if
enabled in the configuration.
"""
self.translator = None
if self.config.language is not None:
self.info(bold('loading translations [%s]... ' %
self.config.language), nonl=True)
# the None entry is the system's default locale path
locale_dirs = [None, path.join(package_dir, 'locale')] + \
[path.join(self.srcdir, x) for x in self.config.locale_dirs]
for dir_ in locale_dirs:
try:
trans = gettext.translation('sphinx', localedir=dir_,
languages=[self.config.language])
if self.translator is None:
self.translator = trans
else:
self.translator._catalog.update(trans._catalog)
except Exception:
# Language couldn't be found in the specified path
pass
if self.translator is not None:
self.info('done')
else:
self.info('locale not available')
if self.translator is None:
self.translator = gettext.NullTranslations()
self.translator.install(unicode=True)
locale.init() # translate common labels
def load_env(self):
"""Set up the build environment."""
if self.env:
return
if not self.freshenv:
try:
self.info(bold('loading pickled environment... '), nonl=True)
self.env = BuildEnvironment.frompickle(self.config,
path.join(self.doctreedir, ENV_PICKLE_FILENAME))
self.info('done')
except Exception, err:
if type(err) is IOError and err.errno == ENOENT:
self.info('not found')
else:
self.info('failed: %s' % err)
self.env = BuildEnvironment(self.srcdir, self.doctreedir,
self.config)
self.env.find_files(self.config)
else:
self.env = BuildEnvironment(self.srcdir, self.doctreedir,
self.config)
self.env.find_files(self.config)
self.env.set_warnfunc(self.warn)
def build_all(self): def build_all(self):
"""Build all source files.""" """Build all source files."""
self.build(None, summary='all source files', method='all') self.build(None, summary='all source files', method='all')
@ -302,6 +236,7 @@ class Builder(object):
if updated_docnames: if updated_docnames:
# save the environment # save the environment
from sphinx.application import ENV_PICKLE_FILENAME
self.info(bold('pickling environment... '), 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')

View File

@ -12,6 +12,7 @@
""" """
import os import os
import re
import cgi import cgi
import sys import sys
from os import path from os import path
@ -30,7 +31,7 @@ except ImportError:
try: try:
import elementtree.ElementTree as etree import elementtree.ElementTree as etree
except ImportError: except ImportError:
import cElementTree.ElemenTree as etree import cElementTree as etree
try: try:
import gzip import gzip
@ -114,11 +115,14 @@ class DevhelpBuilder(StandaloneHTMLBuilder):
else: else:
for i, ref in enumerate(refs): for i, ref in enumerate(refs):
etree.SubElement(functions, 'function', etree.SubElement(functions, 'function',
name="%s [%d]" % (title, i), link=ref) name="[%d] %s" % (i, title),
link=ref)
if subitems: if subitems:
parent_title = re.sub(r'\s*\(.*\)\s*$', '', title)
for subitem in subitems: for subitem in subitems:
write_index(subitem[0], subitem[1], []) write_index("%s %s" % (parent_title, subitem[0]),
subitem[1], [])
for (key, group) in index: for (key, group) in index:
for title, (refs, subitems) in group: for title, (refs, subitems) in group:

View File

@ -10,6 +10,7 @@
""" """
import os import os
import zlib
import codecs import codecs
import posixpath import posixpath
import cPickle as pickle import cPickle as pickle
@ -33,7 +34,8 @@ from sphinx.util import SEP, os_path, relative_uri, ensuredir, \
from sphinx.errors import SphinxError from sphinx.errors import SphinxError
from sphinx.search import js_index from sphinx.search import js_index
from sphinx.theming import Theme from sphinx.theming import Theme
from sphinx.builders import Builder, ENV_PICKLE_FILENAME from sphinx.builders import Builder
from sphinx.application import ENV_PICKLE_FILENAME
from sphinx.highlighting import PygmentsBridge from sphinx.highlighting import PygmentsBridge
from sphinx.util.console import bold from sphinx.util.console import bold
from sphinx.writers.html import HTMLWriter, HTMLTranslator, \ from sphinx.writers.html import HTMLWriter, HTMLTranslator, \
@ -243,7 +245,9 @@ class StandaloneHTMLBuilder(Builder):
rellinks = [] rellinks = []
if self.config.html_use_index: if self.config.html_use_index:
rellinks.append(('genindex', _('General Index'), 'I', _('index'))) rellinks.append(('genindex', _('General Index'), 'I', _('index')))
if self.config.html_use_modindex and self.env.modules: # XXX generalization of modindex?
if self.config.html_use_modindex and \
self.env.domaindata['py']['modules']:
rellinks.append(('modindex', _('Global Module Index'), rellinks.append(('modindex', _('Global Module Index'),
'M', _('modules'))) 'M', _('modules')))
@ -407,12 +411,13 @@ class StandaloneHTMLBuilder(Builder):
# the global module index # the global module index
if self.config.html_use_modindex and self.env.modules: moduleindex = self.env.domaindata['py']['modules']
if self.config.html_use_modindex and moduleindex:
# the sorted list of all modules, for the global module index # the sorted list of all modules, for the global module index
modules = sorted(((mn, (self.get_relative_uri('modindex', fn) + modules = sorted(((mn, (self.get_relative_uri('modindex', fn) +
'#module-' + mn, sy, pl, dep)) '#module-' + mn, sy, pl, dep))
for (mn, (fn, sy, pl, dep)) in for (mn, (fn, sy, pl, dep)) in
self.env.modules.iteritems()), moduleindex.iteritems()),
key=lambda x: x[0].lower()) key=lambda x: x[0].lower())
# collect all platforms # collect all platforms
platforms = set() platforms = set()
@ -642,7 +647,7 @@ class StandaloneHTMLBuilder(Builder):
if self.indexer is not None and title: if self.indexer is not None and title:
self.indexer.feed(pagename, title, doctree) self.indexer.feed(pagename, title, doctree)
def _get_local_toctree(self, docname, collapse=True): def _get_local_toctree(self, docname, collapse=True, maxdepth=0):
return self.render_partial(self.env.get_toctree_for( return self.render_partial(self.env.get_toctree_for(
docname, self, collapse))['fragment'] docname, self, collapse))['fragment']
@ -711,16 +716,22 @@ class StandaloneHTMLBuilder(Builder):
self.info('done') self.info('done')
self.info(bold('dumping object inventory... '), nonl=True) self.info(bold('dumping object inventory... '), nonl=True)
f = open(path.join(self.outdir, INVENTORY_FILENAME), 'w') f = open(path.join(self.outdir, INVENTORY_FILENAME), 'wb')
try: try:
f.write('# Sphinx inventory version 1\n') f.write('# Sphinx inventory version 2\n')
f.write('# Project: %s\n' % self.config.project.encode('utf-8')) f.write('# Project: %s\n' % self.config.project.encode('utf-8'))
f.write('# Version: %s\n' % self.config.version) f.write('# Version: %s\n' % self.config.version)
for modname, info in self.env.modules.iteritems(): f.write('# The remainder of this file is compressed using zlib.\n')
f.write('%s mod %s\n' % (modname, self.get_target_uri(info[0]))) compressor = zlib.compressobj(9)
for refname, (docname, desctype) in self.env.descrefs.iteritems(): for domainname, domain in self.env.domains.iteritems():
f.write('%s %s %s\n' % (refname, desctype, for name, type, docname, anchor, prio in domain.get_objects():
self.get_target_uri(docname))) if anchor.endswith(name):
# this can shorten the inventory by as much as 25%
anchor = anchor[:-len(name)] + '$'
f.write(compressor.compress(
'%s %s:%s %s %s\n' % (name, domainname, type, prio,
self.get_target_uri(docname) + '#' + anchor)))
f.write(compressor.flush())
finally: finally:
f.close() f.close()
self.info('done') self.info('done')

View File

@ -55,6 +55,7 @@ class Config(object):
modindex_common_prefix = ([], 'html'), modindex_common_prefix = ([], 'html'),
rst_epilog = (None, 'env'), rst_epilog = (None, 'env'),
trim_doctest_flags = (True, 'env'), trim_doctest_flags = (True, 'env'),
default_domain = ('py', 'env'),
# HTML options # HTML options
html_theme = ('default', 'html'), html_theme = ('default', 'html'),
@ -127,6 +128,10 @@ class Config(object):
latex_docclass = ({}, None), latex_docclass = ({}, None),
# now deprecated - use latex_elements # now deprecated - use latex_elements
latex_preamble = ('', None), latex_preamble = ('', None),
# text options
text_sectionchars = ('*=-~"+`', 'text'),
text_windows_newlines = (False, 'text'),
) )
def __init__(self, dirname, filename, overrides, tags): def __init__(self, dirname, filename, overrides, tags):

View File

@ -9,11 +9,16 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from docutils.parsers.rst import directives import re
from docutils import nodes
from docutils.parsers.rst import Directive, directives
from docutils.parsers.rst.directives import images from docutils.parsers.rst.directives import images
from sphinx import addnodes
from sphinx.locale import l_
# import and register directives # import and register directives
from sphinx.directives.desc import *
from sphinx.directives.code import * from sphinx.directives.code import *
from sphinx.directives.other import * from sphinx.directives.other import *
@ -25,3 +30,263 @@ try:
except AttributeError: except AttributeError:
images.figure.options['figwidth'] = \ images.figure.options['figwidth'] = \
directives.length_or_percentage_or_unitless directives.length_or_percentage_or_unitless
def _is_only_paragraph(node):
"""True if the node only contains one paragraph (and system messages)."""
if len(node) == 0:
return False
elif len(node) > 1:
for subnode in node[1:]:
if not isinstance(subnode, nodes.system_message):
return False
if isinstance(node[0], nodes.paragraph):
return True
return False
# RE to strip backslash escapes
strip_backslash_re = re.compile(r'\\(?=[^\\])')
class ObjectDescription(Directive):
"""
Directive to describe a class, function or similar object. Not used
directly, but subclassed to add custom behavior.
"""
has_content = True
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {
'noindex': directives.flag,
'module': directives.unchanged,
}
# XXX make this more domain specific
doc_fields_with_arg = {
'param': '%param',
'parameter': '%param',
'arg': '%param',
'argument': '%param',
'keyword': '%param',
'kwarg': '%param',
'kwparam': '%param',
'type': '%type',
'raises': l_('Raises'),
'raise': l_('Raises'),
'exception': l_('Raises'),
'except': l_('Raises'),
'var': l_('Variable'),
'ivar': l_('Variable'),
'cvar': l_('Variable'),
'returns': l_('Returns'),
'return': l_('Returns'),
}
doc_fields_with_linked_arg = ('raises', 'raise', 'exception', 'except')
doc_fields_without_arg = {
'returns': l_('Returns'),
'return': l_('Returns'),
'rtype': l_('Return type'),
}
def handle_doc_fields(self, node):
"""
Convert field lists with known keys inside the description content into
better-looking equivalents.
"""
# don't traverse, only handle field lists that are immediate children
for child in node.children:
if not isinstance(child, nodes.field_list):
continue
params = []
pfield = None
param_nodes = {}
param_types = {}
new_list = nodes.field_list()
for field in child:
fname, fbody = field
try:
typ, obj = fname.astext().split(None, 1)
typdesc = self.doc_fields_with_arg[typ]
if _is_only_paragraph(fbody):
children = fbody.children[0].children
else:
children = fbody.children
if typdesc == '%param':
if not params:
# add the field that later gets all the parameters
pfield = nodes.field()
new_list += pfield
dlitem = nodes.list_item()
dlpar = nodes.paragraph()
dlpar += nodes.emphasis(obj, obj)
dlpar += nodes.Text(' -- ', ' -- ')
dlpar += children
param_nodes[obj] = dlpar
dlitem += dlpar
params.append(dlitem)
elif typdesc == '%type':
typenodes = fbody.children
if _is_only_paragraph(fbody):
typenodes = ([nodes.Text(' (')] +
typenodes[0].children +
[nodes.Text(')')])
param_types[obj] = typenodes
else:
fieldname = typdesc + ' '
nfield = nodes.field()
nfieldname = nodes.field_name(fieldname, fieldname)
nfield += nfieldname
node = nfieldname
if typ in self.doc_fields_with_linked_arg:
# XXX currmodule/currclass
node = addnodes.pending_xref(
obj, reftype='obj', refexplicit=False,
reftarget=obj)
#, modname=self.env.currmodule
#, classname=self.env.currclass
nfieldname += node
node += nodes.Text(obj, obj)
nfield += nodes.field_body()
nfield[1] += fbody.children
new_list += nfield
except (KeyError, ValueError):
fnametext = fname.astext()
try:
typ = self.doc_fields_without_arg[fnametext]
except KeyError:
# at least capitalize the field name
typ = fnametext.capitalize()
fname[0] = nodes.Text(typ)
new_list += field
if params:
if len(params) == 1:
pfield += nodes.field_name('', _('Parameter'))
pfield += nodes.field_body()
pfield[1] += params[0][0]
else:
pfield += nodes.field_name('', _('Parameters'))
pfield += nodes.field_body()
pfield[1] += nodes.bullet_list()
pfield[1][0].extend(params)
for param, type in param_types.iteritems():
if param in param_nodes:
param_nodes[param][1:1] = type
child.replace_self(new_list)
def get_signatures(self):
"""
Retrieve the signatures to document from the directive arguments.
"""
# remove backslashes to support (dummy) escapes; helps Vim highlighting
return [strip_backslash_re.sub('', sig.strip())
for sig in self.arguments[0].split('\n')]
def parse_signature(self, sig, signode):
"""
Parse the signature *sig* into individual nodes and append them to
*signode*. If ValueError is raised, parsing is aborted and the whole
*sig* is put into a single desc_name node.
"""
raise ValueError
def add_target_and_index(self, name, sig, signode):
"""
Add cross-reference IDs and entries to self.indexnode, if applicable.
"""
return # do nothing by default
def before_content(self):
"""
Called before parsing content. Used to set information about the current
directive context on the build environment.
"""
pass
def after_content(self):
"""
Called after parsing content. Used to reset information about the
current directive context on the build environment.
"""
pass
def run(self):
if ':' in self.name:
self.domain, self.objtype = self.name.split(':', 1)
else:
self.domain, self.objtype = '', self.name
self.env = self.state.document.settings.env
self.indexnode = addnodes.index(entries=[])
node = addnodes.desc()
node.document = self.state.document
node['domain'] = self.domain
# 'desctype' is a backwards compatible attribute
node['objtype'] = node['desctype'] = self.objtype
node['noindex'] = noindex = ('noindex' in self.options)
self.names = []
signatures = self.get_signatures()
for i, sig in enumerate(signatures):
# add a signature node for each signature in the current unit
# and add a reference target for it
signode = addnodes.desc_signature(sig, '')
signode['first'] = False
node.append(signode)
try:
# name can also be a tuple, e.g. (classname, objname)
name = self.parse_signature(sig, signode)
except ValueError, err:
# signature parsing failed
signode.clear()
signode += addnodes.desc_name(sig, sig)
continue # we don't want an index entry here
if not noindex and name not in self.names:
# only add target and index entry if this is the first
# description of the object with this name in this desc block
self.names.append(name)
self.add_target_and_index(name, sig, signode)
contentnode = addnodes.desc_content()
node.append(contentnode)
if self.names:
# needed for association of version{added,changed} directives
self.env.doc_read_data['object'] = self.names[0]
self.before_content()
self.state.nested_parse(self.content, self.content_offset, contentnode)
self.handle_doc_fields(contentnode)
self.env.doc_read_data['object'] = None
self.after_content()
return [self.indexnode, node]
# backwards compatible old name
DescDirective = ObjectDescription
class DefaultDomain(Directive):
"""
Directive to (re-)set the default domain for this source file.
"""
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
option_spec = {}
def run(self):
env = self.state.document.settings.env
domain_name = arguments[0]
env.doc_read_data['default_domain'] = env.domains.get(domain_name)
directives.register_directive('default-domain', DefaultDomain)
directives.register_directive('describe', ObjectDescription)
# new, more consistent, name
directives.register_directive('object', ObjectDescription)

View File

@ -13,11 +13,10 @@ import codecs
from os import path from os import path
from docutils import nodes from docutils import nodes
from docutils.parsers.rst import directives from docutils.parsers.rst import Directive, directives
from sphinx import addnodes from sphinx import addnodes
from sphinx.util import parselinenos from sphinx.util import parselinenos
from sphinx.util.compat import Directive, directive_dwim
class Highlight(Directive): class Highlight(Directive):
@ -184,8 +183,8 @@ class LiteralInclude(Directive):
return [retnode] return [retnode]
directives.register_directive('highlight', directive_dwim(Highlight)) directives.register_directive('highlight', Highlight)
directives.register_directive('highlightlang', directive_dwim(Highlight)) # old directives.register_directive('highlightlang', Highlight) # old
directives.register_directive('code-block', directive_dwim(CodeBlock)) directives.register_directive('code-block', CodeBlock)
directives.register_directive('sourcecode', directive_dwim(CodeBlock)) directives.register_directive('sourcecode', CodeBlock)
directives.register_directive('literalinclude', directive_dwim(LiteralInclude)) directives.register_directive('literalinclude', LiteralInclude)

View File

@ -1,771 +0,0 @@
# -*- coding: utf-8 -*-
"""
sphinx.directives.desc
~~~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
import string
from docutils import nodes
from docutils.parsers.rst import directives
from sphinx import addnodes
from sphinx.util import ws_re
from sphinx.util.compat import Directive, directive_dwim
def _is_only_paragraph(node):
"""True if the node only contains one paragraph (and system messages)."""
if len(node) == 0:
return False
elif len(node) > 1:
for subnode in node[1:]:
if not isinstance(subnode, nodes.system_message):
return False
if isinstance(node[0], nodes.paragraph):
return True
return False
# REs for Python signatures
py_sig_re = re.compile(
r'''^ ([\w.]*\.)? # class name(s)
(\w+) \s* # thing name
(?: \((.*)\) # optional: arguments
(?:\s* -> \s* (.*))? # return annotation
)? $ # and nothing more
''', re.VERBOSE)
py_paramlist_re = re.compile(r'([\[\],])') # split at '[', ']' and ','
# REs for C signatures
c_sig_re = re.compile(
r'''^([^(]*?) # return type
([\w:]+) \s* # thing name (colon allowed for C++ class names)
(?: \((.*)\) )? # optionally arguments
(\s+const)? $ # const specifier
''', re.VERBOSE)
c_funcptr_sig_re = re.compile(
r'''^([^(]+?) # return type
(\( [^()]+ \)) \s* # name in parentheses
\( (.*) \) # arguments
(\s+const)? $ # const specifier
''', re.VERBOSE)
c_funcptr_name_re = re.compile(r'^\(\s*\*\s*(.*?)\s*\)$')
# RE for option descriptions
option_desc_re = re.compile(
r'((?:/|-|--)[-_a-zA-Z0-9]+)(\s*.*?)(?=,\s+(?:/|-|--)|$)')
# RE to split at word boundaries
wsplit_re = re.compile(r'(\W+)')
# RE to strip backslash escapes
strip_backslash_re = re.compile(r'\\(?=[^\\])')
class DescDirective(Directive):
"""
Directive to describe a class, function or similar object. Not used
directly, but subclassed to add custom behavior.
"""
has_content = True
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {
'noindex': directives.flag,
'module': directives.unchanged,
}
_ = lambda x: x # make gettext extraction in constants possible
doc_fields_with_arg = {
'param': '%param',
'parameter': '%param',
'arg': '%param',
'argument': '%param',
'keyword': '%param',
'kwarg': '%param',
'kwparam': '%param',
'type': '%type',
'raises': _('Raises'),
'raise': 'Raises',
'exception': 'Raises',
'except': 'Raises',
'var': _('Variable'),
'ivar': 'Variable',
'cvar': 'Variable',
'returns': _('Returns'),
'return': 'Returns',
}
doc_fields_with_linked_arg = ('raises', 'raise', 'exception', 'except')
doc_fields_without_arg = {
'returns': 'Returns',
'return': 'Returns',
'rtype': _('Return type'),
}
def handle_doc_fields(self, node):
"""
Convert field lists with known keys inside the description content into
better-looking equivalents.
"""
# don't traverse, only handle field lists that are immediate children
for child in node.children:
if not isinstance(child, nodes.field_list):
continue
params = []
pfield = None
param_nodes = {}
param_types = {}
new_list = nodes.field_list()
for field in child:
fname, fbody = field
try:
typ, obj = fname.astext().split(None, 1)
typdesc = _(self.doc_fields_with_arg[typ])
if _is_only_paragraph(fbody):
children = fbody.children[0].children
else:
children = fbody.children
if typdesc == '%param':
if not params:
# add the field that later gets all the parameters
pfield = nodes.field()
new_list += pfield
dlitem = nodes.list_item()
dlpar = nodes.paragraph()
dlpar += nodes.emphasis(obj, obj)
dlpar += nodes.Text(' -- ', ' -- ')
dlpar += children
param_nodes[obj] = dlpar
dlitem += dlpar
params.append(dlitem)
elif typdesc == '%type':
typenodes = fbody.children
if _is_only_paragraph(fbody):
typenodes = ([nodes.Text(' (')] +
typenodes[0].children +
[nodes.Text(')')])
param_types[obj] = typenodes
else:
fieldname = typdesc + ' '
nfield = nodes.field()
nfieldname = nodes.field_name(fieldname, fieldname)
nfield += nfieldname
node = nfieldname
if typ in self.doc_fields_with_linked_arg:
node = addnodes.pending_xref(
obj, reftype='obj', refcaption=False,
reftarget=obj, modname=self.env.currmodule,
classname=self.env.currclass)
nfieldname += node
node += nodes.Text(obj, obj)
nfield += nodes.field_body()
nfield[1] += fbody.children
new_list += nfield
except (KeyError, ValueError):
fnametext = fname.astext()
try:
typ = _(self.doc_fields_without_arg[fnametext])
except KeyError:
# at least capitalize the field name
typ = fnametext.capitalize()
fname[0] = nodes.Text(typ)
new_list += field
if params:
if len(params) == 1:
pfield += nodes.field_name('', _('Parameter'))
pfield += nodes.field_body()
pfield[1] += params[0][0]
else:
pfield += nodes.field_name('', _('Parameters'))
pfield += nodes.field_body()
pfield[1] += nodes.bullet_list()
pfield[1][0].extend(params)
for param, type in param_types.iteritems():
if param in param_nodes:
param_nodes[param][1:1] = type
child.replace_self(new_list)
def get_signatures(self):
"""
Retrieve the signatures to document from the directive arguments.
"""
# remove backslashes to support (dummy) escapes; helps Vim highlighting
return [strip_backslash_re.sub('', sig.strip())
for sig in self.arguments[0].split('\n')]
def parse_signature(self, sig, signode):
"""
Parse the signature *sig* into individual nodes and append them to
*signode*. If ValueError is raised, parsing is aborted and the whole
*sig* is put into a single desc_name node.
"""
raise ValueError
def add_target_and_index(self, name, sig, signode):
"""
Add cross-reference IDs and entries to self.indexnode, if applicable.
"""
return # do nothing by default
def before_content(self):
"""
Called before parsing content. Used to set information about the current
directive context on the build environment.
"""
pass
def after_content(self):
"""
Called after parsing content. Used to reset information about the
current directive context on the build environment.
"""
pass
def run(self):
self.desctype = self.name
self.env = self.state.document.settings.env
self.indexnode = addnodes.index(entries=[])
node = addnodes.desc()
node.document = self.state.document
node['desctype'] = self.desctype
node['noindex'] = noindex = ('noindex' in self.options)
self.names = []
signatures = self.get_signatures()
for i, sig in enumerate(signatures):
# add a signature node for each signature in the current unit
# and add a reference target for it
signode = addnodes.desc_signature(sig, '')
signode['first'] = False
node.append(signode)
try:
# name can also be a tuple, e.g. (classname, objname)
name = self.parse_signature(sig, signode)
except ValueError, err:
# signature parsing failed
signode.clear()
signode += addnodes.desc_name(sig, sig)
continue # we don't want an index entry here
if not noindex and name not in self.names:
# only add target and index entry if this is the first
# description of the object with this name in this desc block
self.names.append(name)
self.add_target_and_index(name, sig, signode)
contentnode = addnodes.desc_content()
node.append(contentnode)
if self.names:
# needed for association of version{added,changed} directives
self.env.currdesc = self.names[0]
self.before_content()
self.state.nested_parse(self.content, self.content_offset, contentnode)
self.handle_doc_fields(contentnode)
self.env.currdesc = None
self.after_content()
return [self.indexnode, node]
class PythonDesc(DescDirective):
"""
Description of a general Python object.
"""
def get_signature_prefix(self, sig):
"""
May return a prefix to put before the object name in the signature.
"""
return ''
def needs_arglist(self):
"""
May return true if an empty argument list is to be generated even if
the document contains none.
"""
return False
def parse_signature(self, sig, signode):
"""
Transform a Python signature into RST nodes.
Returns (fully qualified name of the thing, classname if any).
If inside a class, the current class name is handled intelligently:
* it is stripped from the displayed name if present
* it is added to the full name (return value) if not present
"""
m = py_sig_re.match(sig)
if m is None:
raise ValueError
classname, name, arglist, retann = m.groups()
if self.env.currclass:
add_module = False
if classname and classname.startswith(self.env.currclass):
fullname = classname + name
# class name is given again in the signature
classname = classname[len(self.env.currclass):].lstrip('.')
elif classname:
# class name is given in the signature, but different
# (shouldn't happen)
fullname = self.env.currclass + '.' + classname + name
else:
# class name is not given in the signature
fullname = self.env.currclass + '.' + name
else:
add_module = True
fullname = classname and classname + name or name
prefix = self.get_signature_prefix(sig)
if prefix:
signode += addnodes.desc_annotation(prefix, prefix)
if classname:
signode += addnodes.desc_addname(classname, classname)
# exceptions are a special case, since they are documented in the
# 'exceptions' module.
elif add_module and self.env.config.add_module_names:
modname = self.options.get('module', self.env.currmodule)
if modname and modname != 'exceptions':
nodetext = modname + '.'
signode += addnodes.desc_addname(nodetext, nodetext)
signode += addnodes.desc_name(name, name)
if not arglist:
if self.needs_arglist():
# for callables, add an empty parameter list
signode += addnodes.desc_parameterlist()
if retann:
signode += addnodes.desc_returns(retann, retann)
return fullname, classname
signode += addnodes.desc_parameterlist()
stack = [signode[-1]]
for token in py_paramlist_re.split(arglist):
if token == '[':
opt = addnodes.desc_optional()
stack[-1] += opt
stack.append(opt)
elif token == ']':
try:
stack.pop()
except IndexError:
raise ValueError
elif not token or token == ',' or token.isspace():
pass
else:
token = token.strip()
stack[-1] += addnodes.desc_parameter(token, token)
if len(stack) != 1:
raise ValueError
if retann:
signode += addnodes.desc_returns(retann, retann)
return fullname, classname
def get_index_text(self, modname, name):
"""
Return the text for the index entry of the object.
"""
raise NotImplementedError('must be implemented in subclasses')
def add_target_and_index(self, name_cls, sig, signode):
modname = self.options.get('module', self.env.currmodule)
fullname = (modname and modname + '.' or '') + name_cls[0]
# note target
if fullname not in self.state.document.ids:
signode['names'].append(fullname)
signode['ids'].append(fullname)
signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
self.env.note_descref(fullname, self.desctype, self.lineno)
indextext = self.get_index_text(modname, name_cls)
if indextext:
self.indexnode['entries'].append(('single', indextext,
fullname, fullname))
def before_content(self):
# needed for automatic qualification of members (reset in subclasses)
self.clsname_set = False
def after_content(self):
if self.clsname_set:
self.env.currclass = None
class ModulelevelDesc(PythonDesc):
"""
Description of an object on module level (functions, data).
"""
def needs_arglist(self):
return self.desctype == 'function'
def get_index_text(self, modname, name_cls):
if self.desctype == 'function':
if not modname:
return _('%s() (built-in function)') % name_cls[0]
return _('%s() (in module %s)') % (name_cls[0], modname)
elif self.desctype == 'data':
if not modname:
return _('%s (built-in variable)') % name_cls[0]
return _('%s (in module %s)') % (name_cls[0], modname)
else:
return ''
class ClasslikeDesc(PythonDesc):
"""
Description of a class-like object (classes, interfaces, exceptions).
"""
def get_signature_prefix(self, sig):
return self.desctype + ' '
def get_index_text(self, modname, name_cls):
if self.desctype == 'class':
if not modname:
return _('%s (built-in class)') % name_cls[0]
return _('%s (class in %s)') % (name_cls[0], modname)
elif self.desctype == 'exception':
return name_cls[0]
else:
return ''
def before_content(self):
PythonDesc.before_content(self)
if self.names:
self.env.currclass = self.names[0][0]
self.clsname_set = True
class ClassmemberDesc(PythonDesc):
"""
Description of a class member (methods, attributes).
"""
def needs_arglist(self):
return self.desctype.endswith('method')
def get_signature_prefix(self, sig):
if self.desctype == 'staticmethod':
return 'static '
elif self.desctype == 'classmethod':
return 'classmethod '
return ''
def get_index_text(self, modname, name_cls):
name, cls = name_cls
add_modules = self.env.config.add_module_names
if self.desctype == 'method':
try:
clsname, methname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s() (in module %s)') % (name, modname)
else:
return '%s()' % name
if modname and add_modules:
return _('%s() (%s.%s method)') % (methname, modname, clsname)
else:
return _('%s() (%s method)') % (methname, clsname)
elif self.desctype == 'staticmethod':
try:
clsname, methname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s() (in module %s)') % (name, modname)
else:
return '%s()' % name
if modname and add_modules:
return _('%s() (%s.%s static method)') % (methname, modname,
clsname)
else:
return _('%s() (%s static method)') % (methname, clsname)
elif self.desctype == 'classmethod':
try:
clsname, methname = name.rsplit('.', 1)
except ValueError:
if modname:
return '%s() (in module %s)' % (name, modname)
else:
return '%s()' % name
if modname:
return '%s() (%s.%s class method)' % (methname, modname,
clsname)
else:
return '%s() (%s class method)' % (methname, clsname)
elif self.desctype == 'attribute':
try:
clsname, attrname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s (in module %s)') % (name, modname)
else:
return name
if modname and add_modules:
return _('%s (%s.%s attribute)') % (attrname, modname, clsname)
else:
return _('%s (%s attribute)') % (attrname, clsname)
else:
return ''
def before_content(self):
PythonDesc.before_content(self)
if self.names and self.names[-1][1] and not self.env.currclass:
self.env.currclass = self.names[-1][1].strip('.')
self.clsname_set = True
class CDesc(DescDirective):
"""
Description of a C language object.
"""
# These C types aren't described anywhere, so don't try to create
# a cross-reference to them
stopwords = set(('const', 'void', 'char', 'int', 'long', 'FILE', 'struct'))
def _parse_type(self, node, ctype):
# add cross-ref nodes for all words
for part in filter(None, wsplit_re.split(ctype)):
tnode = nodes.Text(part, part)
if part[0] in string.ascii_letters+'_' and \
part not in self.stopwords:
pnode = addnodes.pending_xref(
'', reftype='ctype', reftarget=part,
modname=None, classname=None)
pnode += tnode
node += pnode
else:
node += tnode
def parse_signature(self, sig, signode):
"""Transform a C (or C++) signature into RST nodes."""
# first try the function pointer signature regex, it's more specific
m = c_funcptr_sig_re.match(sig)
if m is None:
m = c_sig_re.match(sig)
if m is None:
raise ValueError('no match')
rettype, name, arglist, const = m.groups()
signode += addnodes.desc_type('', '')
self._parse_type(signode[-1], rettype)
try:
classname, funcname = name.split('::', 1)
classname += '::'
signode += addnodes.desc_addname(classname, classname)
signode += addnodes.desc_name(funcname, funcname)
# name (the full name) is still both parts
except ValueError:
signode += addnodes.desc_name(name, name)
# clean up parentheses from canonical name
m = c_funcptr_name_re.match(name)
if m:
name = m.group(1)
if not arglist:
if self.desctype == 'cfunction':
# for functions, add an empty parameter list
signode += addnodes.desc_parameterlist()
if const:
signode += addnodes.desc_addname(const, const)
return name
paramlist = addnodes.desc_parameterlist()
arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup
# this messes up function pointer types, but not too badly ;)
args = arglist.split(',')
for arg in args:
arg = arg.strip()
param = addnodes.desc_parameter('', '', noemph=True)
try:
ctype, argname = arg.rsplit(' ', 1)
except ValueError:
# no argument name given, only the type
self._parse_type(param, arg)
else:
self._parse_type(param, ctype)
param += nodes.emphasis(' '+argname, ' '+argname)
paramlist += param
signode += paramlist
if const:
signode += addnodes.desc_addname(const, const)
return name
def get_index_text(self, name):
if self.desctype == 'cfunction':
return _('%s (C function)') % name
elif self.desctype == 'cmember':
return _('%s (C member)') % name
elif self.desctype == 'cmacro':
return _('%s (C macro)') % name
elif self.desctype == 'ctype':
return _('%s (C type)') % name
elif self.desctype == 'cvar':
return _('%s (C variable)') % name
else:
return ''
def add_target_and_index(self, name, sig, signode):
# note target
if name not in self.state.document.ids:
signode['names'].append(name)
signode['ids'].append(name)
signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
self.env.note_descref(name, self.desctype, self.lineno)
indextext = self.get_index_text(name)
if indextext:
self.indexnode['entries'].append(('single', indextext, name, name))
class CmdoptionDesc(DescDirective):
"""
Description of a command-line option (.. cmdoption).
"""
def parse_signature(self, sig, signode):
"""Transform an option description into RST nodes."""
count = 0
firstname = ''
for m in option_desc_re.finditer(sig):
optname, args = m.groups()
if count:
signode += addnodes.desc_addname(', ', ', ')
signode += addnodes.desc_name(optname, optname)
signode += addnodes.desc_addname(args, args)
if not count:
firstname = optname
count += 1
if not firstname:
raise ValueError
return firstname
def add_target_and_index(self, name, sig, signode):
targetname = name.replace('/', '-')
if self.env.currprogram:
targetname = '-' + self.env.currprogram + targetname
targetname = 'cmdoption' + targetname
signode['ids'].append(targetname)
self.state.document.note_explicit_target(signode)
self.indexnode['entries'].append(
('pair', _('%scommand line option; %s') %
((self.env.currprogram and
self.env.currprogram + ' ' or ''), sig),
targetname, targetname))
self.env.note_progoption(name, targetname)
class GenericDesc(DescDirective):
"""
A generic x-ref directive registered with Sphinx.add_description_unit().
"""
def parse_signature(self, sig, signode):
parse_node = additional_xref_types[self.desctype][2]
if parse_node:
name = parse_node(self.env, sig, signode)
else:
signode.clear()
signode += addnodes.desc_name(sig, sig)
# normalize whitespace like xfileref_role does
name = ws_re.sub('', sig)
return name
def add_target_and_index(self, name, sig, signode):
rolename, indextemplate = additional_xref_types[self.desctype][:2]
targetname = '%s-%s' % (rolename, name)
signode['ids'].append(targetname)
self.state.document.note_explicit_target(signode)
if indextemplate:
indexentry = _(indextemplate) % (name,)
indextype = 'single'
colon = indexentry.find(':')
if colon != -1:
indextype = indexentry[:colon].strip()
indexentry = indexentry[colon+1:].strip()
self.indexnode['entries'].append((indextype, indexentry,
targetname, targetname))
self.env.note_reftarget(rolename, name, targetname)
class Target(Directive):
"""
Generic target for user-defined cross-reference types.
"""
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {}
def run(self):
env = self.state.document.settings.env
rolename, indextemplate, foo = additional_xref_types[self.name]
# normalize whitespace in fullname like xfileref_role does
fullname = ws_re.sub('', self.arguments[0].strip())
targetname = '%s-%s' % (rolename, fullname)
node = nodes.target('', '', ids=[targetname])
self.state.document.note_explicit_target(node)
ret = [node]
if indextemplate:
indexentry = indextemplate % (fullname,)
indextype = 'single'
colon = indexentry.find(':')
if colon != -1:
indextype = indexentry[:colon].strip()
indexentry = indexentry[colon+1:].strip()
inode = addnodes.index(entries=[(indextype, indexentry,
targetname, targetname)])
ret.insert(0, inode)
env.note_reftarget(rolename, fullname, targetname)
return ret
# Note: the target directive is not registered here, it is used by the
# application when registering additional xref types.
_ = lambda x: x
# Generic cross-reference types; they can be registered in the application;
# the directives are either desc_directive or target_directive.
additional_xref_types = {
# directive name: (role name, index text, function to parse the desc node)
'envvar': ('envvar', _('environment variable; %s'), None),
}
del _
directives.register_directive('describe', directive_dwim(DescDirective))
directives.register_directive('function', directive_dwim(ModulelevelDesc))
directives.register_directive('data', directive_dwim(ModulelevelDesc))
directives.register_directive('class', directive_dwim(ClasslikeDesc))
directives.register_directive('exception', directive_dwim(ClasslikeDesc))
directives.register_directive('method', directive_dwim(ClassmemberDesc))
directives.register_directive('classmethod', directive_dwim(ClassmemberDesc))
directives.register_directive('staticmethod', directive_dwim(ClassmemberDesc))
directives.register_directive('attribute', directive_dwim(ClassmemberDesc))
directives.register_directive('cfunction', directive_dwim(CDesc))
directives.register_directive('cmember', directive_dwim(CDesc))
directives.register_directive('cmacro', directive_dwim(CDesc))
directives.register_directive('ctype', directive_dwim(CDesc))
directives.register_directive('cvar', directive_dwim(CDesc))
directives.register_directive('cmdoption', directive_dwim(CmdoptionDesc))
directives.register_directive('envvar', directive_dwim(GenericDesc))

View File

@ -10,13 +10,13 @@
import re import re
from docutils import nodes from docutils import nodes
from docutils.parsers.rst import directives from docutils.parsers.rst import Directive, directives
from sphinx import addnodes from sphinx import addnodes
from sphinx.locale import pairindextypes from sphinx.locale import pairindextypes
from sphinx.util import patfilter, ws_re, url_re, docname_join, \ from sphinx.util import patfilter, ws_re, url_re, docname_join, \
explicit_title_re explicit_title_re
from sphinx.util.compat import Directive, directive_dwim, make_admonition from sphinx.util.compat import make_admonition
class TocTree(Directive): class TocTree(Directive):
@ -104,75 +104,6 @@ class TocTree(Directive):
return ret return ret
class Module(Directive):
"""
Directive to mark description of a new module.
"""
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
option_spec = {
'platform': lambda x: x,
'synopsis': lambda x: x,
'noindex': directives.flag,
'deprecated': directives.flag,
}
def run(self):
env = self.state.document.settings.env
modname = self.arguments[0].strip()
noindex = 'noindex' in self.options
env.currmodule = modname
env.note_module(modname, self.options.get('synopsis', ''),
self.options.get('platform', ''),
'deprecated' in self.options)
modulenode = addnodes.module()
modulenode['modname'] = modname
modulenode['synopsis'] = self.options.get('synopsis', '')
targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True)
self.state.document.note_explicit_target(targetnode)
ret = [modulenode, targetnode]
if 'platform' in self.options:
platform = self.options['platform']
modulenode['platform'] = platform
node = nodes.paragraph()
node += nodes.emphasis('', _('Platforms: '))
node += nodes.Text(platform, platform)
ret.append(node)
# the synopsis isn't printed; in fact, it is only used in the
# modindex currently
if not noindex:
indextext = _('%s (module)') % modname
inode = addnodes.index(entries=[('single', indextext,
'module-' + modname, modname)])
ret.insert(0, inode)
return ret
class CurrentModule(Directive):
"""
This directive is just to tell Sphinx that we're documenting
stuff in module foo, but links to module foo won't lead here.
"""
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
option_spec = {}
def run(self):
env = self.state.document.settings.env
modname = self.arguments[0].strip()
if modname == 'None':
env.currmodule = None
else:
env.currmodule = modname
return []
class Author(Directive): class Author(Directive):
""" """
Directive to give the name of the author of the current document Directive to give the name of the author of the current document
@ -205,27 +136,6 @@ class Author(Directive):
return [para] + messages return [para] + messages
class Program(Directive):
"""
Directive to name the program for which options are documented.
"""
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {}
def run(self):
env = self.state.document.settings.env
program = ws_re.sub('-', self.arguments[0].strip())
if program == 'None':
env.currprogram = None
else:
env.currprogram = program
return []
class Index(Directive): class Index(Directive):
""" """
Directive to add entries to the index. Directive to add entries to the index.
@ -244,8 +154,7 @@ class Index(Directive):
def run(self): def run(self):
arguments = self.arguments[0].split('\n') arguments = self.arguments[0].split('\n')
env = self.state.document.settings.env env = self.state.document.settings.env
targetid = 'index-%s' % env.index_num targetid = 'index-%s' % env.new_serialno('index')
env.index_num += 1
targetnode = nodes.target('', '', ids=[targetid]) targetnode = nodes.target('', '', ids=[targetid])
self.state.document.note_explicit_target(targetnode) self.state.document.note_explicit_target(targetnode)
indexnode = addnodes.index() indexnode = addnodes.index()
@ -302,7 +211,11 @@ class VersionChange(Directive):
else: else:
ret = [node] ret = [node]
env = self.state.document.settings.env env = self.state.document.settings.env
env.note_versionchange(node['type'], node['version'], node, self.lineno) env.versionchanges.setdefault(node['version'], []).append(
(node['type'], env.doc_read_data['docname'], self.lineno,
env.doc_read_data.get('py_module'),
env.doc_read_data.get('object'),
node.astext()))
return ret return ret
@ -332,66 +245,6 @@ class SeeAlso(Directive):
return ret return ret
token_re = re.compile('`([a-z_]+)`')
def token_xrefs(text, env):
retnodes = []
pos = 0
for m in token_re.finditer(text):
if m.start() > pos:
txt = text[pos:m.start()]
retnodes.append(nodes.Text(txt, txt))
refnode = addnodes.pending_xref(m.group(1))
refnode['reftype'] = 'token'
refnode['reftarget'] = m.group(1)
refnode['modname'] = env.currmodule
refnode['classname'] = env.currclass
refnode += nodes.literal(m.group(1), m.group(1), classes=['xref'])
retnodes.append(refnode)
pos = m.end()
if pos < len(text):
retnodes.append(nodes.Text(text[pos:], text[pos:]))
return retnodes
class ProductionList(Directive):
"""
Directive to list grammar productions.
"""
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {}
def run(self):
env = self.state.document.settings.env
node = addnodes.productionlist()
messages = []
i = 0
for rule in self.arguments[0].split('\n'):
if i == 0 and ':' not in rule:
# production group
continue
i += 1
try:
name, tokens = rule.split(':', 1)
except ValueError:
break
subnode = addnodes.production()
subnode['tokenname'] = name.strip()
if subnode['tokenname']:
idname = 'grammar-token-%s' % subnode['tokenname']
if idname not in self.state.document.ids:
subnode['ids'].append(idname)
self.state.document.note_implicit_target(subnode, subnode)
env.note_reftarget('token', subnode['tokenname'], idname)
subnode.extend(token_xrefs(tokens, env))
node.append(subnode)
return [node] + messages
class TabularColumns(Directive): class TabularColumns(Directive):
""" """
Directive to give an explicit tabulary column definition to LaTeX. Directive to give an explicit tabulary column definition to LaTeX.
@ -409,57 +262,6 @@ class TabularColumns(Directive):
return [node] return [node]
class Glossary(Directive):
"""
Directive to create a glossary with cross-reference targets
for :term: roles.
"""
has_content = True
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = False
option_spec = {
'sorted': directives.flag,
}
def run(self):
env = self.state.document.settings.env
node = addnodes.glossary()
node.document = self.state.document
self.state.nested_parse(self.content, self.content_offset, node)
# the content should be definition lists
dls = [child for child in node
if isinstance(child, nodes.definition_list)]
# now, extract definition terms to enable cross-reference creation
new_dl = nodes.definition_list()
new_dl['classes'].append('glossary')
items = []
for dl in dls:
for li in dl.children:
if not li.children or not isinstance(li[0], nodes.term):
continue
termtext = li.children[0].astext()
new_id = 'term-' + nodes.make_id(termtext)
if new_id in env.gloss_entries:
new_id = 'term-' + str(len(env.gloss_entries))
env.gloss_entries.add(new_id)
li[0]['names'].append(new_id)
li[0]['ids'].append(new_id)
env.note_reftarget('term', termtext.lower(), new_id)
# add an index entry too
indexnode = addnodes.index()
indexnode['entries'] = [('single', termtext, new_id, termtext)]
li.insert(0, indexnode)
items.append((termtext, li))
if 'sorted' in self.options:
items.sort(key=lambda x: x[0].lower())
new_dl.extend(item[1] for item in items)
node.children = [new_dl]
return [node]
class Centered(Directive): class Centered(Directive):
""" """
Directive to create a centered line of bold text. Directive to create a centered line of bold text.
@ -562,36 +364,21 @@ class Only(Directive):
return [node] return [node]
directives.register_directive('toctree', directive_dwim(TocTree)) directives.register_directive('toctree', TocTree)
directives.register_directive('module', directive_dwim(Module)) directives.register_directive('sectionauthor', Author)
directives.register_directive('currentmodule', directive_dwim(CurrentModule)) directives.register_directive('moduleauthor', Author)
directives.register_directive('sectionauthor', directive_dwim(Author)) directives.register_directive('index', Index)
directives.register_directive('moduleauthor', directive_dwim(Author)) directives.register_directive('deprecated', VersionChange)
directives.register_directive('program', directive_dwim(Program)) directives.register_directive('versionadded', VersionChange)
directives.register_directive('index', directive_dwim(Index)) directives.register_directive('versionchanged', VersionChange)
directives.register_directive('deprecated', directive_dwim(VersionChange)) directives.register_directive('seealso', SeeAlso)
directives.register_directive('versionadded', directive_dwim(VersionChange)) directives.register_directive('tabularcolumns', TabularColumns)
directives.register_directive('versionchanged', directive_dwim(VersionChange)) directives.register_directive('centered', Centered)
directives.register_directive('seealso', directive_dwim(SeeAlso)) directives.register_directive('acks', Acks)
directives.register_directive('productionlist', directive_dwim(ProductionList)) directives.register_directive('hlist', HList)
directives.register_directive('tabularcolumns', directive_dwim(TabularColumns)) directives.register_directive('only', Only)
directives.register_directive('glossary', directive_dwim(Glossary))
directives.register_directive('centered', directive_dwim(Centered))
directives.register_directive('acks', directive_dwim(Acks))
directives.register_directive('hlist', directive_dwim(HList))
directives.register_directive('only', directive_dwim(Only))
# register the standard rst class directive under a different name # register the standard rst class directive under a different name
# only for backwards compatibility now
try: from docutils.parsers.rst.directives.misc import Class
# docutils 0.4 directives.register_directive('cssclass', Class)
from docutils.parsers.rst.directives.misc import class_directive
directives.register_directive('cssclass', class_directive)
except ImportError:
try:
# docutils 0.5
from docutils.parsers.rst.directives.misc import Class
directives.register_directive('cssclass', Class)
except ImportError:
# whatever :)
pass

180
sphinx/domains/__init__.py Normal file
View File

@ -0,0 +1,180 @@
# -*- coding: utf-8 -*-
"""
sphinx.domains
~~~~~~~~~~~~~~
Support for domains, which are groupings of description directives
and roles describing e.g. constructs of one programming language.
:copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
class ObjType(object):
"""
XXX add docstring
"""
known_attrs = {
'searchprio': 1,
}
def __init__(self, lname, *roles, **attrs):
self.lname = lname
self.roles = roles
self.attrs = self.known_attrs.copy()
self.attrs.update(attrs)
class Domain(object):
"""
A Domain is meant to be a group of "object" description directives for
objects of a similar nature, and corresponding roles to create references to
them. Examples would be Python modules, classes, functions etc., elements
of a templating language, Sphinx roles and directives, etc.
Each domain has a separate storage for information about existing objects
and how to reference them in `data`, which must be a dictionary. It also
must implement several functions that expose the object information in a
uniform way to parts of Sphinx that allow the user to reference or search
for objects in a domain-agnostic way.
About `self.data`: since all object and cross-referencing information is
stored on a BuildEnvironment instance, the `domain.data` object is also
stored in the `env.domaindata` dict under the key `domain.name`. Before the
build process starts, every active domain is instantiated and given the
environment object; the `domaindata` dict must then either be nonexistent or
a dictionary whose 'version' key is equal to the domain class'
`data_version` attribute. Otherwise, `IOError` is raised and the pickled
environment is discarded.
"""
#: domain name: should be short, but unique
name = ''
#: domain label: longer, more descriptive (used in messages)
label = ''
#: type (usually directive) name -> ObjType instance
object_types = {}
#: directive name -> directive class
directives = {}
#: role name -> role callable
roles = {}
#: data value for a fresh environment
initial_data = {}
#: data version
data_version = 0
def __init__(self, env):
self.env = env
if self.name not in env.domaindata:
assert isinstance(self.initial_data, dict)
new_data = self.initial_data.copy()
new_data['version'] = self.data_version
self.data = env.domaindata[self.name] = new_data
else:
self.data = env.domaindata[self.name]
if self.data['version'] != self.data_version:
raise IOError('data of %r domain out of date' % self.label)
self._role_cache = {}
self._directive_cache = {}
self._role2type = {}
for name, obj in self.object_types.iteritems():
for rolename in obj.roles:
self._role2type.setdefault(rolename, []).append(name)
self.objtypes_for_role = self._role2type.get
def role(self, name):
"""
Return a role adapter function that always gives the registered
role its full name ('domain:name') as the first argument.
"""
if name in self._role_cache:
return self._role_cache[name]
if name not in self.roles:
return None
fullname = '%s:%s' % (self.name, name)
def role_adapter(typ, rawtext, text, lineno, inliner,
options={}, content=[]):
return self.roles[name](fullname, rawtext, text, lineno,
inliner, options, content)
self._role_cache[name] = role_adapter
return role_adapter
def directive(self, name):
"""
Return a directive adapter class that always gives the registered
directive its full name ('domain:name') as ``self.name``.
"""
if name in self._directive_cache:
return self._directive_cache[name]
if name not in self.directives:
return None
fullname = '%s:%s' % (self.name, name)
BaseDirective = self.directives[name]
class DirectiveAdapter(BaseDirective):
def run(self):
self.name = fullname
return BaseDirective.run(self)
self._directive_cache[name] = DirectiveAdapter
return DirectiveAdapter
# methods that should be overwritten
def clear_doc(self, docname):
"""
Remove traces of a document in the domain-specific inventories.
"""
pass
# XXX way for individual docs to override methods in an existing domain?
def resolve_xref(self, env, fromdocname, builder,
typ, target, node, contnode):
"""
Resolve the ``pending_xref`` *node* with the given *typ* and *target*.
This method should return a new node, to replace the xref node,
containing the *contnode* which is the markup content of the
cross-reference.
If no resolution can be found, None can be returned; the xref node will
then given to the 'missing-reference' event, and if that yields no
resolution, replaced by *contnode*.
The method can also raise `sphinx.environment.NoUri` to suppress the
'missing-reference' event being emitted.
"""
pass
def get_objects(self):
"""
Return an iterable of "object descriptions", which are tuples with
five items:
* `name` -- fully qualified name
* `type` -- object type, a key in ``self.object_types``
* `docname` -- the document where it is to be found
* `anchor` -- the anchor name for the object
* `priority` -- how "important" the object is (determines placement
in search results)
1: default priority (placed before full-text matches)
0: object is important (placed before default-priority objects)
2: object is unimportant (placed after full-text matches)
-1: object should not show up in search at all
"""
return []
from sphinx.domains.c import CDomain
from sphinx.domains.std import StandardDomain
from sphinx.domains.python import PythonDomain
# this contains all registered domains
all_domains = {
'std': StandardDomain,
'py': PythonDomain,
'c': CDomain,
}

204
sphinx/domains/c.py Normal file
View File

@ -0,0 +1,204 @@
# -*- coding: utf-8 -*-
"""
sphinx.domains.c
~~~~~~~~~~~~~~~~
The C language domain.
:copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
import string
from docutils import nodes
from docutils.parsers.rst import directives
from sphinx import addnodes
from sphinx.roles import XRefRole
from sphinx.locale import l_
from sphinx.domains import Domain, ObjType
from sphinx.directives import ObjectDescription
from sphinx.util import make_refnode
from sphinx.util.compat import Directive
# RE to split at word boundaries
wsplit_re = re.compile(r'(\W+)')
# REs for C signatures
c_sig_re = re.compile(
r'''^([^(]*?) # return type
([\w:.]+) \s* # thing name (colon allowed for C++ class names)
(?: \((.*)\) )? # optionally arguments
(\s+const)? $ # const specifier
''', re.VERBOSE)
c_funcptr_sig_re = re.compile(
r'''^([^(]+?) # return type
(\( [^()]+ \)) \s* # name in parentheses
\( (.*) \) # arguments
(\s+const)? $ # const specifier
''', re.VERBOSE)
c_funcptr_name_re = re.compile(r'^\(\s*\*\s*(.*?)\s*\)$')
class CObject(ObjectDescription):
"""
Description of a C language object.
"""
# These C types aren't described anywhere, so don't try to create
# a cross-reference to them
stopwords = set(('const', 'void', 'char', 'int', 'long', 'FILE', 'struct'))
def _parse_type(self, node, ctype):
# add cross-ref nodes for all words
for part in filter(None, wsplit_re.split(ctype)):
tnode = nodes.Text(part, part)
if part[0] in string.ascii_letters+'_' and \
part not in self.stopwords:
pnode = addnodes.pending_xref(
'', refdomain='c', reftype='type', reftarget=part,
modname=None, classname=None)
pnode += tnode
node += pnode
else:
node += tnode
def parse_signature(self, sig, signode):
"""Transform a C (or C++) signature into RST nodes."""
# first try the function pointer signature regex, it's more specific
m = c_funcptr_sig_re.match(sig)
if m is None:
m = c_sig_re.match(sig)
if m is None:
raise ValueError('no match')
rettype, name, arglist, const = m.groups()
signode += addnodes.desc_type('', '')
self._parse_type(signode[-1], rettype)
try:
classname, funcname = name.split('::', 1)
classname += '::'
signode += addnodes.desc_addname(classname, classname)
signode += addnodes.desc_name(funcname, funcname)
# name (the full name) is still both parts
except ValueError:
signode += addnodes.desc_name(name, name)
# clean up parentheses from canonical name
m = c_funcptr_name_re.match(name)
if m:
name = m.group(1)
if not arglist:
if self.objtype == 'function':
# for functions, add an empty parameter list
signode += addnodes.desc_parameterlist()
if const:
signode += addnodes.desc_addname(const, const)
return name
paramlist = addnodes.desc_parameterlist()
arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup
# this messes up function pointer types, but not too badly ;)
args = arglist.split(',')
for arg in args:
arg = arg.strip()
param = addnodes.desc_parameter('', '', noemph=True)
try:
ctype, argname = arg.rsplit(' ', 1)
except ValueError:
# no argument name given, only the type
self._parse_type(param, arg)
else:
self._parse_type(param, ctype)
param += nodes.emphasis(' '+argname, ' '+argname)
paramlist += param
signode += paramlist
if const:
signode += addnodes.desc_addname(const, const)
return name
def get_index_text(self, name):
if self.objtype == 'function':
return _('%s (C function)') % name
elif self.objtype == 'member':
return _('%s (C member)') % name
elif self.objtype == 'macro':
return _('%s (C macro)') % name
elif self.objtype == 'type':
return _('%s (C type)') % name
elif self.objtype == 'var':
return _('%s (C variable)') % name
else:
return ''
def add_target_and_index(self, name, sig, signode):
# note target
if name not in self.state.document.ids:
signode['names'].append(name)
signode['ids'].append(name)
signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
inv = self.env.domaindata['c']['objects']
if name in inv:
self.env.warn(
self.env.docname,
'duplicate C object description of %s, ' % name +
'other instance in ' + self.env.doc2path(inv[name][0]),
self.lineno)
inv[name] = (self.env.docname, self.objtype)
indextext = self.get_index_text(name)
if indextext:
self.indexnode['entries'].append(('single', indextext, name, name))
class CDomain(Domain):
"""C language domain."""
name = 'c'
label = 'C'
object_types = {
'function': ObjType(l_('C function'), 'func'),
'member': ObjType(l_('C member'), 'member'),
'macro': ObjType(l_('C macro'), 'macro'),
'type': ObjType(l_('C type'), 'type'),
'var': ObjType(l_('C variable'), 'data'),
}
directives = {
'function': CObject,
'member': CObject,
'macro': CObject,
'type': CObject,
'var': CObject,
}
roles = {
'func' : XRefRole(fix_parens=True),
'member': XRefRole(),
'macro': XRefRole(),
'data': XRefRole(),
'type': XRefRole(),
}
initial_data = {
'objects': {}, # fullname -> docname, objtype
}
def clear_doc(self, docname):
for fullname, (fn, _) in self.data['objects'].items():
if fn == docname:
del self.data['objects'][fullname]
def resolve_xref(self, env, fromdocname, builder,
typ, target, node, contnode):
# strip pointer asterisk
target = target.rstrip(' *')
if target not in self.data['objects']:
return None
obj = self.data['objects'][target]
return make_refnode(builder, fromdocname, obj[0], target,
contnode, target)
def get_objects(self):
for refname, (docname, type) in self.data['objects'].iteritems():
yield (refname, type, docname, refname, 1)

514
sphinx/domains/python.py Normal file
View File

@ -0,0 +1,514 @@
# -*- coding: utf-8 -*-
"""
sphinx.domains.python
~~~~~~~~~~~~~~~~~~~~~
The Python domain.
:copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
from docutils import nodes
from docutils.parsers.rst import directives
from sphinx import addnodes
from sphinx.roles import XRefRole
from sphinx.locale import l_
from sphinx.domains import Domain, ObjType
from sphinx.directives import ObjectDescription
from sphinx.util import make_refnode
from sphinx.util.compat import Directive
# REs for Python signatures
py_sig_re = re.compile(
r'''^ ([\w.]*\.)? # class name(s)
(\w+) \s* # thing name
(?: \((.*)\) # optional: arguments
(?:\s* -> \s* (.*))? # return annotation
)? $ # and nothing more
''', re.VERBOSE)
py_paramlist_re = re.compile(r'([\[\],])') # split at '[', ']' and ','
class PyObject(ObjectDescription):
"""
Description of a general Python object.
"""
def get_signature_prefix(self, sig):
"""
May return a prefix to put before the object name in the signature.
"""
return ''
def needs_arglist(self):
"""
May return true if an empty argument list is to be generated even if
the document contains none.
"""
return False
def parse_signature(self, sig, signode):
"""
Transform a Python signature into RST nodes.
Returns (fully qualified name of the thing, classname if any).
If inside a class, the current class name is handled intelligently:
* it is stripped from the displayed name if present
* it is added to the full name (return value) if not present
"""
m = py_sig_re.match(sig)
if m is None:
raise ValueError
classname, name, arglist, retann = m.groups()
currclass = self.env.doc_read_data.get('py_class')
if currclass:
add_module = False
if classname and classname.startswith(currclass):
fullname = classname + name
# class name is given again in the signature
classname = classname[len(currclass):].lstrip('.')
elif classname:
# class name is given in the signature, but different
# (shouldn't happen)
fullname = currclass + '.' + classname + name
else:
# class name is not given in the signature
fullname = currclass + '.' + name
else:
add_module = True
fullname = classname and classname + name or name
prefix = self.get_signature_prefix(sig)
if prefix:
signode += addnodes.desc_annotation(prefix, prefix)
if classname:
signode += addnodes.desc_addname(classname, classname)
# exceptions are a special case, since they are documented in the
# 'exceptions' module.
elif add_module and self.env.config.add_module_names:
modname = self.options.get(
'module', self.env.doc_read_data.get('py_module'))
if modname and modname != 'exceptions':
nodetext = modname + '.'
signode += addnodes.desc_addname(nodetext, nodetext)
signode += addnodes.desc_name(name, name)
if not arglist:
if self.needs_arglist():
# for callables, add an empty parameter list
signode += addnodes.desc_parameterlist()
if retann:
signode += addnodes.desc_returns(retann, retann)
return fullname, classname
signode += addnodes.desc_parameterlist()
stack = [signode[-1]]
for token in py_paramlist_re.split(arglist):
if token == '[':
opt = addnodes.desc_optional()
stack[-1] += opt
stack.append(opt)
elif token == ']':
try:
stack.pop()
except IndexError:
raise ValueError
elif not token or token == ',' or token.isspace():
pass
else:
token = token.strip()
stack[-1] += addnodes.desc_parameter(token, token)
if len(stack) != 1:
raise ValueError
if retann:
signode += addnodes.desc_returns(retann, retann)
return fullname, classname
def get_index_text(self, modname, name):
"""
Return the text for the index entry of the object.
"""
raise NotImplementedError('must be implemented in subclasses')
def add_target_and_index(self, name_cls, sig, signode):
modname = self.options.get(
'module', self.env.doc_read_data.get('py_module'))
fullname = (modname and modname + '.' or '') + name_cls[0]
# note target
if fullname not in self.state.document.ids:
signode['names'].append(fullname)
signode['ids'].append(fullname)
signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
objects = self.env.domaindata['py']['objects']
if fullname in objects:
self.env.warn(
self.env.docname,
'duplicate object description of %s, ' % fullname +
'other instance in ' +
self.env.doc2path(objects[fullname][0]),
self.lineno)
objects[fullname] = (self.env.docname, self.objtype)
indextext = self.get_index_text(modname, name_cls)
if indextext:
self.indexnode['entries'].append(('single', indextext,
fullname, fullname))
def before_content(self):
# needed for automatic qualification of members (reset in subclasses)
self.clsname_set = False
def after_content(self):
if self.clsname_set:
self.env.doc_read_data['py_class'] = None
class PyModulelevel(PyObject):
"""
Description of an object on module level (functions, data).
"""
def needs_arglist(self):
return self.objtype == 'function'
def get_index_text(self, modname, name_cls):
if self.objtype == 'function':
if not modname:
return _('%s() (built-in function)') % name_cls[0]
return _('%s() (in module %s)') % (name_cls[0], modname)
elif self.objtype == 'data':
if not modname:
return _('%s (built-in variable)') % name_cls[0]
return _('%s (in module %s)') % (name_cls[0], modname)
else:
return ''
class PyClasslike(PyObject):
"""
Description of a class-like object (classes, interfaces, exceptions).
"""
def get_signature_prefix(self, sig):
return self.objtype + ' '
def get_index_text(self, modname, name_cls):
if self.objtype == 'class':
if not modname:
return _('%s (built-in class)') % name_cls[0]
return _('%s (class in %s)') % (name_cls[0], modname)
elif self.objtype == 'exception':
return name_cls[0]
else:
return ''
def before_content(self):
PyObject.before_content(self)
if self.names:
self.env.doc_read_data['py_class'] = self.names[0][0]
self.clsname_set = True
class PyClassmember(PyObject):
"""
Description of a class member (methods, attributes).
"""
def needs_arglist(self):
return self.objtype.endswith('method')
def get_signature_prefix(self, sig):
if self.objtype == 'staticmethod':
return 'static '
elif self.objtype == 'classmethod':
return 'classmethod '
return ''
def get_index_text(self, modname, name_cls):
name, cls = name_cls
add_modules = self.env.config.add_module_names
if self.objtype == 'method':
try:
clsname, methname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s() (in module %s)') % (name, modname)
else:
return '%s()' % name
if modname and add_modules:
return _('%s() (%s.%s method)') % (methname, modname, clsname)
else:
return _('%s() (%s method)') % (methname, clsname)
elif self.objtype == 'staticmethod':
try:
clsname, methname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s() (in module %s)') % (name, modname)
else:
return '%s()' % name
if modname and add_modules:
return _('%s() (%s.%s static method)') % (methname, modname,
clsname)
else:
return _('%s() (%s static method)') % (methname, clsname)
elif self.objtype == 'classmethod':
try:
clsname, methname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s() (in module %s)') % (name, modname)
else:
return '%s()' % name
if modname:
return _('%s() (%s.%s class method)') % (methname, modname,
clsname)
else:
return _('%s() (%s class method)') % (methname, clsname)
elif self.objtype == 'attribute':
try:
clsname, attrname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s (in module %s)') % (name, modname)
else:
return name
if modname and add_modules:
return _('%s (%s.%s attribute)') % (attrname, modname, clsname)
else:
return _('%s (%s attribute)') % (attrname, clsname)
else:
return ''
def before_content(self):
PyObject.before_content(self)
lastname = self.names and self.names[-1][1]
if lastname and not self.env.doc_read_data.get('py_class'):
self.env.doc_read_data['py_class'] = lastname.strip('.')
self.clsname_set = True
class PyModule(Directive):
"""
Directive to mark description of a new module.
"""
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
option_spec = {
'platform': lambda x: x,
'synopsis': lambda x: x,
'noindex': directives.flag,
'deprecated': directives.flag,
}
def run(self):
env = self.state.document.settings.env
modname = self.arguments[0].strip()
noindex = 'noindex' in self.options
env.doc_read_data['py_module'] = modname
env.domaindata['py']['modules'][modname] = \
(env.docname, self.options.get('synopsis', ''),
self.options.get('platform', ''), 'deprecated' in self.options)
modulenode = addnodes.module()
modulenode['modname'] = modname
modulenode['synopsis'] = self.options.get('synopsis', '')
targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True)
self.state.document.note_explicit_target(targetnode)
ret = [modulenode, targetnode]
if 'platform' in self.options:
platform = self.options['platform']
modulenode['platform'] = platform
node = nodes.paragraph()
node += nodes.emphasis('', _('Platforms: '))
node += nodes.Text(platform, platform)
ret.append(node)
# the synopsis isn't printed; in fact, it is only used in the
# modindex currently
if not noindex:
indextext = _('%s (module)') % modname
inode = addnodes.index(entries=[('single', indextext,
'module-' + modname, modname)])
ret.insert(0, inode)
return ret
class PyCurrentModule(Directive):
"""
This directive is just to tell Sphinx that we're documenting
stuff in module foo, but links to module foo won't lead here.
"""
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
option_spec = {}
def run(self):
env = self.state.document.settings.env
modname = self.arguments[0].strip()
if modname == 'None':
env.doc_read_data['py_module'] = None
else:
env.doc_read_data['py_module'] = modname
return []
class PyXRefRole(XRefRole):
def process_link(self, env, refnode, has_explicit_title, title, target):
refnode['py_module'] = env.doc_read_data.get('py_module')
refnode['py_class'] = env.doc_read_data.get('py_class')
if not has_explicit_title:
title = title.lstrip('.') # only has a meaning for the target
target = target.lstrip('~') # only has a meaning for the title
# if the first character is a tilde, don't display the module/class
# parts of the contents
if title[0:1] == '~':
title = title[1:]
dot = title.rfind('.')
if dot != -1:
title = title[dot+1:]
# if the first character is a dot, search more specific namespaces first
# else search builtins first
if target[0:1] == '.':
target = target[1:]
refnode['refspecific'] = True
return title, target
class PythonDomain(Domain):
"""Python language domain."""
name = 'py'
label = 'Python'
object_types = {
'function': ObjType(l_('function'), 'func', 'obj'),
'data': ObjType(l_('data'), 'data', 'obj'),
'class': ObjType(l_('class'), 'class', 'obj'),
'exception': ObjType(l_('exception'), 'exc', 'obj'),
'method': ObjType(l_('method'), 'meth', 'obj'),
'attribute': ObjType(l_('attribute'), 'attr', 'obj'),
'module': ObjType(l_('module'), 'mod', 'obj'),
}
directives = {
'function': PyModulelevel,
'data': PyModulelevel,
'class': PyClasslike,
'exception': PyClasslike,
'method': PyClassmember,
'classmethod': PyClassmember,
'staticmethod': PyClassmember,
'attribute': PyClassmember,
'module': PyModule,
'currentmodule': PyCurrentModule,
}
roles = {
'data': PyXRefRole(),
'exc': PyXRefRole(),
'func': PyXRefRole(fix_parens=True),
'class': PyXRefRole(),
'const': PyXRefRole(),
'attr': PyXRefRole(),
'meth': PyXRefRole(fix_parens=True),
'mod': PyXRefRole(),
'obj': PyXRefRole(),
}
initial_data = {
'objects': {}, # fullname -> docname, objtype
'modules': {}, # modname -> docname, synopsis, platform, deprecated
}
def clear_doc(self, docname):
for fullname, (fn, _) in self.data['objects'].items():
if fn == docname:
del self.data['objects'][fullname]
for modname, (fn, _, _, _) in self.data['modules'].items():
if fn == docname:
del self.data['modules'][modname]
def find_obj(self, env, modname, classname, name, type, searchorder=0):
"""
Find a Python object for "name", perhaps using the given module and/or
classname.
"""
# skip parens
if name[-2:] == '()':
name = name[:-2]
if not name:
return None, None
objects = self.data['objects']
newname = None
if searchorder == 1:
if modname and classname and \
modname + '.' + classname + '.' + name in objects:
newname = modname + '.' + classname + '.' + name
elif modname and modname + '.' + name in objects:
newname = modname + '.' + name
elif name in objects:
newname = name
else:
if name in objects:
newname = name
elif modname and modname + '.' + name in objects:
newname = modname + '.' + name
elif modname and classname and \
modname + '.' + classname + '.' + name in objects:
newname = modname + '.' + classname + '.' + name
# special case: builtin exceptions have module "exceptions" set
elif type == 'exc' and '.' not in name and \
'exceptions.' + name in objects:
newname = 'exceptions.' + name
# special case: object methods
elif type in ('func', 'meth') and '.' not in name and \
'object.' + name in objects:
newname = 'object.' + name
if newname is None:
return None, None
return newname, objects[newname]
def resolve_xref(self, env, fromdocname, builder,
typ, target, node, contnode):
if (typ == 'mod' or
typ == 'obj' and target in self.data['modules']):
docname, synopsis, platform, deprecated = \
self.data['modules'].get(target, ('','','', ''))
if not docname:
return None
else:
title = '%s%s%s' % ((platform and '(%s) ' % platform),
synopsis,
(deprecated and ' (deprecated)' or ''))
return make_refnode(builder, fromdocname, docname,
'module-' + target, contnode, title)
else:
modname = node.get('py_module')
clsname = node.get('py_class')
searchorder = node.hasattr('refspecific') and 1 or 0
name, obj = self.find_obj(env, modname, clsname,
target, typ, searchorder)
if not obj:
return None
else:
return make_refnode(builder, fromdocname, obj[0], name,
contnode, name)
def get_objects(self):
for modname, info in self.data['modules'].iteritems():
yield (modname, 'module', info[0], 'module-' + modname, 0)
for refname, (docname, type) in self.data['objects'].iteritems():
yield (refname, type, docname, refname, 1)

384
sphinx/domains/std.py Normal file
View File

@ -0,0 +1,384 @@
# -*- coding: utf-8 -*-
"""
sphinx.domains.std
~~~~~~~~~~~~~~~~~~
The standard domain.
:copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
from docutils import nodes, utils
from docutils.parsers.rst import directives
from sphinx import addnodes
from sphinx.roles import XRefRole
from sphinx.locale import l_
from sphinx.domains import Domain, ObjType
from sphinx.directives import ObjectDescription
from sphinx.util import make_refnode, ws_re
from sphinx.util.compat import Directive
# RE for option descriptions
option_desc_re = re.compile(
r'((?:/|-|--)[-_a-zA-Z0-9]+)(\s*.*?)(?=,\s+(?:/|-|--)|$)')
class GenericObject(ObjectDescription):
"""
A generic x-ref directive registered with Sphinx.add_object_type().
"""
indextemplate = ''
parse_node = None
def parse_signature(self, sig, signode):
if self.parse_node:
name = self.parse_node(self.env, sig, signode)
else:
signode.clear()
signode += addnodes.desc_name(sig, sig)
# normalize whitespace like XRefRole does
name = ws_re.sub('', sig)
return name
def add_target_and_index(self, name, sig, signode):
targetname = '%s-%s' % (self.objtype, name)
signode['ids'].append(targetname)
self.state.document.note_explicit_target(signode)
if self.indextemplate:
colon = self.indextemplate.find(':')
if colon != -1:
indextype = self.indextemplate[:colon].strip()
indexentry = self.indextemplate[colon+1:].strip() % (name,)
else:
indextype = 'single'
indexentry = self.indextemplate % (name,)
self.indexnode['entries'].append((indextype, indexentry,
targetname, targetname))
self.env.domaindata['std']['objects'][self.objtype, name] = \
self.env.docname, targetname
class EnvVar(GenericObject):
indextemplate = l_('environment variable; %s')
class EnvVarXRefRole(XRefRole):
"""
Cross-referencing role for environment variables (adds an index entry).
"""
def result_nodes(self, document, env, node, is_ref):
if not is_ref:
return [node], []
varname = node['reftarget']
tgtid = 'index-%s' % env.new_serialno('index')
indexnode = addnodes.index()
indexnode['entries'] = [
('single', varname, tgtid, varname),
('single', _('environment variable; %s') % varname, tgtid, varname)
]
targetnode = nodes.target('', '', ids=[tgtid])
document.note_explicit_target(targetnode)
return [indexnode, targetnode, node], []
class Target(Directive):
"""
Generic target for user-defined cross-reference types.
"""
indextemplate = ''
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {}
def run(self):
env = self.state.document.settings.env
# normalize whitespace in fullname like XRefRole does
fullname = ws_re.sub('', self.arguments[0].strip())
targetname = '%s-%s' % (self.name, fullname)
node = nodes.target('', '', ids=[targetname])
self.state.document.note_explicit_target(node)
ret = [node]
if self.indextemplate:
indexentry = self.indextemplate % (fullname,)
indextype = 'single'
colon = indexentry.find(':')
if colon != -1:
indextype = indexentry[:colon].strip()
indexentry = indexentry[colon+1:].strip()
inode = addnodes.index(entries=[(indextype, indexentry,
targetname, targetname)])
ret.insert(0, inode)
env.domaindata['std']['objects'][self.name, fullname] = \
env.docname, targetname
return ret
class Cmdoption(ObjectDescription):
"""
Description of a command-line option (.. cmdoption).
"""
def parse_signature(self, sig, signode):
"""Transform an option description into RST nodes."""
count = 0
firstname = ''
for m in option_desc_re.finditer(sig):
optname, args = m.groups()
if count:
signode += addnodes.desc_addname(', ', ', ')
signode += addnodes.desc_name(optname, optname)
signode += addnodes.desc_addname(args, args)
if not count:
firstname = optname
count += 1
if not firstname:
raise ValueError
return firstname
def add_target_and_index(self, name, sig, signode):
targetname = name.replace('/', '-')
currprogram = self.env.doc_read_data.get('std_program')
if currprogram:
targetname = '-' + currprogram + targetname
targetname = 'cmdoption' + targetname
signode['ids'].append(targetname)
self.state.document.note_explicit_target(signode)
self.indexnode['entries'].append(
('pair', _('%scommand line option; %s') %
((currprogram and currprogram + ' ' or ''), sig),
targetname, targetname))
self.env.domaindata['std']['progoptions'][currprogram, name] = \
self.env.docname, targetname
class Program(Directive):
"""
Directive to name the program for which options are documented.
"""
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {}
def run(self):
env = self.state.document.settings.env
program = ws_re.sub('-', self.arguments[0].strip())
if program == 'None':
env.doc_read_data['std_program'] = None
else:
env.doc_read_data['std_program'] = program
return []
class OptionXRefRole(XRefRole):
innernodeclass = addnodes.literal_emphasis
def process_link(self, env, refnode, has_explicit_title, title, target):
program = env.doc_read_data.get('std_program')
if not has_explicit_title:
if ' ' in title and not (title.startswith('/') or
title.startswith('-')):
program, target = re.split(' (?=-|--|/)', title, 1)
program = ws_re.sub('-', program)
target = target.strip()
elif ' ' in target:
program, target = re.split(' (?=-|--|/)', target, 1)
program = ws_re.sub('-', program)
refnode['refprogram'] = program
return title, target
class Glossary(Directive):
"""
Directive to create a glossary with cross-reference targets
for :term: roles.
"""
has_content = True
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = False
option_spec = {
'sorted': directives.flag,
}
def run(self):
env = self.state.document.settings.env
objects = env.domaindata['std']['objects']
gloss_entries = env.doc_read_data.setdefault('gloss_entries', set())
node = addnodes.glossary()
node.document = self.state.document
self.state.nested_parse(self.content, self.content_offset, node)
# the content should be definition lists
dls = [child for child in node
if isinstance(child, nodes.definition_list)]
# now, extract definition terms to enable cross-reference creation
new_dl = nodes.definition_list()
new_dl['classes'].append('glossary')
items = []
for dl in dls:
for li in dl.children:
if not li.children or not isinstance(li[0], nodes.term):
continue
termtext = li.children[0].astext()
new_id = 'term-' + nodes.make_id(termtext)
if new_id in gloss_entries:
new_id = 'term-' + str(len(gloss_entries))
gloss_entries.add(new_id)
li[0]['names'].append(new_id)
li[0]['ids'].append(new_id)
objects['term', termtext.lower()] = env.docname, new_id
# add an index entry too
indexnode = addnodes.index()
indexnode['entries'] = [('single', termtext, new_id, termtext)]
li.insert(0, indexnode)
items.append((termtext, li))
if 'sorted' in self.options:
items.sort(key=lambda x: x[0].lower())
new_dl.extend(item[1] for item in items)
node.children = [new_dl]
return [node]
token_re = re.compile('`([a-z_][a-z0-9_]*)`')
def token_xrefs(text):
retnodes = []
pos = 0
for m in token_re.finditer(text):
if m.start() > pos:
txt = text[pos:m.start()]
retnodes.append(nodes.Text(txt, txt))
refnode = addnodes.pending_xref(
m.group(1), reftype='token', refdomain='std', reftarget=m.group(1))
refnode += nodes.literal(m.group(1), m.group(1), classes=['xref'])
retnodes.append(refnode)
pos = m.end()
if pos < len(text):
retnodes.append(nodes.Text(text[pos:], text[pos:]))
return retnodes
class ProductionList(Directive):
"""
Directive to list grammar productions.
"""
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {}
def run(self):
env = self.state.document.settings.env
objects = env.domaindata['std']['objects']
node = addnodes.productionlist()
messages = []
i = 0
for rule in self.arguments[0].split('\n'):
if i == 0 and ':' not in rule:
# production group
continue
i += 1
try:
name, tokens = rule.split(':', 1)
except ValueError:
break
subnode = addnodes.production()
subnode['tokenname'] = name.strip()
if subnode['tokenname']:
idname = 'grammar-token-%s' % subnode['tokenname']
if idname not in self.state.document.ids:
subnode['ids'].append(idname)
self.state.document.note_implicit_target(subnode, subnode)
objects['token', subnode['tokenname']] = env.docname, idname
subnode.extend(token_xrefs(tokens))
node.append(subnode)
return [node] + messages
class StandardDomain(Domain):
"""
Domain for all objects that don't fit into another domain or are added
via the application interface.
"""
name = 'std'
label = 'Default'
object_types = {
'term': ObjType(l_('glossary term'), 'term', searchprio=-1),
'token': ObjType(l_('grammar token'), 'token', searchprio=-1),
'envvar': ObjType(l_('environment variable'), 'envvar'),
'cmdoption': ObjType(l_('program option'), 'option'),
}
directives = {
'program': Program,
'cmdoption': Cmdoption,
'envvar': EnvVar,
'glossary': Glossary,
'productionlist': ProductionList,
}
roles = {
'option': OptionXRefRole(innernodeclass=addnodes.literal_emphasis),
'envvar': EnvVarXRefRole(),
'token': XRefRole(),
'term': XRefRole(lowercase=True, innernodeclass=nodes.emphasis),
}
initial_data = {
'progoptions': {}, # (program, name) -> docname, labelid
'objects': {}, # (type, name) -> docname, labelid
}
def clear_doc(self, docname):
for key, (fn, _) in self.data['progoptions'].items():
if fn == docname:
del self.data['progoptions'][key]
for key, (fn, _) in self.data['objects'].items():
if fn == docname:
del self.data['objects'][key]
def resolve_xref(self, env, fromdocname, builder,
typ, target, node, contnode):
if typ == 'option':
progname = node['refprogram']
docname, labelid = self.data['progoptions'].get((progname, target),
('', ''))
if not docname:
return None
else:
return make_refnode(builder, fromdocname, docname,
labelid, contnode)
else:
docname, labelid = self.data['objects'].get((typ, target), ('', ''))
if not docname:
if typ == 'term':
env.warn(fromdocname, 'term not in glossary: %s' % target,
node.line)
return None
else:
return make_refnode(builder, fromdocname, docname,
labelid, contnode)
def get_objects(self):
for (prog, option), info in self.data['progoptions'].iteritems():
yield (option, 'option', info[0], info[1], 1)
for (type, name), info in self.data['objects'].iteritems():
yield (name, type, info[0], info[1],
self.object_types[type].attrs['searchprio'])

View File

@ -26,7 +26,7 @@ from docutils.io import FileInput, NullOutput
from docutils.core import Publisher from docutils.core import Publisher
from docutils.utils import Reporter, relative_path from docutils.utils import Reporter, relative_path
from docutils.readers import standalone from docutils.readers import standalone
from docutils.parsers.rst import roles from docutils.parsers.rst import roles, directives
from docutils.parsers.rst.languages import en as english from docutils.parsers.rst.languages import en as english
from docutils.parsers.rst.directives.html import MetaBody from docutils.parsers.rst.directives.html import MetaBody
from docutils.writers import UnfilteredWriter from docutils.writers import UnfilteredWriter
@ -35,9 +35,41 @@ from docutils.transforms.parts import ContentsFilter
from sphinx import addnodes from sphinx import addnodes
from sphinx.util import movefile, get_matching_docs, SEP, ustrftime, \ from sphinx.util import movefile, get_matching_docs, SEP, ustrftime, \
docname_join, FilenameUniqDict, url_re docname_join, FilenameUniqDict, url_re, make_refnode
from sphinx.errors import SphinxError from sphinx.errors import SphinxError, ExtensionError
from sphinx.directives import additional_xref_types
orig_role_function = roles.role
orig_directive_function = directives.directive
class ElementLookupError(Exception): pass
def lookup_domain_element(env, type, name):
"""Lookup a markup element (directive or role), given its name which can
be a full name (with domain).
"""
name = name.lower()
# explicit domain given?
if ':' in name:
domain_name, name = name.split(':', 1)
if domain_name in env.domains:
domain = env.domains[domain_name]
element = getattr(domain, type)(name)
if element is not None:
return element, []
# else look in the default domain
else:
def_domain = env.doc_read_data.get('default_domain')
if def_domain is not None:
element = getattr(def_domain, type)(name)
if element is not None:
return element, []
# always look in the std domain
element = getattr(env.domains['std'], type)(name)
if element is not None:
return element, []
raise ElementLookupError
default_settings = { default_settings = {
'embed_stylesheet': False, 'embed_stylesheet': False,
@ -51,7 +83,7 @@ default_settings = {
# This is increased every time an environment attribute is added # This is increased every time an environment attribute is added
# or changed to properly invalidate pickle files. # or changed to properly invalidate pickle files.
ENV_VERSION = 30 ENV_VERSION = 32
default_substitutions = set([ default_substitutions = set([
@ -202,9 +234,9 @@ class BuildEnvironment:
env = pickle.load(picklefile) env = pickle.load(picklefile)
finally: finally:
picklefile.close() picklefile.close()
env.config.values = config.values
if env.version != ENV_VERSION: if env.version != ENV_VERSION:
raise IOError('env version not current') raise IOError('env version not current')
env.config.values = config.values
return env return env
def topickle(self, filename): def topickle(self, filename):
@ -213,6 +245,8 @@ class BuildEnvironment:
self.set_warnfunc(None) self.set_warnfunc(None)
values = self.config.values values = self.config.values
del self.config.values del self.config.values
domains = self.domains
del self.domains
# first write to a temporary file, so that if dumping fails, # first write to a temporary file, so that if dumping fails,
# the existing environment won't be overwritten # the existing environment won't be overwritten
picklefile = open(filename + '.tmp', 'wb') picklefile = open(filename + '.tmp', 'wb')
@ -229,6 +263,7 @@ class BuildEnvironment:
picklefile.close() picklefile.close()
movefile(filename + '.tmp', filename) movefile(filename + '.tmp', filename)
# reset attributes # reset attributes
self.domains = domains
self.config.values = values self.config.values = values
self.set_warnfunc(warnfunc) self.set_warnfunc(warnfunc)
@ -242,6 +277,9 @@ class BuildEnvironment:
# the application object; only set while update() runs # the application object; only set while update() runs
self.app = None self.app = None
# all the registered domains, set by the application
self.domains = {}
# the docutils settings for building # the docutils settings for building
self.settings = default_settings.copy() self.settings = default_settings.copy()
self.settings['env'] = self self.settings['env'] = self
@ -280,16 +318,13 @@ class BuildEnvironment:
self.glob_toctrees = set() # docnames that have :glob: toctrees self.glob_toctrees = set() # docnames that have :glob: toctrees
self.numbered_toctrees = set() # docnames that have :numbered: toctrees self.numbered_toctrees = set() # docnames that have :numbered: toctrees
# domain-specific inventories, here to be pickled
self.domaindata = {} # domainname -> domain-specific dict
# X-ref target inventory # X-ref target inventory
self.descrefs = {} # fullname -> docname, desctype
self.filemodules = {} # docname -> [modules]
self.modules = {} # modname -> docname, synopsis,
# platform, deprecated
self.labels = {} # labelname -> docname, labelid, sectionname self.labels = {} # labelname -> docname, labelid, sectionname
self.anonlabels = {} # labelname -> docname, labelid self.anonlabels = {} # labelname -> docname, labelid
self.progoptions = {} # (program, name) -> docname, labelid self.citations = {} # citation name -> docname, labelid
self.reftargets = {} # (type, name) -> docname, labelid
# type: term, token, envvar, citation
# Other inventories # Other inventories
self.indexentries = {} # docname -> list of self.indexentries = {} # docname -> list of
@ -301,14 +336,8 @@ class BuildEnvironment:
self.images = FilenameUniqDict() self.images = FilenameUniqDict()
self.dlfiles = FilenameUniqDict() self.dlfiles = FilenameUniqDict()
# These are set while parsing a file # temporary data storage while reading a document
self.docname = None # current document name self.doc_read_data = {}
self.currmodule = None # current module name
self.currclass = None # current class name
self.currdesc = None # current descref name
self.currprogram = None # current program name
self.index_num = 0 # autonumber for index targets
self.gloss_entries = set() # existing definition labels
# Some magically present labels # Some magically present labels
def add_magic_label(name, description): def add_magic_label(name, description):
@ -342,7 +371,6 @@ class BuildEnvironment:
self.toc_secnumbers.pop(docname, None) self.toc_secnumbers.pop(docname, None)
self.toc_num_entries.pop(docname, None) self.toc_num_entries.pop(docname, None)
self.toctree_includes.pop(docname, None) self.toctree_includes.pop(docname, None)
self.filemodules.pop(docname, None)
self.indexentries.pop(docname, None) self.indexentries.pop(docname, None)
self.glob_toctrees.discard(docname) self.glob_toctrees.discard(docname)
self.numbered_toctrees.discard(docname) self.numbered_toctrees.discard(docname)
@ -353,25 +381,20 @@ class BuildEnvironment:
fnset.discard(docname) fnset.discard(docname)
if not fnset: if not fnset:
del self.files_to_rebuild[subfn] del self.files_to_rebuild[subfn]
for fullname, (fn, _) in self.descrefs.items():
if fn == docname:
del self.descrefs[fullname]
for modname, (fn, _, _, _) in self.modules.items():
if fn == docname:
del self.modules[modname]
for labelname, (fn, _, _) in self.labels.items(): for labelname, (fn, _, _) in self.labels.items():
if fn == docname: if fn == docname:
del self.labels[labelname] del self.labels[labelname]
for key, (fn, _) in self.reftargets.items(): for key, (fn, _) in self.citations.items():
if fn == docname: if fn == docname:
del self.reftargets[key] del self.citations[key]
for key, (fn, _) in self.progoptions.items():
if fn == docname:
del self.progoptions[key]
for version, changes in self.versionchanges.items(): for version, changes in self.versionchanges.items():
new = [change for change in changes if change[1] != docname] new = [change for change in changes if change[1] != docname]
changes[:] = new changes[:] = new
# XXX why does this not work inside the if?
for domain in self.domains.values():
domain.clear_doc(docname)
def doc2path(self, docname, base=True, suffix=None): def doc2path(self, docname, base=True, suffix=None):
""" """
Return the filename for the document name. Return the filename for the document name.
@ -543,6 +566,26 @@ class BuildEnvironment:
error.object[error.end:lineend]), lineno) error.object[error.end:lineend]), lineno)
return (u'?', error.end) return (u'?', error.end)
def patch_lookup_functions(self):
"""
Monkey-patch directive and role dispatch, so that domain-specific
markup takes precedence.
"""
def directive(name, lang_module, document):
try:
return lookup_domain_element(self, 'directive', name)
except ElementLookupError:
return orig_directive_function(name, lang_module, document)
def role(name, lang_module, lineno, reporter):
try:
return lookup_domain_element(self, 'role', name)
except ElementLookupError:
return orig_role_function(name, lang_module, lineno, reporter)
directives.directive = directive
roles.role = role
def read_doc(self, docname, 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. Parse a file and add/update inventory entries for the doctree.
@ -551,6 +594,7 @@ class BuildEnvironment:
# remove all inventory entries for that file # remove all inventory entries for that file
if app: if app:
app.emit('env-purge-doc', self, docname) app.emit('env-purge-doc', self, docname)
self.clear_doc(docname) self.clear_doc(docname)
if src_path is None: if src_path is None:
@ -565,12 +609,16 @@ class BuildEnvironment:
self.warn(docname, 'default role %s not found' % self.warn(docname, 'default role %s not found' %
self.config.default_role) self.config.default_role)
self.docname = docname self.doc_read_data['docname'] = docname
# defaults to the global default, but can be re-set in a document
self.doc_read_data['default_domain'] = \
self.domains.get(self.config.default_domain)
self.settings['input_encoding'] = self.config.source_encoding self.settings['input_encoding'] = self.config.source_encoding
self.settings['trim_footnote_reference_space'] = \ self.settings['trim_footnote_reference_space'] = \
self.config.trim_footnote_reference_space self.config.trim_footnote_reference_space
codecs.register_error('sphinx', self.warn_and_replace) self.patch_lookup_functions()
codecs.register_error('sphinx', self.warn_and_replace) codecs.register_error('sphinx', self.warn_and_replace)
@ -603,6 +651,8 @@ class BuildEnvironment:
doctree = pub.document doctree = pub.document
except UnicodeError, err: except UnicodeError, err:
raise SphinxError(str(err)) raise SphinxError(str(err))
# post-processing
self.filter_messages(doctree) self.filter_messages(doctree)
self.process_dependencies(docname, doctree) self.process_dependencies(docname, doctree)
self.process_images(docname, doctree) self.process_images(docname, doctree)
@ -614,12 +664,13 @@ class BuildEnvironment:
self.note_citations_from(docname, doctree) self.note_citations_from(docname, doctree)
self.build_toc_from(docname, doctree) self.build_toc_from(docname, doctree)
# store time of reading, used to find outdated files # allow extension-specific post-processing
self.all_docs[docname] = time.time()
if app: if app:
app.emit('doctree-read', doctree) app.emit('doctree-read', doctree)
# store time of reading, used to find outdated files
self.all_docs[docname] = time.time()
# make it picklable # make it picklable
doctree.reporter = None doctree.reporter = None
doctree.transformer = None doctree.transformer = None
@ -631,10 +682,7 @@ class BuildEnvironment:
metanode.__class__ = addnodes.meta metanode.__class__ = addnodes.meta
# cleanup # cleanup
self.docname = None self.doc_read_data.clear()
self.currmodule = None
self.currclass = None
self.gloss_entries = set()
if save_parsed: if save_parsed:
# save the parsed doctree # save the parsed doctree
@ -651,6 +699,35 @@ class BuildEnvironment:
else: else:
return doctree return doctree
# utilities to use while reading a document
@property
def docname(self):
"""Backwards compatible alias."""
return self.doc_read_data['docname']
@property
def currmodule(self):
"""Backwards compatible alias."""
return self.doc_read_data.get('py_module')
@property
def currclass(self):
"""Backwards compatible alias."""
return self.doc_read_data.get('py_class')
def new_serialno(self, category=''):
"""Return a serial number, e.g. for index entry targets."""
key = category + 'serialno'
cur = self.doc_read_data.get(key, 0)
self.doc_read_data[key] = cur + 1
return cur
def note_dependency(self, filename):
self.dependencies.setdefault(self.docname, set()).add(filename)
# post-processing of read doctrees
def filter_messages(self, doctree): def filter_messages(self, doctree):
""" """
Filter system messages from a doctree. Filter system messages from a doctree.
@ -812,7 +889,7 @@ class BuildEnvironment:
if name.isdigit() or node.has_key('refuri') or \ if name.isdigit() or node.has_key('refuri') or \
node.tagname.startswith('desc_'): node.tagname.startswith('desc_'):
# ignore footnote labels, labels automatically generated from a # ignore footnote labels, labels automatically generated from a
# link and description units # link and object descriptions
continue continue
if name in self.labels: if name in self.labels:
self.warn(docname, 'duplicate label %s, ' % name + self.warn(docname, 'duplicate label %s, ' % name +
@ -842,11 +919,11 @@ class BuildEnvironment:
def note_citations_from(self, docname, document): def note_citations_from(self, docname, document):
for node in document.traverse(nodes.citation): for node in document.traverse(nodes.citation):
label = node[0].astext() label = node[0].astext()
if ('citation', label) in self.reftargets: if label in self.citations:
self.warn(docname, 'duplicate citation %s, ' % label + self.warn(docname, 'duplicate citation %s, ' % label +
'other instance in %s' % self.doc2path( 'other instance in %s' % self.doc2path(
self.reftargets['citation', label][0]), node.line) self.citations[label][0]), node.line)
self.reftargets['citation', label] = (docname, node['ids'][0]) self.citations[label] = (docname, node['ids'][0])
def note_toctree(self, docname, toctreenode): def note_toctree(self, docname, toctreenode):
"""Note a TOC tree directive in a document and gather information about """Note a TOC tree directive in a document and gather information about
@ -934,45 +1011,23 @@ class BuildEnvironment:
node['refuri'] = node['anchorname'] or '#' node['refuri'] = node['anchorname'] or '#'
return toc return toc
def get_toctree_for(self, docname, builder, collapse): def get_toctree_for(self, docname, builder, collapse, maxdepth=0):
"""Return the global TOC nodetree.""" """Return the global TOC nodetree."""
doctree = self.get_doctree(self.config.master_doc) doctree = self.get_doctree(self.config.master_doc)
for toctreenode in doctree.traverse(addnodes.toctree): for toctreenode in doctree.traverse(addnodes.toctree):
result = self.resolve_toctree(docname, builder, toctreenode, result = self.resolve_toctree(docname, builder, toctreenode,
prune=True, collapse=collapse) prune=True, collapse=collapse,
maxdepth=maxdepth)
if result is not None: if result is not None:
return result return result
# ------- def get_domain(self, domainname):
# these are called from docutils directives and therefore use self.docname """Return the domain instance with the specified name.
# Raises an ExtensionError if the domain is not registered."""
def note_descref(self, fullname, desctype, line): try:
if fullname in self.descrefs: return self.domains[domainname]
self.warn(self.docname, except KeyError:
'duplicate canonical description name %s, ' % fullname + raise ExtensionError('Domain %r is not registered' % domainname)
'other instance in ' +
self.doc2path(self.descrefs[fullname][0]),
line)
self.descrefs[fullname] = (self.docname, desctype)
def note_module(self, modname, synopsis, platform, deprecated):
self.modules[modname] = (self.docname, synopsis, platform, deprecated)
self.filemodules.setdefault(self.docname, []).append(modname)
def note_progoption(self, optname, labelid):
self.progoptions[self.currprogram, optname] = (self.docname, labelid)
def note_reftarget(self, type, name, labelid):
self.reftargets[type, name] = (self.docname, labelid)
def note_versionchange(self, type, version, node, lineno):
self.versionchanges.setdefault(version, []).append(
(type, self.docname, lineno, self.currmodule, self.currdesc,
node.astext()))
def note_dependency(self, filename):
self.dependencies.setdefault(self.docname, set()).add(filename)
# -------
# --------- RESOLVING REFERENCES AND TOCTREES ------------------------------ # --------- RESOLVING REFERENCES AND TOCTREES ------------------------------
@ -1156,15 +1211,7 @@ class BuildEnvironment:
docname, refnode['refuri']) + refnode['anchorname'] docname, refnode['refuri']) + refnode['anchorname']
return newnode return newnode
descroles = frozenset(('data', 'exc', 'func', 'class', 'const',
'attr', 'obj', 'meth', 'cfunc', 'cmember',
'cdata', 'ctype', 'cmacro'))
def resolve_references(self, doctree, fromdocname, builder): def resolve_references(self, doctree, fromdocname, builder):
reftarget_roles = set(('token', 'term', 'citation'))
# 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
@ -1173,8 +1220,17 @@ class BuildEnvironment:
target = node['reftarget'] target = node['reftarget']
try: try:
if typ == 'ref': if node.has_key('refdomain') and node['refdomain']:
if node['refcaption']: # let the domain try to resolve the reference
try:
domain = self.domains[node['refdomain']]
except KeyError:
raise NoUri
newnode = domain.resolve_xref(self, fromdocname, builder,
typ, target, node, contnode)
# really hardwired reference types
elif typ == 'ref':
if node['refexplicit']:
# reference to anonymous label; the reference uses # reference to anonymous label; the reference uses
# the supplied link caption # the supplied link caption
docname, labelid = self.anonlabels.get(target, ('','')) docname, labelid = self.anonlabels.get(target, ('',''))
@ -1221,7 +1277,7 @@ class BuildEnvironment:
node.line) node.line)
newnode = contnode newnode = contnode
else: else:
if node['refcaption']: if node['refexplicit']:
# reference with explicit title # reference with explicit title
caption = node.astext() caption = node.astext()
else: else:
@ -1231,101 +1287,35 @@ class BuildEnvironment:
newnode['refuri'] = builder.get_relative_uri( newnode['refuri'] = builder.get_relative_uri(
fromdocname, docname) fromdocname, docname)
newnode.append(innernode) newnode.append(innernode)
elif typ == 'citation':
docname, labelid = self.citations.get(target, ('', ''))
if not docname:
self.warn(fromdocname,
'citation not found: %s' % target, node.line)
newnode = None
else:
newnode = make_refnode(builder, fromdocname, docname,
labelid, contnode)
elif typ == 'keyword': elif typ == 'keyword':
# keywords are referenced by named labels # keywords are oddballs: they are referenced by named labels
docname, labelid, _ = self.labels.get(target, ('','','')) docname, labelid, _ = self.labels.get(target, ('','',''))
if not docname: if not docname:
#self.warn(fromdocname, 'unknown keyword: %s' % target) #self.warn(fromdocname, 'unknown keyword: %s' % target)
newnode = contnode newnode = None
else: else:
newnode = nodes.reference('', '') newnode = make_refnode(builder, fromdocname, docname,
if docname == fromdocname: labelid, contnode)
newnode['refid'] = labelid
else:
newnode['refuri'] = builder.get_relative_uri(
fromdocname, docname) + '#' + labelid
newnode.append(contnode)
elif typ == 'option':
progname = node['refprogram']
docname, labelid = self.progoptions.get((progname, target),
('', ''))
if not docname:
newnode = contnode
else:
newnode = nodes.reference('', '')
if docname == fromdocname:
newnode['refid'] = labelid
else:
newnode['refuri'] = builder.get_relative_uri(
fromdocname, docname) + '#' + labelid
newnode.append(contnode)
elif typ in reftarget_roles:
docname, labelid = self.reftargets.get((typ, target),
('', ''))
if not docname:
if typ == 'term':
self.warn(fromdocname,
'term not in glossary: %s' % target,
node.line)
elif typ == 'citation':
self.warn(fromdocname,
'citation not found: %s' % target,
node.line)
newnode = contnode
else:
newnode = nodes.reference('', '')
if docname == fromdocname:
newnode['refid'] = labelid
else:
newnode['refuri'] = builder.get_relative_uri(
fromdocname, docname, typ) + '#' + labelid
newnode.append(contnode)
elif typ == 'mod' or \
typ == 'obj' and target in self.modules:
docname, synopsis, platform, deprecated = \
self.modules.get(target, ('','','', ''))
if not docname:
newnode = builder.app.emit_firstresult(
'missing-reference', self, node, contnode)
if not newnode:
newnode = contnode
else:
newnode = nodes.reference('', '')
newnode['refuri'] = builder.get_relative_uri(
fromdocname, docname) + '#module-' + target
newnode['reftitle'] = '%s%s%s' % (
(platform and '(%s) ' % platform),
synopsis, (deprecated and ' (deprecated)' or ''))
newnode.append(contnode)
elif typ in self.descroles:
# "descrefs"
modname = node['modname']
clsname = node['classname']
searchorder = node.hasattr('refspecific') and 1 or 0
name, desc = self.find_desc(modname, clsname,
target, typ, searchorder)
if not desc:
newnode = builder.app.emit_firstresult(
'missing-reference', self, node, contnode)
if not newnode:
newnode = contnode
else:
newnode = nodes.reference('', '')
if desc[0] == fromdocname:
newnode['refid'] = name
else:
newnode['refuri'] = (
builder.get_relative_uri(fromdocname, desc[0])
+ '#' + name)
newnode['reftitle'] = name
newnode.append(contnode)
else: else:
raise RuntimeError('unknown xfileref node encountered: %s' raise RuntimeError('unknown xfileref node encountered: %s'
% node) % node)
# no new node found? try the missing-reference event
if newnode is None:
newnode = builder.app.emit_firstresult(
'missing-reference', self, node, contnode)
except NoUri: except NoUri:
newnode = contnode newnode = contnode
if newnode: node.replace_self(newnode or contnode)
node.replace_self(newnode)
for node in doctree.traverse(addnodes.only): for node in doctree.traverse(addnodes.only):
try: try:
@ -1556,116 +1546,3 @@ class BuildEnvironment:
# the master file is not included anywhere ;) # the master file is not included anywhere ;)
continue continue
self.warn(docname, 'document isn\'t included in any toctree') self.warn(docname, 'document isn\'t included in any toctree')
# --------- QUERYING -------------------------------------------------------
def find_desc(self, modname, classname, name, type, searchorder=0):
"""Find a description node matching "name", perhaps using
the given module and/or classname."""
# skip parens
if name[-2:] == '()':
name = name[:-2]
if not name:
return None, None
# don't add module and class names for C things
if type[0] == 'c' and type not in ('class', 'const'):
# skip trailing star and whitespace
name = name.rstrip(' *')
if name in self.descrefs and self.descrefs[name][1][0] == 'c':
return name, self.descrefs[name]
return None, None
newname = None
if searchorder == 1:
if modname and classname and \
modname + '.' + classname + '.' + name in self.descrefs:
newname = modname + '.' + classname + '.' + name
elif modname and modname + '.' + name in self.descrefs:
newname = modname + '.' + name
elif name in self.descrefs:
newname = name
else:
if name in self.descrefs:
newname = name
elif modname and modname + '.' + name in self.descrefs:
newname = modname + '.' + name
elif modname and classname and \
modname + '.' + classname + '.' + name in self.descrefs:
newname = modname + '.' + classname + '.' + name
# special case: builtin exceptions have module "exceptions" set
elif type == 'exc' and '.' not in name and \
'exceptions.' + name in self.descrefs:
newname = 'exceptions.' + name
# special case: object methods
elif type in ('func', 'meth') and '.' not in name and \
'object.' + name in self.descrefs:
newname = 'object.' + name
if newname is None:
return None, None
return newname, self.descrefs[newname]
def find_keyword(self, keyword, avoid_fuzzy=False, cutoff=0.6, n=20):
"""
Find keyword matches for a keyword. If there's an exact match,
just return it, else return a list of fuzzy matches if avoid_fuzzy
isn't True.
Keywords searched are: first modules, then descrefs.
Returns: None if nothing found
(type, docname, anchorname) if exact match found
list of (quality, type, docname, anchorname, description)
if fuzzy
"""
if keyword in self.modules:
docname, title, system, deprecated = self.modules[keyword]
return 'module', docname, 'module-' + keyword
if keyword in self.descrefs:
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:
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:
docname, ref_type = self.descrefs['object.'+keyword]
return ref_type, docname, 'object.'+keyword
if avoid_fuzzy:
return
# find fuzzy matches
s = difflib.SequenceMatcher()
s.set_seq2(keyword.lower())
def possibilities():
for title, (fn, desc, _, _) in self.modules.iteritems():
yield ('module', fn, 'module-'+title, desc)
for title, (fn, desctype) in self.descrefs.iteritems():
yield (desctype, fn, title, '')
def dotsearch(string):
parts = string.lower().split('.')
for idx in xrange(0, len(parts)):
yield '.'.join(parts[idx:])
result = []
for type, docname, title, desc in possibilities():
best_res = 0
for part in dotsearch(title):
s.set_seq1(part)
if s.real_quick_ratio() >= cutoff and \
s.quick_ratio() >= cutoff and \
s.ratio() >= cutoff and \
s.ratio() > best_res:
best_res = s.ratio()
if best_res:
result.append((best_res, type, docname, title, desc))
return heapq.nlargest(n, result)

View File

@ -392,9 +392,11 @@ class Documenter(object):
def add_directive_header(self, sig): def add_directive_header(self, sig):
"""Add the directive header and options to the generated content.""" """Add the directive header and options to the generated content."""
domain = getattr(self, 'domain', 'py')
directive = getattr(self, 'directivetype', self.objtype) directive = getattr(self, 'directivetype', self.objtype)
name = self.format_name() name = self.format_name()
self.add_line(u'.. %s:: %s%s' % (directive, name, sig), '<autodoc>') self.add_line(u'.. %s:%s:: %s%s' % (domain, directive, name, sig),
'<autodoc>')
if self.options.noindex: if self.options.noindex:
self.add_line(u' :noindex:', '<autodoc>') self.add_line(u' :noindex:', '<autodoc>')
if self.objpath: if self.objpath:
@ -548,9 +550,9 @@ class Documenter(object):
do all members, else those given by *self.options.members*. do all members, else those given by *self.options.members*.
""" """
# set current namespace for finding members # set current namespace for finding members
self.env.autodoc_current_module = self.modname self.env.doc_read_data['autodoc_module'] = self.modname
if self.objpath: if self.objpath:
self.env.autodoc_current_class = self.objpath[0] self.env.doc_read_data['autodoc_class'] = self.objpath[0]
want_all = all_members or self.options.inherited_members or \ want_all = all_members or self.options.inherited_members or \
self.options.members is ALL self.options.members is ALL
@ -591,8 +593,8 @@ class Documenter(object):
check_module=members_check_module) check_module=members_check_module)
# reset current objects # reset current objects
self.env.autodoc_current_module = None self.env.doc_read_data['autodoc_module'] = None
self.env.autodoc_current_class = None self.env.doc_read_data['autodoc_class'] = None
def generate(self, more_content=None, real_modname=None, def generate(self, more_content=None, real_modname=None,
check_module=False, all_members=False): check_module=False, all_members=False):
@ -750,11 +752,10 @@ class ModuleLevelDocumenter(Documenter):
else: else:
# if documenting a toplevel object without explicit module, # if documenting a toplevel object without explicit module,
# it can be contained in another auto directive ... # it can be contained in another auto directive ...
if hasattr(self.env, 'autodoc_current_module'): modname = self.env.doc_read_data.get('autodoc_module')
modname = self.env.autodoc_current_module
# ... or in the scope of a module directive # ... or in the scope of a module directive
if not modname: if not modname:
modname = self.env.currmodule modname = self.env.doc_read_data.get('py_module')
# ... else, it stays None, which means invalid # ... else, it stays None, which means invalid
return modname, parents + [base] return modname, parents + [base]
@ -773,21 +774,20 @@ class ClassLevelDocumenter(Documenter):
# if documenting a class-level object without path, # if documenting a class-level object without path,
# there must be a current class, either from a parent # there must be a current class, either from a parent
# auto directive ... # auto directive ...
if hasattr(self.env, 'autodoc_current_class'): mod_cls = self.env.doc_read_data.get('autodoc_class')
mod_cls = self.env.autodoc_current_class
# ... or from a class directive # ... or from a class directive
if mod_cls is None: if mod_cls is None:
mod_cls = self.env.currclass mod_cls = self.env.doc_read_data.get('py_class')
# ... if still None, there's no way to know # ... if still None, there's no way to know
if mod_cls is None: if mod_cls is None:
return None, [] return None, []
modname, cls = rpartition(mod_cls, '.') modname, cls = rpartition(mod_cls, '.')
parents = [cls] parents = [cls]
# if the module name is still missing, get it like above # if the module name is still missing, get it like above
if not modname and hasattr(self.env, 'autodoc_current_module'):
modname = self.env.autodoc_current_module
if not modname: if not modname:
modname = self.env.currmodule modname = self.env.doc_read_data.get('autodoc_module')
if not modname:
modname = self.env.doc_read_data.get('py_module')
# ... else, it stays None, which means invalid # ... else, it stays None, which means invalid
return modname, parents + [base] return modname, parents + [base]
@ -1091,7 +1091,7 @@ class AutoDirective(Directive):
# record all filenames as dependencies -- this will at least # record all filenames as dependencies -- this will at least
# partially make automatic invalidation possible # partially make automatic invalidation possible
for fn in self.filename_set: for fn in self.filename_set:
self.env.note_dependency(fn) self.state.document.settings.record_dependencies.add(fn)
# use a custom reporter that correctly assigns lines to source # use a custom reporter that correctly assigns lines to source
# filename/description and lineno # filename/description and lineno

View File

@ -228,8 +228,9 @@ class Autosummary(Directive):
env = self.state.document.settings.env env = self.state.document.settings.env
prefixes = [''] prefixes = ['']
if env.currmodule: currmodule = env.doc_read_data.get('py_module')
prefixes.insert(0, env.currmodule) if currmodule:
prefixes.insert(0, currmodule)
items = [] items = []
@ -443,8 +444,9 @@ def autolink_role(typ, rawtext, etext, lineno, inliner,
Expands to ':obj:`text`' if `text` is an object that can be imported; Expands to ':obj:`text`' if `text` is an object that can be imported;
otherwise expands to '*text*'. otherwise expands to '*text*'.
""" """
r = roles.xfileref_role('obj', rawtext, etext, lineno, inliner, env = inliner.document.settings.env
options, content) r = env.get_domain('py').role('obj')(
'obj', rawtext, etext, lineno, inliner, options, content)
pnode = r[0][0] pnode = r[0][0]
prefixes = [None] prefixes = [None]

View File

@ -79,6 +79,7 @@ class CoverageBuilder(Builder):
def build_c_coverage(self): def build_c_coverage(self):
# Fetch all the info from the header files # Fetch all the info from the header files
c_objects = self.env.domaindata['c']['objects']
for filename in self.c_sourcefiles: for filename in self.c_sourcefiles:
undoc = [] undoc = []
f = open(filename, 'r') f = open(filename, 'r')
@ -88,7 +89,7 @@ class CoverageBuilder(Builder):
match = regex.match(line) match = regex.match(line)
if match: if match:
name = match.groups()[0] name = match.groups()[0]
if name not in self.env.descrefs: if name not in c_objects:
for exp in self.c_ignorexps.get(key, ()): for exp in self.c_ignorexps.get(key, ()):
if exp.match(name): if exp.match(name):
break break
@ -116,7 +117,10 @@ class CoverageBuilder(Builder):
op.close() op.close()
def build_py_coverage(self): def build_py_coverage(self):
for mod_name in self.env.modules: objects = self.env.domaindata['py']['objects']
modules = self.env.domaindata['py']['modules']
for mod_name in modules:
ignore = False ignore = False
for exp in self.mod_ignorexps: for exp in self.mod_ignorexps:
if exp.match(mod_name): if exp.match(mod_name):
@ -151,7 +155,7 @@ class CoverageBuilder(Builder):
full_name = '%s.%s' % (mod_name, name) full_name = '%s.%s' % (mod_name, name)
if inspect.isfunction(obj): if inspect.isfunction(obj):
if full_name not in self.env.descrefs: if full_name not in objects:
for exp in self.fun_ignorexps: for exp in self.fun_ignorexps:
if exp.match(name): if exp.match(name):
break break
@ -162,7 +166,7 @@ class CoverageBuilder(Builder):
if exp.match(name): if exp.match(name):
break break
else: else:
if full_name not in self.env.descrefs: if full_name not in objects:
# not documented at all # not documented at all
classes[name] = [] classes[name] = []
continue continue
@ -176,7 +180,7 @@ class CoverageBuilder(Builder):
continue continue
full_attr_name = '%s.%s' % (full_name, attr_name) full_attr_name = '%s.%s' % (full_name, attr_name)
if full_attr_name not in self.env.descrefs: if full_attr_name not in objects:
attrs.append(attr_name) attrs.append(attr_name)
if attrs: if attrs:

View File

@ -47,7 +47,6 @@ except ImportError:
from docutils import nodes from docutils import nodes
from docutils.parsers.rst import directives from docutils.parsers.rst import directives
from sphinx.roles import xfileref_role
from sphinx.ext.graphviz import render_dot_html, render_dot_latex from sphinx.ext.graphviz import render_dot_html, render_dot_latex
from sphinx.util.compat import Directive from sphinx.util.compat import Directive
@ -280,10 +279,12 @@ class InheritanceDiagram(Directive):
node.document = self.state.document node.document = self.state.document
env = self.state.document.settings.env env = self.state.document.settings.env
class_names = self.arguments[0].split() class_names = self.arguments[0].split()
class_role = env.get_domain('py').role('class')
# Create a graph starting with the list of classes # Create a graph starting with the list of classes
try: try:
graph = InheritanceGraph(class_names, env.currmodule) graph = InheritanceGraph(class_names,
env.doc_read_data.get('py_module'))
except InheritanceException, err: except InheritanceException, err:
return [node.document.reporter.warning(err.args[0], return [node.document.reporter.warning(err.args[0],
line=self.lineno)] line=self.lineno)]
@ -293,7 +294,7 @@ class InheritanceDiagram(Directive):
# references to real URLs later. These nodes will eventually be # references to real URLs later. These nodes will eventually be
# removed from the doctree after we're done with them. # removed from the doctree after we're done with them.
for name in graph.get_all_class_names(): for name in graph.get_all_class_names():
refnodes, x = xfileref_role( refnodes, x = class_role(
'class', ':class:`%s`' % name, name, 0, self.state) 'class', ':class:`%s`' % name, name, 0, self.state)
node.extend(refnodes) node.extend(refnodes)
# Store the graph object so we can use it to generate the # Store the graph object so we can use it to generate the

View File

@ -3,17 +3,17 @@
sphinx.ext.intersphinx sphinx.ext.intersphinx
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
Insert links to Python objects documented in remote Sphinx documentation. Insert links to objects documented in remote Sphinx documentation.
This works as follows: This works as follows:
* Each Sphinx HTML build creates a file named "objects.inv" that contains * Each Sphinx HTML build creates a file named "objects.inv" that contains a
a mapping from Python identifiers to URIs relative to the HTML set's root. mapping from object names to URIs relative to the HTML set's root.
* Projects using the Intersphinx extension can specify links to such mapping * Projects using the Intersphinx extension can specify links to such mapping
files in the `intersphinx_mapping` config value. The mapping will then be files in the `intersphinx_mapping` config value. The mapping will then be
used to resolve otherwise missing references to Python objects into links used to resolve otherwise missing references to objects into links to the
to the other documentation. other documentation.
* By default, the mapping file is assumed to be at the same location as the * 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 rest of the documentation; however, the location of the mapping file can
@ -25,6 +25,7 @@
""" """
import time import time
import zlib
import urllib2 import urllib2
import posixpath import posixpath
from os import path from os import path
@ -41,12 +42,68 @@ if hasattr(urllib2, 'HTTPSHandler'):
urllib2.install_opener(urllib2.build_opener(*handlers)) urllib2.install_opener(urllib2.build_opener(*handlers))
def read_inventory_v1(f, uri, join):
invdata = {}
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)
location = join(uri, location)
# version 1 did not add anchors to the location
if type == 'mod':
type = 'py:module'
location += '#module-' + name
else:
type = 'py:' + type
location += '#' + name
invdata.setdefault(type, {})[name] = (projname, version, location)
return invdata
def read_inventory_v2(f, uri, join, bufsize=16*1024):
invdata = {}
line = f.readline()
projname = line.rstrip()[11:].decode('utf-8')
line = f.readline()
version = line.rstrip()[11:]
line = f.readline()
if 'zlib' not in line:
raise ValueError
def read_chunks():
decompressor = zlib.decompressobj()
for chunk in iter(lambda: f.read(bufsize), ''):
yield decompressor.decompress(chunk)
yield decompressor.flush()
def split_lines(iter):
buf = ''
for chunk in iter:
buf += chunk
lineend = buf.find('\n')
while lineend != -1:
yield buf[:lineend]
buf = buf[lineend+1:]
lineend = buf.find('\n')
assert not buf
for line in split_lines(read_chunks()):
name, type, prio, location = line.rstrip().split(None, 3)
if location.endswith('$'):
location = location[:-1] + name
location = join(uri, location)
invdata.setdefault(type, {})[name] = (projname, version, location)
return invdata
def fetch_inventory(app, uri, inv): def fetch_inventory(app, uri, inv):
"""Fetch, parse and return an intersphinx inventory file.""" """Fetch, parse and return an intersphinx inventory file."""
invdata = {}
# both *uri* (base URI of the links to generate) and *inv* (actual # both *uri* (base URI of the links to generate) and *inv* (actual
# location of the inventory file) can be local or remote URIs # location of the inventory file) can be local or remote URIs
localuri = uri.find('://') == -1 localuri = uri.find('://') == -1
join = localuri and path.join or posixpath.join
try: try:
if inv.find('://') != -1: if inv.find('://') != -1:
f = urllib2.urlopen(inv) f = urllib2.urlopen(inv)
@ -57,24 +114,21 @@ def fetch_inventory(app, uri, inv):
'%s: %s' % (inv, err.__class__, err)) '%s: %s' % (inv, err.__class__, err))
return return
try: try:
line = f.next() line = f.readline().rstrip()
if line.rstrip() != '# Sphinx inventory version 1': try:
raise ValueError('unknown or unsupported inventory version') if line == '# Sphinx inventory version 1':
line = f.next() invdata = read_inventory_v1(f, uri, join)
projname = line.rstrip()[11:].decode('utf-8') elif line == '# Sphinx inventory version 2':
line = f.next() invdata = read_inventory_v2(f, uri, join)
version = line.rstrip()[11:]
for line in f:
name, type, location = line.rstrip().split(None, 2)
if localuri:
location = path.join(uri, location)
else: else:
location = posixpath.join(uri, location) raise ValueError
invdata[name] = (type, projname, version, location) f.close()
f.close() except ValueError:
f.close()
raise ValueError('unknown or unsupported inventory version')
except Exception, err: except Exception, err:
app.warn('intersphinx inventory %r not readable due to ' app.warn('intersphinx inventory %r not readable due to '
'%s: %s' % (inv, err.__class__, err)) '%s: %s' % (inv, err.__class__.__name__, err))
else: else:
return invdata return invdata
@ -110,31 +164,24 @@ def load_mappings(app):
def missing_reference(app, env, node, contnode): def missing_reference(app, env, node, contnode):
"""Attempt to resolve a missing reference via intersphinx references.""" """Attempt to resolve a missing reference via intersphinx references."""
type = node['reftype'] domain = node.get('refdomain')
if not domain:
# only objects in domains are in the inventory
return
target = node['reftarget'] target = node['reftarget']
if type == 'mod': objtypes = env.domains[domain].objtypes_for_role(node['reftype'])
type, proj, version, uri = env.intersphinx_inventory.get(target, if not objtypes:
('','','','')) return
if type != 'mod': for objtype in objtypes:
return None fulltype = '%s:%s' % (domain, objtype)
target = 'module-' + target # for link anchor if fulltype in env.intersphinx_inventory and \
target in env.intersphinx_inventory[fulltype]:
break
else: else:
if target[-2:] == '()': return
target = target[:-2] proj, version, uri = env.intersphinx_inventory[fulltype][target]
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 = nodes.reference('', '')
newnode['refuri'] = uri + '#' + target newnode['refuri'] = uri
newnode['reftitle'] = '(in %s v%s)' % (proj, version) newnode['reftitle'] = '(in %s v%s)' % (proj, version)
newnode['class'] = 'external-xref' newnode['class'] = 'external-xref'
newnode.append(contnode) newnode.append(contnode)

View File

@ -69,7 +69,7 @@ class Refcounts(dict):
def add_refcount_annotations(self, app, doctree): def add_refcount_annotations(self, app, doctree):
for node in doctree.traverse(addnodes.desc_content): for node in doctree.traverse(addnodes.desc_content):
par = node.parent par = node.parent
if par['desctype'] != 'cfunction': if par['domain'] != 'c' or par['objtype'] != 'function':
continue continue
if not par[0].has_key('names') or not par[0]['names']: if not par[0].has_key('names') or not par[0]['names']:
continue continue

View File

@ -34,9 +34,7 @@ class Todo(Directive):
def run(self): def run(self):
env = self.state.document.settings.env env = self.state.document.settings.env
targetid = 'index-%s' % env.new_serialno('index')
targetid = "todo-%s" % env.index_num
env.index_num += 1
targetnode = nodes.target('', '', ids=[targetid]) targetnode = nodes.target('', '', ids=[targetid])
ad = make_admonition(todo_node, self.name, [_('Todo')], self.options, ad = make_admonition(todo_node, self.name, [_('Todo')], self.options,

View File

@ -93,7 +93,7 @@ class BuiltinTemplateLoader(TemplateBridge, BaseLoader):
# make the paths into loaders # make the paths into loaders
self.loaders = map(SphinxFileSystemLoader, chain) self.loaders = map(SphinxFileSystemLoader, chain)
use_i18n = builder.translator is not None use_i18n = builder.app.translator is not None
extensions = use_i18n and ['jinja2.ext.i18n'] or [] extensions = use_i18n and ['jinja2.ext.i18n'] or []
self.environment = SandboxedEnvironment(loader=self, self.environment = SandboxedEnvironment(loader=self,
extensions=extensions) extensions=extensions)
@ -101,7 +101,8 @@ class BuiltinTemplateLoader(TemplateBridge, BaseLoader):
self.environment.globals['debug'] = contextfunction(pformat) self.environment.globals['debug'] = contextfunction(pformat)
self.environment.globals['accesskey'] = contextfunction(accesskey) self.environment.globals['accesskey'] = contextfunction(accesskey)
if use_i18n: if use_i18n:
self.environment.install_gettext_translations(builder.translator) self.environment.install_gettext_translations(
builder.app.translator)
def render(self, template, context): def render(self, template, context):
return self.environment.get_template(template).render(context) return self.environment.get_template(template).render(context)

View File

@ -8,41 +8,179 @@
:copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import gettext
import UserString
from sphinx import package_dir
class _TranslationProxy(UserString.UserString, object):
"""Class for proxy strings from gettext translations. This is a helper
for the lazy_* functions from this module.
The proxy implementation attempts to be as complete as possible, so that
the lazy objects should mostly work as expected, for example for sorting.
This inherits from UserString because some docutils versions use UserString
for their Text nodes, which then checks its argument for being either a
basestring or UserString, otherwise calls str() -- not unicode() -- on it.
This also inherits from object to make the __new__ method work.
"""
__slots__ = ('_func', '_args')
def __new__(cls, func, *args):
if not args:
# not called with "function" and "arguments", but a plain string
return unicode(func)
return object.__new__(cls)
def __init__(self, func, *args):
self._func = func
self._args = args
data = property(lambda x: x._func(*x._args))
def __contains__(self, key):
return key in self.data
def __nonzero__(self):
return bool(self.data)
def __dir__(self):
return dir(unicode)
def __iter__(self):
return iter(self.data)
def __len__(self):
return len(self.data)
def __str__(self):
return str(self.data)
def __unicode__(self):
return unicode(self.data)
def __add__(self, other):
return self.data + other
def __radd__(self, other):
return other + self.data
def __mod__(self, other):
return self.data % other
def __rmod__(self, other):
return other % self.data
def __mul__(self, other):
return self.data * other
def __rmul__(self, other):
return other * self.data
def __lt__(self, other):
return self.data < other
def __le__(self, other):
return self.data <= other
def __eq__(self, other):
return self.data == other
def __ne__(self, other):
return self.data != other
def __gt__(self, other):
return self.data > other
def __ge__(self, other):
return self.data >= other
def __getattr__(self, name):
if name == '__members__':
return self.__dir__()
return getattr(self.data, name)
def __getstate__(self):
return self._func, self._args
def __setstate__(self, tup):
self._func, self._args = tup
def __getitem__(self, key):
return self.data[key]
def __copy__(self):
return self
def __repr__(self):
try:
return 'i' + repr(unicode(self.data))
except:
return '<%s broken>' % self.__class__.__name__
def mygettext(string):
"""Used instead of _ when creating TranslationProxies, because _ is
not bound yet at that time."""
return _(string)
def lazy_gettext(string):
"""A lazy version of `gettext`."""
#if isinstance(string, _TranslationProxy):
# return string
return _TranslationProxy(mygettext, string)
l_ = lazy_gettext
_ = lambda x: x
admonitionlabels = { admonitionlabels = {
'attention': _('Attention'), 'attention': l_('Attention'),
'caution': _('Caution'), 'caution': l_('Caution'),
'danger': _('Danger'), 'danger': l_('Danger'),
'error': _('Error'), 'error': l_('Error'),
'hint': _('Hint'), 'hint': l_('Hint'),
'important': _('Important'), 'important': l_('Important'),
'note': _('Note'), 'note': l_('Note'),
'seealso': _('See Also'), 'seealso': l_('See Also'),
'tip': _('Tip'), 'tip': l_('Tip'),
'warning': _('Warning'), 'warning': l_('Warning'),
} }
versionlabels = { versionlabels = {
'versionadded': _('New in version %s'), 'versionadded': l_('New in version %s'),
'versionchanged': _('Changed in version %s'), 'versionchanged': l_('Changed in version %s'),
'deprecated': _('Deprecated since version %s'), 'deprecated': l_('Deprecated since version %s'),
} }
pairindextypes = { pairindextypes = {
'module': _('module'), 'module': l_('module'),
'keyword': _('keyword'), 'keyword': l_('keyword'),
'operator': _('operator'), 'operator': l_('operator'),
'object': _('object'), 'object': l_('object'),
'exception': _('exception'), 'exception': l_('exception'),
'statement': _('statement'), 'statement': l_('statement'),
'builtin': _('built-in function'), 'builtin': l_('built-in function'),
} }
del _
def init(): def init(locale_dirs, language):
for dct in (admonitionlabels, versionlabels, pairindextypes): # the None entry is the system's default locale path
for key in dct: translator = None
dct[key] = _(dct[key]) has_translation = True
for dir_ in locale_dirs:
try:
trans = gettext.translation('sphinx', localedir=dir_,
languages=[language])
if translator is None:
translator = trans
else:
translator._catalog.update(trans._catalog)
except Exception:
# Language couldn't be found in the specified path
pass
if translator is None:
translator = gettext.NullTranslations()
has_translation = False
translator.install(unicode=True)
return translator, has_translation

View File

@ -0,0 +1 @@
Documentation.addTranslations({"locale": "ca", "plural_expr": "(n != 1)", "messages": {"Search Results": "Resultats de la Cerca", "Preparing search...": "Preparant la cerca...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "La teva cerca no ha donat resultats. Assegura't que totes les paraules estan ben escrites i que has seleccionat prou categories.", "Search finished, found %s page(s) matching the search query.": "Cerca finalitzada, s'han trobat %s p\u00e0gin(a/es) de resultats.", ", in ": ", a ", "Permalink to this headline": "Link permanent a aquest t\u00edtol", "Searching": "Cercant", "Permalink to this definition": "Link permanent a aquesta definici\u00f3", "module, in ": "m\u00f2dule, a ", "Hide Search Matches": "Oculta Resultats de Cerca"}});

Binary file not shown.

View File

@ -0,0 +1,606 @@
# Catalan translations for Sphinx.
# Copyright (C) 2009 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: Sphinx 1.0\n"
"Report-Msgid-Bugs-To: pau.fernandez@upc.edu\n"
"POT-Creation-Date: 2009-05-22 18:51+0200\n"
"PO-Revision-Date: 2009-06-07 14:20+0200\n"
"Last-Translator: Pau Fernández <pau.fernandez@upc.edu>\n"
"Language-Team: ca <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.4\n"
#: sphinx/environment.py:104 sphinx/writers/latex.py:184
#, python-format
msgid "%B %d, %Y"
msgstr "%d de %B de %Y"
#: sphinx/environment.py:323 sphinx/themes/basic/genindex-single.html:2
#: sphinx/themes/basic/genindex-split.html:2
#: sphinx/themes/basic/genindex-split.html:5
#: sphinx/themes/basic/genindex.html:2 sphinx/themes/basic/genindex.html:5
#: sphinx/themes/basic/genindex.html:48 sphinx/themes/basic/layout.html:131
#: sphinx/writers/latex.py:190
msgid "Index"
msgstr "Índex"
#: sphinx/environment.py:324 sphinx/writers/latex.py:189
msgid "Module Index"
msgstr "Índex de Mòduls"
#: sphinx/environment.py:325 sphinx/themes/basic/defindex.html:16
msgid "Search Page"
msgstr "Pàgina de Cerca"
#: sphinx/roles.py:55 sphinx/directives/desc.py:747
#, python-format
msgid "environment variable; %s"
msgstr "variable d'entorn; %s"
#: sphinx/roles.py:62
#, python-format
msgid "Python Enhancement Proposals!PEP %s"
msgstr "Python Enhancement Proposals!PEP %s"
#: sphinx/builders/changes.py:71
msgid "Builtins"
msgstr "Mòduls Interns"
#: sphinx/builders/changes.py:73
msgid "Module level"
msgstr "Nivell de mòdul"
#: sphinx/builders/html.py:219
#, python-format
msgid "%b %d, %Y"
msgstr "%d %b, %Y"
#: sphinx/builders/html.py:238 sphinx/themes/basic/defindex.html:21
msgid "General Index"
msgstr "Índex General"
#: sphinx/builders/html.py:238
msgid "index"
msgstr "índex"
#: sphinx/builders/html.py:240 sphinx/builders/htmlhelp.py:184
#: sphinx/builders/qthelp.py:132 sphinx/themes/basic/defindex.html:19
#: sphinx/themes/basic/modindex.html:2 sphinx/themes/basic/modindex.html:13
msgid "Global Module Index"
msgstr "Índex Global de Mòduls"
#: sphinx/builders/html.py:241
msgid "modules"
msgstr "mòduls"
#: sphinx/builders/html.py:296
msgid "next"
msgstr "següent"
#: sphinx/builders/html.py:305
msgid "previous"
msgstr "anterior"
#: sphinx/builders/latex.py:162
msgid " (in "
msgstr " (a "
#: sphinx/directives/desc.py:97
msgid "Raises"
msgstr "Llença"
#: sphinx/directives/desc.py:101
msgid "Variable"
msgstr "Variable"
#: sphinx/directives/desc.py:104
msgid "Returns"
msgstr "Retorna"
#: sphinx/directives/desc.py:113
msgid "Return type"
msgstr "Tipus de retorn"
#: sphinx/directives/desc.py:186
msgid "Parameter"
msgstr "Paràmetre"
#: sphinx/directives/desc.py:190
msgid "Parameters"
msgstr "Paràmetres"
#: sphinx/directives/desc.py:418
#, python-format
msgid "%s() (built-in function)"
msgstr "%s() (funció interna)"
#: sphinx/directives/desc.py:419 sphinx/directives/desc.py:476
#: sphinx/directives/desc.py:488
#, python-format
msgid "%s() (in module %s)"
msgstr "%s() (al mòdul %s)"
#: sphinx/directives/desc.py:422
#, python-format
msgid "%s (built-in variable)"
msgstr "%s (variable interna)"
#: sphinx/directives/desc.py:423 sphinx/directives/desc.py:514
#, python-format
msgid "%s (in module %s)"
msgstr "%s (al mòdul %s)"
#: sphinx/directives/desc.py:439
#, python-format
msgid "%s (built-in class)"
msgstr "%s (classe interna)"
#: sphinx/directives/desc.py:440
#, python-format
msgid "%s (class in %s)"
msgstr "%s (class a %s)"
#: sphinx/directives/desc.py:480
#, python-format
msgid "%s() (%s.%s method)"
msgstr "%s() (mètode %s.%s)"
#: sphinx/directives/desc.py:482
#, python-format
msgid "%s() (%s method)"
msgstr "%s() (mètode %s)"
#: sphinx/directives/desc.py:492
#, python-format
msgid "%s() (%s.%s static method)"
msgstr "%s() (mètode estàtic %s.%s)"
#: sphinx/directives/desc.py:495
#, python-format
msgid "%s() (%s static method)"
msgstr "%s() (mètode estàtic %s)"
#: sphinx/directives/desc.py:518
#, python-format
msgid "%s (%s.%s attribute)"
msgstr "%s (atribut %s.%s)"
#: sphinx/directives/desc.py:520
#, python-format
msgid "%s (%s attribute)"
msgstr "%s (atribut %s)"
#: sphinx/directives/desc.py:609
#, python-format
msgid "%s (C function)"
msgstr "%s (funció de C)"
#: sphinx/directives/desc.py:611
#, python-format
msgid "%s (C member)"
msgstr "%s (membre de C)"
#: sphinx/directives/desc.py:613
#, python-format
msgid "%s (C macro)"
msgstr "%s (macro de C)"
#: sphinx/directives/desc.py:615
#, python-format
msgid "%s (C type)"
msgstr "%s (tipus de C)"
#: sphinx/directives/desc.py:617
#, python-format
msgid "%s (C variable)"
msgstr "%s (variable de C)"
#: sphinx/directives/desc.py:665
#, python-format
msgid "%scommand line option; %s"
msgstr "opció de línia de comandes %s; %s"
#: sphinx/directives/other.py:138
msgid "Platforms: "
msgstr "Plataformes: "
#: sphinx/directives/other.py:144
#, python-format
msgid "%s (module)"
msgstr "%s (mòdul)"
#: sphinx/directives/other.py:193
msgid "Section author: "
msgstr "Autor de la secció:"
#: sphinx/directives/other.py:195
msgid "Module author: "
msgstr "Autor del mòdul: "
#: sphinx/directives/other.py:197
msgid "Author: "
msgstr "Autor: "
#: sphinx/directives/other.py:317
msgid "See also"
msgstr "Vegeu també"
#: sphinx/ext/autodoc.py:888
#, python-format
msgid " Bases: %s"
msgstr " Bases: %s"
#: sphinx/ext/autodoc.py:919
#, python-format
msgid "alias of :class:`%s`"
msgstr "àlies de :class:`%s`"
#: sphinx/ext/todo.py:41
msgid "Todo"
msgstr "Pendent"
#: sphinx/ext/todo.py:99
#, python-format
msgid "(The original entry is located in %s, line %d and can be found "
msgstr "(La entrada original està a %s, línia %d i es pot trobar "
#: sphinx/ext/todo.py:105
msgid "here"
msgstr "aquí"
#: sphinx/locale/__init__.py:15
msgid "Attention"
msgstr "Atenció"
#: sphinx/locale/__init__.py:16
msgid "Caution"
msgstr "Compte"
#: sphinx/locale/__init__.py:17
msgid "Danger"
msgstr "Perill"
#: sphinx/locale/__init__.py:18
msgid "Error"
msgstr "Error"
#: sphinx/locale/__init__.py:19
msgid "Hint"
msgstr "Suggerència"
#: sphinx/locale/__init__.py:20
msgid "Important"
msgstr "Important"
#: sphinx/locale/__init__.py:21
msgid "Note"
msgstr "Nota"
#: sphinx/locale/__init__.py:22
msgid "See Also"
msgstr "Vegeu També"
#: sphinx/locale/__init__.py:23
msgid "Tip"
msgstr "Truc"
#: sphinx/locale/__init__.py:24
msgid "Warning"
msgstr "Avís"
#: sphinx/locale/__init__.py:28
#, python-format
msgid "New in version %s"
msgstr "Novetat de la versió %s"
#: sphinx/locale/__init__.py:29
#, python-format
msgid "Changed in version %s"
msgstr "Canviat a la versió %s"
#: sphinx/locale/__init__.py:30
#, python-format
msgid "Deprecated since version %s"
msgstr "Obsolet desde la versió %s"
#: sphinx/locale/__init__.py:34
msgid "module"
msgstr "mòdul"
#: sphinx/locale/__init__.py:35
msgid "keyword"
msgstr "paraula clau"
#: sphinx/locale/__init__.py:36
msgid "operator"
msgstr "operador"
#: sphinx/locale/__init__.py:37
msgid "object"
msgstr "objecte"
#: sphinx/locale/__init__.py:38
msgid "exception"
msgstr "excepció"
#: sphinx/locale/__init__.py:39
msgid "statement"
msgstr "sentència"
#: sphinx/locale/__init__.py:40
msgid "built-in function"
msgstr "funció interna"
#: sphinx/themes/basic/defindex.html:2
msgid "Overview"
msgstr "Resum"
#: sphinx/themes/basic/defindex.html:11
msgid "Indices and tables:"
msgstr "Índexs i taules:"
#: sphinx/themes/basic/defindex.html:14
msgid "Complete Table of Contents"
msgstr "Taula de Contingut Completa"
#: sphinx/themes/basic/defindex.html:15
msgid "lists all sections and subsections"
msgstr "llista totes les seccions i subseccions"
#: sphinx/themes/basic/defindex.html:17
msgid "search this documentation"
msgstr "cerca aquesta documentació"
#: sphinx/themes/basic/defindex.html:20
msgid "quick access to all modules"
msgstr "accés ràpid a tots els mòduls"
#: sphinx/themes/basic/defindex.html:22
msgid "all functions, classes, terms"
msgstr "totes les funcions, classes, termes"
#: sphinx/themes/basic/genindex-single.html:5
#, python-format
msgid "Index &ndash; %(key)s"
msgstr "Índes &ndash; %(key)s"
#: sphinx/themes/basic/genindex-single.html:44
#: sphinx/themes/basic/genindex-split.html:14
#: sphinx/themes/basic/genindex-split.html:27
#: sphinx/themes/basic/genindex.html:54
msgid "Full index on one page"
msgstr "Índex complet en una pàgina"
#: sphinx/themes/basic/genindex-split.html:7
msgid "Index pages by letter"
msgstr "Pàgines d'índex per lletra"
#: sphinx/themes/basic/genindex-split.html:15
msgid "can be huge"
msgstr "pot ser gegant"
#: sphinx/themes/basic/layout.html:10
msgid "Navigation"
msgstr "Navegació"
#: sphinx/themes/basic/layout.html:42
msgid "Table Of Contents"
msgstr "Taula de Contingut"
#: sphinx/themes/basic/layout.html:48
msgid "Previous topic"
msgstr "Tema anterior"
#: sphinx/themes/basic/layout.html:50
msgid "previous chapter"
msgstr "capítol anterior"
#: sphinx/themes/basic/layout.html:53
msgid "Next topic"
msgstr "Tema següent"
#: sphinx/themes/basic/layout.html:55
msgid "next chapter"
msgstr "capítol següent"
#: sphinx/themes/basic/layout.html:60
msgid "This Page"
msgstr "Aquesta Pàgina"
#: sphinx/themes/basic/layout.html:63
msgid "Show Source"
msgstr "Mostra Codi Font"
#: sphinx/themes/basic/layout.html:73
msgid "Quick search"
msgstr "Cerca ràpida"
#: sphinx/themes/basic/layout.html:76
msgid "Go"
msgstr "Ves a"
#: sphinx/themes/basic/layout.html:81
msgid "Enter search terms or a module, class or function name."
msgstr "Entra paraules de cerca o el nom d'un mòdul, classe o funció."
#: sphinx/themes/basic/layout.html:119
#, python-format
msgid "Search within %(docstitle)s"
msgstr "Cerca dins de %(docstitle)s"
#: sphinx/themes/basic/layout.html:128
msgid "About these documents"
msgstr "Quant a aquests documents"
#: sphinx/themes/basic/layout.html:134 sphinx/themes/basic/search.html:2
#: sphinx/themes/basic/search.html:5
msgid "Search"
msgstr "Cerca"
#: sphinx/themes/basic/layout.html:137
msgid "Copyright"
msgstr "Copyright"
#: sphinx/themes/basic/layout.html:184
#, python-format
msgid "&copy; <a href=\"%(path)s\">Copyright</a> %(copyright)s."
msgstr "&copy; <a href=\\\"%(path)s\\\">Copyright</a> %(copyright)s."
#: sphinx/themes/basic/layout.html:186
#, python-format
msgid "&copy; Copyright %(copyright)s."
msgstr "&copy; Copyright %(copyright)s."
#: sphinx/themes/basic/layout.html:190
#, python-format
msgid "Last updated on %(last_updated)s."
msgstr "Última actualització el %(last_updated)s."
#: sphinx/themes/basic/layout.html:193
#, python-format
msgid ""
"Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> "
"%(sphinx_version)s."
msgstr ""
"Creat amb <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> "
"%(sphinx_version)s."
#: sphinx/themes/basic/modindex.html:36
msgid "Deprecated"
msgstr "Obsolet"
#: sphinx/themes/basic/opensearch.xml:4
#, python-format
msgid "Search %(docstitle)s"
msgstr "Cercar a %(docstitle)s"
#: sphinx/themes/basic/search.html:9
msgid ""
"Please activate JavaScript to enable the search\n"
" functionality."
msgstr ""
"Activa JavaScript per utilitzar la funcionalitat\n"
"de cerca."
#: sphinx/themes/basic/search.html:14
msgid ""
"From here you can search these documents. Enter your search\n"
" words into the box below and click \"search\". Note that the search\n"
" function will automatically search for all of the words. Pages\n"
" containing fewer words won't appear in the result list."
msgstr ""
"Des d'aquí pots fer cerques en aquests documents. Entra les \n"
"paraules de la teva cerca i clica el botó \"cerca\". Tingues en compte\n"
"que la cerca inclourà totes les paraules que posis. Les pàgines que no\n"
"tenen totes les paraules no sortiràn."
#: sphinx/themes/basic/search.html:21
msgid "search"
msgstr "cerca"
#: sphinx/themes/basic/search.html:25
#: sphinx/themes/basic/static/searchtools.js:462
msgid "Search Results"
msgstr "Resultats de la Cerca"
#: sphinx/themes/basic/search.html:27
msgid "Your search did not match any results."
msgstr "La teva cerca no té resultats."
#: sphinx/themes/basic/changes/frameset.html:5
#: sphinx/themes/basic/changes/versionchanges.html:12
#, python-format
msgid "Changes in Version %(version)s &mdash; %(docstitle)s"
msgstr "Canvis a la Versió %(version)s &mdash; %(docstitle)s"
#: sphinx/themes/basic/changes/rstsource.html:5
#, python-format
msgid "%(filename)s &mdash; %(docstitle)s"
msgstr "%(filename)s &mdash; %(docstitle)s"
#: sphinx/themes/basic/changes/versionchanges.html:17
#, python-format
msgid "Automatically generated list of changes in version %(version)s"
msgstr "Llista de canvis de la versió %(version)s generada automàticament"
#: sphinx/themes/basic/changes/versionchanges.html:18
msgid "Library changes"
msgstr "Canvis a la llibreria"
#: sphinx/themes/basic/changes/versionchanges.html:23
msgid "C API changes"
msgstr "Canvis a la API de C"
#: sphinx/themes/basic/changes/versionchanges.html:25
msgid "Other changes"
msgstr "Altres canvis"
#: sphinx/themes/basic/static/doctools.js:139 sphinx/writers/html.py:473
#: sphinx/writers/html.py:478
msgid "Permalink to this headline"
msgstr "Link permanent a aquest títol"
#: sphinx/themes/basic/static/doctools.js:145 sphinx/writers/html.py:80
msgid "Permalink to this definition"
msgstr "Link permanent a aquesta definició"
#: sphinx/themes/basic/static/doctools.js:174
msgid "Hide Search Matches"
msgstr "Oculta Resultats de Cerca"
#: sphinx/themes/basic/static/searchtools.js:274
msgid "Searching"
msgstr "Cercant"
#: sphinx/themes/basic/static/searchtools.js:279
msgid "Preparing search..."
msgstr "Preparant la cerca..."
#: sphinx/themes/basic/static/searchtools.js:347
msgid "module, in "
msgstr "mòdule, a "
#: sphinx/themes/basic/static/searchtools.js:356
msgid ", in "
msgstr ", a "
#: sphinx/themes/basic/static/searchtools.js:464
msgid ""
"Your search did not match any documents. Please make sure that all words "
"are spelled correctly and that you've selected enough categories."
msgstr ""
"La teva cerca no ha donat resultats. Assegura't que totes les paraules "
"estan ben escrites i que has seleccionat prou categories."
#: sphinx/themes/basic/static/searchtools.js:466
#, python-format
msgid "Search finished, found %s page(s) matching the search query."
msgstr "Cerca finalitzada, s'han trobat %s pàgin(a/es) de resultats."
#: sphinx/writers/latex.py:187
msgid "Release"
msgstr "Versió"
#: sphinx/writers/latex.py:639
msgid "continued from previous page"
msgstr "ve de la pàgina anterior"
#: sphinx/writers/latex.py:643
msgid "Continued on next page"
msgstr "Continua a la pàgina següent"
#: sphinx/writers/text.py:166
#, python-format
msgid "Platform: %s"
msgstr "Plataforma: %s"
#: sphinx/writers/text.py:428
msgid "[image]"
msgstr "[imatge]"

View File

@ -6,9 +6,9 @@
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Sphinx 1.0\n" "Project-Id-Version: Sphinx 1.0pre/[?1034h2e1ab15e035e\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2009-08-06 23:04+0200\n" "POT-Creation-Date: 2009-11-08 16:28+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,12 +17,12 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.4\n" "Generated-By: Babel 0.9.4\n"
#: sphinx/environment.py:103 sphinx/writers/latex.py:184 #: sphinx/environment.py:130 sphinx/writers/latex.py:184
#, python-format #, python-format
msgid "%B %d, %Y" msgid "%B %d, %Y"
msgstr "" msgstr ""
#: sphinx/environment.py:324 sphinx/themes/basic/genindex-single.html:2 #: sphinx/environment.py:348 sphinx/themes/basic/genindex-single.html:2
#: sphinx/themes/basic/genindex-split.html:2 #: sphinx/themes/basic/genindex-split.html:2
#: sphinx/themes/basic/genindex-split.html:5 #: sphinx/themes/basic/genindex-split.html:5
#: sphinx/themes/basic/genindex.html:2 sphinx/themes/basic/genindex.html:5 #: sphinx/themes/basic/genindex.html:2 sphinx/themes/basic/genindex.html:5
@ -31,60 +31,56 @@ msgstr ""
msgid "Index" msgid "Index"
msgstr "" msgstr ""
#: sphinx/environment.py:325 sphinx/writers/latex.py:189 #: sphinx/environment.py:349 sphinx/writers/latex.py:189
msgid "Module Index" msgid "Module Index"
msgstr "" msgstr ""
#: sphinx/environment.py:326 sphinx/themes/basic/defindex.html:16 #: sphinx/environment.py:350 sphinx/themes/basic/defindex.html:16
msgid "Search Page" msgid "Search Page"
msgstr "" msgstr ""
#: sphinx/roles.py:55 sphinx/directives/desc.py:747 #: sphinx/roles.py:167
#, python-format
msgid "environment variable; %s"
msgstr ""
#: sphinx/roles.py:62
#, python-format #, python-format
msgid "Python Enhancement Proposals!PEP %s" msgid "Python Enhancement Proposals!PEP %s"
msgstr "" msgstr ""
#: sphinx/builders/changes.py:71 #: sphinx/builders/changes.py:70
msgid "Builtins" msgid "Builtins"
msgstr "" msgstr ""
#: sphinx/builders/changes.py:73 #: sphinx/builders/changes.py:72
msgid "Module level" msgid "Module level"
msgstr "" msgstr ""
#: sphinx/builders/html.py:222 #: sphinx/builders/html.py:224
#, python-format #, python-format
msgid "%b %d, %Y" msgid "%b %d, %Y"
msgstr "" msgstr ""
#: sphinx/builders/html.py:241 sphinx/themes/basic/defindex.html:21 #: sphinx/builders/html.py:243 sphinx/themes/basic/defindex.html:21
msgid "General Index" msgid "General Index"
msgstr "" msgstr ""
#: sphinx/builders/html.py:241 #: sphinx/builders/html.py:243
msgid "index" msgid "index"
msgstr "" msgstr ""
#: sphinx/builders/html.py:243 sphinx/builders/htmlhelp.py:219 #: sphinx/builders/html.py:247 sphinx/builders/htmlhelp.py:220
#: sphinx/builders/qthelp.py:133 sphinx/themes/basic/defindex.html:19 #: sphinx/builders/qthelp.py:133 sphinx/themes/basic/defindex.html:19
#: sphinx/themes/basic/modindex.html:2 sphinx/themes/basic/modindex.html:13 #: sphinx/themes/basic/modindex.html:2 sphinx/themes/basic/modindex.html:13
#: sphinx/themes/scrolls/modindex.html:2 sphinx/themes/scrolls/modindex.html:13
msgid "Global Module Index" msgid "Global Module Index"
msgstr "" msgstr ""
#: sphinx/builders/html.py:244 #: sphinx/builders/html.py:248
msgid "modules" msgid "modules"
msgstr "" msgstr ""
#: sphinx/builders/html.py:300 #: sphinx/builders/html.py:304
msgid "next" msgid "next"
msgstr "" msgstr ""
#: sphinx/builders/html.py:309 #: sphinx/builders/html.py:313
msgid "previous" msgid "previous"
msgstr "" msgstr ""
@ -92,249 +88,323 @@ msgstr ""
msgid " (in " msgid " (in "
msgstr "" msgstr ""
#: sphinx/directives/desc.py:97 #: sphinx/directives/__init__.py:78 sphinx/directives/__init__.py:79
#: sphinx/directives/__init__.py:80 sphinx/directives/__init__.py:81
msgid "Raises" msgid "Raises"
msgstr "" msgstr ""
#: sphinx/directives/desc.py:101 #: sphinx/directives/__init__.py:82 sphinx/directives/__init__.py:83
#: sphinx/directives/__init__.py:84
msgid "Variable" msgid "Variable"
msgstr "" msgstr ""
#: sphinx/directives/desc.py:104 #: sphinx/directives/__init__.py:85 sphinx/directives/__init__.py:86
#: sphinx/directives/__init__.py:92 sphinx/directives/__init__.py:93
msgid "Returns" msgid "Returns"
msgstr "" msgstr ""
#: sphinx/directives/desc.py:113 #: sphinx/directives/__init__.py:94
msgid "Return type" msgid "Return type"
msgstr "" msgstr ""
#: sphinx/directives/desc.py:186 #: sphinx/directives/__init__.py:169
msgid "Parameter" msgid "Parameter"
msgstr "" msgstr ""
#: sphinx/directives/desc.py:190 #: sphinx/directives/__init__.py:173
msgid "Parameters" msgid "Parameters"
msgstr "" msgstr ""
#: sphinx/directives/desc.py:418 #: sphinx/directives/other.py:127
#, python-format msgid "Section author: "
msgid "%s() (built-in function)"
msgstr "" msgstr ""
#: sphinx/directives/desc.py:419 sphinx/directives/desc.py:476 #: sphinx/directives/other.py:129
#: sphinx/directives/desc.py:488 msgid "Module author: "
#, python-format
msgid "%s() (in module %s)"
msgstr "" msgstr ""
#: sphinx/directives/desc.py:422 #: sphinx/directives/other.py:131
#, python-format msgid "Author: "
msgid "%s (built-in variable)"
msgstr "" msgstr ""
#: sphinx/directives/desc.py:423 sphinx/directives/desc.py:514 #: sphinx/directives/other.py:233
#, python-format msgid "See also"
msgid "%s (in module %s)"
msgstr "" msgstr ""
#: sphinx/directives/desc.py:439 #: sphinx/domains/c.py:124
#, python-format
msgid "%s (built-in class)"
msgstr ""
#: sphinx/directives/desc.py:440
#, python-format
msgid "%s (class in %s)"
msgstr ""
#: sphinx/directives/desc.py:480
#, python-format
msgid "%s() (%s.%s method)"
msgstr ""
#: sphinx/directives/desc.py:482
#, python-format
msgid "%s() (%s method)"
msgstr ""
#: sphinx/directives/desc.py:492
#, python-format
msgid "%s() (%s.%s static method)"
msgstr ""
#: sphinx/directives/desc.py:495
#, python-format
msgid "%s() (%s static method)"
msgstr ""
#: sphinx/directives/desc.py:518
#, python-format
msgid "%s (%s.%s attribute)"
msgstr ""
#: sphinx/directives/desc.py:520
#, python-format
msgid "%s (%s attribute)"
msgstr ""
#: sphinx/directives/desc.py:609
#, python-format #, python-format
msgid "%s (C function)" msgid "%s (C function)"
msgstr "" msgstr ""
#: sphinx/directives/desc.py:611 #: sphinx/domains/c.py:126
#, python-format #, python-format
msgid "%s (C member)" msgid "%s (C member)"
msgstr "" msgstr ""
#: sphinx/directives/desc.py:613 #: sphinx/domains/c.py:128
#, python-format #, python-format
msgid "%s (C macro)" msgid "%s (C macro)"
msgstr "" msgstr ""
#: sphinx/directives/desc.py:615 #: sphinx/domains/c.py:130
#, python-format #, python-format
msgid "%s (C type)" msgid "%s (C type)"
msgstr "" msgstr ""
#: sphinx/directives/desc.py:617 #: sphinx/domains/c.py:132
#, python-format #, python-format
msgid "%s (C variable)" msgid "%s (C variable)"
msgstr "" msgstr ""
#: sphinx/directives/desc.py:665 #: sphinx/domains/c.py:162
#, python-format msgid "C function"
msgid "%scommand line option; %s"
msgstr "" msgstr ""
#: sphinx/directives/other.py:140 #: sphinx/domains/c.py:163
msgid "C member"
msgstr ""
#: sphinx/domains/c.py:164
msgid "C macro"
msgstr ""
#: sphinx/domains/c.py:165
msgid "C type"
msgstr ""
#: sphinx/domains/c.py:166
msgid "C variable"
msgstr ""
#: sphinx/domains/python.py:186
#, python-format
msgid "%s() (built-in function)"
msgstr ""
#: sphinx/domains/python.py:187 sphinx/domains/python.py:244
#: sphinx/domains/python.py:256 sphinx/domains/python.py:269
#, python-format
msgid "%s() (in module %s)"
msgstr ""
#: sphinx/domains/python.py:190
#, python-format
msgid "%s (built-in variable)"
msgstr ""
#: sphinx/domains/python.py:191 sphinx/domains/python.py:282
#, python-format
msgid "%s (in module %s)"
msgstr ""
#: sphinx/domains/python.py:207
#, python-format
msgid "%s (built-in class)"
msgstr ""
#: sphinx/domains/python.py:208
#, python-format
msgid "%s (class in %s)"
msgstr ""
#: sphinx/domains/python.py:248
#, python-format
msgid "%s() (%s.%s method)"
msgstr ""
#: sphinx/domains/python.py:250
#, python-format
msgid "%s() (%s method)"
msgstr ""
#: sphinx/domains/python.py:260
#, python-format
msgid "%s() (%s.%s static method)"
msgstr ""
#: sphinx/domains/python.py:263
#, python-format
msgid "%s() (%s static method)"
msgstr ""
#: sphinx/domains/python.py:273
#, python-format
msgid "%s() (%s.%s class method)"
msgstr ""
#: sphinx/domains/python.py:276
#, python-format
msgid "%s() (%s class method)"
msgstr ""
#: sphinx/domains/python.py:286
#, python-format
msgid "%s (%s.%s attribute)"
msgstr ""
#: sphinx/domains/python.py:288
#, python-format
msgid "%s (%s attribute)"
msgstr ""
#: sphinx/domains/python.py:334
msgid "Platforms: " msgid "Platforms: "
msgstr "" msgstr ""
#: sphinx/directives/other.py:146 #: sphinx/domains/python.py:340
#, python-format #, python-format
msgid "%s (module)" msgid "%s (module)"
msgstr "" msgstr ""
#: sphinx/directives/other.py:195 #: sphinx/domains/python.py:396
msgid "Section author: " msgid "function"
msgstr "" msgstr ""
#: sphinx/directives/other.py:197 #: sphinx/domains/python.py:397
msgid "Module author: " msgid "data"
msgstr "" msgstr ""
#: sphinx/directives/other.py:199 #: sphinx/domains/python.py:398
msgid "Author: " msgid "class"
msgstr "" msgstr ""
#: sphinx/directives/other.py:319 #: sphinx/domains/python.py:399 sphinx/locale/__init__.py:161
msgid "See also" msgid "exception"
msgstr "" msgstr ""
#: sphinx/ext/autodoc.py:889 #: sphinx/domains/python.py:400
msgid "method"
msgstr ""
#: sphinx/domains/python.py:401
msgid "attribute"
msgstr ""
#: sphinx/domains/python.py:402 sphinx/locale/__init__.py:157
msgid "module"
msgstr ""
#: sphinx/domains/std.py:67 sphinx/domains/std.py:83
#, python-format
msgid "environment variable; %s"
msgstr ""
#: sphinx/domains/std.py:156
#, python-format
msgid "%scommand line option; %s"
msgstr ""
#: sphinx/domains/std.py:324
msgid "glossary term"
msgstr ""
#: sphinx/domains/std.py:325
msgid "grammar token"
msgstr ""
#: sphinx/domains/std.py:326
msgid "environment variable"
msgstr ""
#: sphinx/domains/std.py:327
msgid "program option"
msgstr ""
#: sphinx/ext/autodoc.py:892
#, python-format #, python-format
msgid " Bases: %s" msgid " Bases: %s"
msgstr "" msgstr ""
#: sphinx/ext/autodoc.py:922 #: sphinx/ext/autodoc.py:925
#, python-format #, python-format
msgid "alias of :class:`%s`" msgid "alias of :class:`%s`"
msgstr "" msgstr ""
#: sphinx/ext/todo.py:41 #: sphinx/ext/todo.py:40
msgid "Todo" msgid "Todo"
msgstr "" msgstr ""
#: sphinx/ext/todo.py:99 #: sphinx/ext/todo.py:98
#, python-format #, python-format
msgid "(The original entry is located in %s, line %d and can be found " msgid "(The original entry is located in %s, line %d and can be found "
msgstr "" msgstr ""
#: sphinx/ext/todo.py:105 #: sphinx/ext/todo.py:104
msgid "here" msgid "here"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:15 #: sphinx/locale/__init__.py:138
msgid "Attention" msgid "Attention"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:16 #: sphinx/locale/__init__.py:139
msgid "Caution" msgid "Caution"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:17 #: sphinx/locale/__init__.py:140
msgid "Danger" msgid "Danger"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:18 #: sphinx/locale/__init__.py:141
msgid "Error" msgid "Error"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:19 #: sphinx/locale/__init__.py:142
msgid "Hint" msgid "Hint"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:20 #: sphinx/locale/__init__.py:143
msgid "Important" msgid "Important"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:21 #: sphinx/locale/__init__.py:144
msgid "Note" msgid "Note"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:22 #: sphinx/locale/__init__.py:145
msgid "See Also" msgid "See Also"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:23 #: sphinx/locale/__init__.py:146
msgid "Tip" msgid "Tip"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:24 #: sphinx/locale/__init__.py:147
msgid "Warning" msgid "Warning"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:28 #: sphinx/locale/__init__.py:151
#, python-format #, python-format
msgid "New in version %s" msgid "New in version %s"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:29 #: sphinx/locale/__init__.py:152
#, python-format #, python-format
msgid "Changed in version %s" msgid "Changed in version %s"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:30 #: sphinx/locale/__init__.py:153
#, python-format #, python-format
msgid "Deprecated since version %s" msgid "Deprecated since version %s"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:34 #: sphinx/locale/__init__.py:158
msgid "module"
msgstr ""
#: sphinx/locale/__init__.py:35
msgid "keyword" msgid "keyword"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:36 #: sphinx/locale/__init__.py:159
msgid "operator" msgid "operator"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:37 #: sphinx/locale/__init__.py:160
msgid "object" msgid "object"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:38 #: sphinx/locale/__init__.py:162
msgid "exception"
msgstr ""
#: sphinx/locale/__init__.py:39
msgid "statement" msgid "statement"
msgstr "" msgstr ""
#: sphinx/locale/__init__.py:40 #: sphinx/locale/__init__.py:163
msgid "built-in function" msgid "built-in function"
msgstr "" msgstr ""
@ -448,29 +518,29 @@ msgstr ""
msgid "Copyright" msgid "Copyright"
msgstr "" msgstr ""
#: sphinx/themes/basic/layout.html:187 #: sphinx/themes/basic/layout.html:187 sphinx/themes/scrolls/layout.html:83
#, python-format #, python-format
msgid "&copy; <a href=\"%(path)s\">Copyright</a> %(copyright)s." msgid "&copy; <a href=\"%(path)s\">Copyright</a> %(copyright)s."
msgstr "" msgstr ""
#: sphinx/themes/basic/layout.html:189 #: sphinx/themes/basic/layout.html:189 sphinx/themes/scrolls/layout.html:85
#, python-format #, python-format
msgid "&copy; Copyright %(copyright)s." msgid "&copy; Copyright %(copyright)s."
msgstr "" msgstr ""
#: sphinx/themes/basic/layout.html:193 #: sphinx/themes/basic/layout.html:193 sphinx/themes/scrolls/layout.html:89
#, python-format #, python-format
msgid "Last updated on %(last_updated)s." msgid "Last updated on %(last_updated)s."
msgstr "" msgstr ""
#: sphinx/themes/basic/layout.html:196 #: sphinx/themes/basic/layout.html:196 sphinx/themes/scrolls/layout.html:92
#, python-format #, python-format
msgid "" msgid ""
"Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> "
"%(sphinx_version)s." "%(sphinx_version)s."
msgstr "" msgstr ""
#: sphinx/themes/basic/modindex.html:36 #: sphinx/themes/basic/modindex.html:36 sphinx/themes/scrolls/modindex.html:37
msgid "Deprecated" msgid "Deprecated"
msgstr "" msgstr ""
@ -498,7 +568,7 @@ msgid "search"
msgstr "" msgstr ""
#: sphinx/themes/basic/search.html:25 #: sphinx/themes/basic/search.html:25
#: sphinx/themes/basic/static/searchtools.js:462 #: sphinx/themes/basic/static/searchtools.js:473
msgid "Search Results" msgid "Search Results"
msgstr "" msgstr ""
@ -534,16 +604,16 @@ msgstr ""
msgid "Other changes" msgid "Other changes"
msgstr "" msgstr ""
#: sphinx/themes/basic/static/doctools.js:139 sphinx/writers/html.py:473 #: sphinx/themes/basic/static/doctools.js:138 sphinx/writers/html.py:462
#: sphinx/writers/html.py:478 #: sphinx/writers/html.py:467
msgid "Permalink to this headline" msgid "Permalink to this headline"
msgstr "" msgstr ""
#: sphinx/themes/basic/static/doctools.js:145 sphinx/writers/html.py:80 #: sphinx/themes/basic/static/doctools.js:144 sphinx/writers/html.py:80
msgid "Permalink to this definition" msgid "Permalink to this definition"
msgstr "" msgstr ""
#: sphinx/themes/basic/static/doctools.js:174 #: sphinx/themes/basic/static/doctools.js:173
msgid "Hide Search Matches" msgid "Hide Search Matches"
msgstr "" msgstr ""
@ -555,21 +625,17 @@ msgstr ""
msgid "Preparing search..." msgid "Preparing search..."
msgstr "" msgstr ""
#: sphinx/themes/basic/static/searchtools.js:347 #: sphinx/themes/basic/static/searchtools.js:352
msgid "module, in "
msgstr ""
#: sphinx/themes/basic/static/searchtools.js:356
msgid ", in " msgid ", in "
msgstr "" msgstr ""
#: sphinx/themes/basic/static/searchtools.js:464 #: sphinx/themes/basic/static/searchtools.js:475
msgid "" msgid ""
"Your search did not match any documents. Please make sure that all words " "Your search did not match any documents. Please make sure that all words "
"are spelled correctly and that you've selected enough categories." "are spelled correctly and that you've selected enough categories."
msgstr "" msgstr ""
#: sphinx/themes/basic/static/searchtools.js:466 #: sphinx/themes/basic/static/searchtools.js:477
#, python-format #, python-format
msgid "Search finished, found %s page(s) matching the search query." msgid "Search finished, found %s page(s) matching the search query."
msgstr "" msgstr ""
@ -578,15 +644,15 @@ msgstr ""
msgid "Release" msgid "Release"
msgstr "" msgstr ""
#: sphinx/writers/latex.py:578 #: sphinx/writers/latex.py:579
msgid "Footnotes" msgid "Footnotes"
msgstr "" msgstr ""
#: sphinx/writers/latex.py:646 #: sphinx/writers/latex.py:647
msgid "continued from previous page" msgid "continued from previous page"
msgstr "" msgstr ""
#: sphinx/writers/latex.py:651 #: sphinx/writers/latex.py:652
msgid "Continued on next page" msgid "Continued on next page"
msgstr "" msgstr ""

View File

@ -10,6 +10,7 @@
""" """
import re import re
import warnings
from docutils import nodes, utils from docutils import nodes, utils
from docutils.parsers.rst import roles from docutils.parsers.rst import roles
@ -28,7 +29,7 @@ generic_docroles = {
'manpage' : addnodes.literal_emphasis, 'manpage' : addnodes.literal_emphasis,
'mimetype' : addnodes.literal_emphasis, 'mimetype' : addnodes.literal_emphasis,
'newsgroup' : addnodes.literal_emphasis, 'newsgroup' : addnodes.literal_emphasis,
'program' : nodes.strong, 'program' : nodes.strong, # XXX should be an x-ref
'regexp' : nodes.literal, 'regexp' : nodes.literal,
} }
@ -37,27 +38,131 @@ for rolename, nodeclass in generic_docroles.iteritems():
roles.register_local_role(rolename, role) roles.register_local_role(rolename, role)
# -- generic cross-reference role ----------------------------------------------
class XRefRole(object):
"""
A generic cross-referencing role. To create a callable that can be used as
a role function, create an instance of this class.
The general features of this role are:
* Automatic creation of a reference and a content node.
* Optional separation of title and target with `title <target>`.
* The implementation is a class rather than a function to make
customization easier.
Customization can be done in two ways:
* Supplying constructor parameters:
* `fix_parens` to normalize parentheses (strip from target, and add to
title if configured)
* `lowercase` to lowercase the target
* `nodeclass` and `innernodeclass` select the node classes for
the reference and the content node
* Subclassing and overwriting `process_link()` and/or `result_nodes()`.
"""
nodeclass = addnodes.pending_xref
innernodeclass = nodes.literal
def __init__(self, fix_parens=False, lowercase=False,
nodeclass=None, innernodeclass=None):
self.fix_parens = fix_parens
self.lowercase = lowercase
if nodeclass is not None:
self.nodeclass = nodeclass
if innernodeclass is not None:
self.innernodeclass = innernodeclass
def _fix_parens(self, env, has_explicit_title, title, target):
if not has_explicit_title:
if title.endswith('()'):
# remove parentheses
title = title[:-2]
if env.config.add_function_parentheses:
# add them back to all occurrences if configured
title += '()'
# remove parentheses from the target too
if target.endswith('()'):
target = target[:-2]
return title, target
def __call__(self, typ, rawtext, text, lineno, inliner,
options={}, content=[]):
env = inliner.document.settings.env
if not typ:
typ = env.config.default_role
else:
typ = typ.lower()
if ':' not in typ:
domain, role = '', typ
else:
domain, role = typ.split(':', 1)
text = utils.unescape(text)
# if the first character is a bang, don't cross-reference at all
if text[0:1] == '!':
if self.fix_parens:
text, _ = self._fix_parens(env, False, text[1:], "")
innernode = self.innernodeclass(rawtext, text, classes=['xref'])
return self.result_nodes(inliner.document, env, innernode,
is_ref=False)
# split title and target in role content
has_explicit_title, title, target = split_explicit_title(text)
# fix-up title and target
if self.lowercase:
target = target.lower()
if self.fix_parens:
title, target = self._fix_parens(
env, has_explicit_title, title, target)
# create the reference node
refnode = self.nodeclass(rawtext, reftype=role, refdomain=domain,
refexplicit=has_explicit_title)
# we may need the line number for warnings
refnode.line = lineno
title, target = self.process_link(
env, refnode, has_explicit_title, title, target)
# now that the target and title are finally determined, set them
refnode['reftarget'] = target
refnode += self.innernodeclass(rawtext, title, classes=['xref'])
# result_nodes allow further modification of return values
return self.result_nodes(inliner.document, env, refnode, is_ref=True)
# methods that can be overwritten
def process_link(self, env, refnode, has_explicit_title, title, target):
"""
Called after parsing title and target text, and creating the reference
node (given in *refnode*). This method can alter the reference node and
must return a new (or the same) ``(title, target)`` tuple.
"""
return title, ws_re.sub(' ', target)
def result_nodes(self, document, env, node, is_ref):
"""
Called before returning the finished nodes. *node* is the reference
node if one was created (*is_ref* is then true), else the content node.
This method can add other nodes and must return a ``(nodes, messages)``
tuple (the usual return value of a role function).
"""
return [node], []
def indexmarkup_role(typ, rawtext, etext, lineno, inliner, def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
options={}, content=[]): options={}, content=[]):
"""Role for PEP/RFC references that generate an index entry."""
env = inliner.document.settings.env env = inliner.document.settings.env
if not typ: if not typ:
typ = env.config.default_role typ = env.config.default_role
else: else:
typ = typ.lower() typ = typ.lower()
text = utils.unescape(etext) text = utils.unescape(etext)
targetid = 'index-%s' % env.index_num targetid = 'index-%s' % env.new_serialno('index')
env.index_num += 1
indexnode = addnodes.index() indexnode = addnodes.index()
targetnode = nodes.target('', '', ids=[targetid]) targetnode = nodes.target('', '', ids=[targetid])
inliner.document.note_explicit_target(targetnode) inliner.document.note_explicit_target(targetnode)
if typ == 'envvar': if typ == 'pep':
indexnode['entries'] = [('single', text, targetid, text),
('single', _('environment variable; %s') % text,
targetid, text)]
xref_nodes = xfileref_role(typ, rawtext, etext, lineno, inliner,
options, content)[0]
return [indexnode, targetnode] + xref_nodes, []
elif typ == 'pep':
indexnode['entries'] = [ indexnode['entries'] = [
('single', _('Python Enhancement Proposals!PEP %s') % text, ('single', _('Python Enhancement Proposals!PEP %s') % text,
targetid, 'PEP %s' % text)] targetid, 'PEP %s' % text)]
@ -89,117 +194,12 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
rn += sn rn += sn
return [indexnode, targetnode, rn], [] return [indexnode, targetnode, rn], []
roles.register_local_role('envvar', indexmarkup_role)
roles.register_local_role('pep', indexmarkup_role)
roles.register_local_role('rfc', indexmarkup_role)
# default is `literal`
innernodetypes = {
'ref': nodes.emphasis,
'term': nodes.emphasis,
'token': nodes.strong,
'envvar': nodes.strong,
'download': nodes.strong,
'option': addnodes.literal_emphasis,
}
def _fix_parens(typ, text, env):
if typ in ('func', 'meth', 'cfunc'):
if text.endswith('()'):
# remove parentheses
text = text[:-2]
if env.config.add_function_parentheses:
# add them back to all occurrences if configured
text += '()'
return text
def xfileref_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
env = inliner.document.settings.env
if not typ:
typ = env.config.default_role
else:
typ = typ.lower()
text = utils.unescape(text)
# if the first character is a bang, don't cross-reference at all
if text[0:1] == '!':
text = _fix_parens(typ, text[1:], env)
return [innernodetypes.get(typ, nodes.literal)(
rawtext, text, classes=['xref'])], []
# we want a cross-reference, create the reference node
nodeclass = (typ == 'download') and addnodes.download_reference or \
addnodes.pending_xref
pnode = nodeclass(rawtext, reftype=typ, refcaption=False,
modname=env.currmodule, classname=env.currclass)
# we may need the line number for warnings
pnode.line = lineno
# look if explicit title and target are given with `foo <bar>` syntax
has_explicit_title, title, target = split_explicit_title(text)
if has_explicit_title:
pnode['refcaption'] = True
# special target for Python object cross-references
if typ in ('data', 'exc', 'func', 'class', 'const', 'attr',
'meth', 'mod', 'obj'):
# fix-up parentheses in link title
if not has_explicit_title:
title = title.lstrip('.') # only has a meaning for the target
target = target.lstrip('~') # only has a meaning for the title
title = _fix_parens(typ, title, env)
# if the first character is a tilde, don't display the module/class
# parts of the contents
if title[0:1] == '~':
title = title[1:]
dot = title.rfind('.')
if dot != -1:
title = title[dot+1:]
# remove parentheses from the target too
if target.endswith('()'):
target = target[:-2]
# if the first character is a dot, search more specific namespaces first
# else search builtins first
if target[0:1] == '.':
target = target[1:]
pnode['refspecific'] = True
# some other special cases for the target
elif typ == 'option':
program = env.currprogram
if not has_explicit_title:
if ' ' in title and not (title.startswith('/') or
title.startswith('-')):
program, target = re.split(' (?=-|--|/)', title, 1)
program = ws_re.sub('-', program)
target = target.strip()
elif ' ' in target:
program, target = re.split(' (?=-|--|/)', target, 1)
program = ws_re.sub('-', program)
pnode['refprogram'] = program
elif typ == 'term':
# normalize whitespace in definition terms (if the term reference is
# broken over a line, a newline will be in target)
target = ws_re.sub(' ', target).lower()
elif typ == 'ref':
# reST label names are always lowercased
target = ws_re.sub('', target).lower()
elif typ == 'cfunc':
# fix-up parens for C functions too
if not has_explicit_title:
title = _fix_parens(typ, title, env)
# remove parentheses from the target too
if target.endswith('()'):
target = target[:-2]
else:
# remove all whitespace to avoid referencing problems
target = ws_re.sub('', target)
pnode['reftarget'] = target
pnode += innernodetypes.get(typ, nodes.literal)(rawtext, title,
classes=['xref'])
return [pnode], []
def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
return [nodes.emphasis( return [nodes.emphasis(
rawtext, rawtext,
utils.unescape(text).replace('-->', u'\N{TRIANGULAR BULLET}'))], [] utils.unescape(text).replace('-->', u'\N{TRIANGULAR BULLET}'))], []
return role
_litvar_re = re.compile('{([^}]+)}') _litvar_re = re.compile('{([^}]+)}')
@ -233,30 +233,17 @@ def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
specific_docroles = { specific_docroles = {
'data': xfileref_role, # links to download references
'exc': xfileref_role, 'download': XRefRole(nodeclass=addnodes.download_reference),
'func': xfileref_role, # links to headings or arbitrary labels
'class': xfileref_role, 'ref': XRefRole(lowercase=True, innernodeclass=nodes.emphasis),
'const': xfileref_role, # links to documents
'attr': xfileref_role, 'doc': XRefRole(),
'meth': xfileref_role, # links to labels, without a different title
'obj': xfileref_role, 'keyword': XRefRole(),
'cfunc' : xfileref_role,
'cmember': xfileref_role,
'cdata': xfileref_role,
'ctype': xfileref_role,
'cmacro': xfileref_role,
'mod': xfileref_role,
'keyword': xfileref_role,
'ref': xfileref_role,
'token': xfileref_role,
'term': xfileref_role,
'option': xfileref_role,
'doc': xfileref_role,
'download': xfileref_role,
'pep': indexmarkup_role,
'rfc': indexmarkup_role,
'menuselection': menusel_role, 'menuselection': menusel_role,
'file': emph_literal_role, 'file': emph_literal_role,
'samp': emph_literal_role, 'samp': emph_literal_role,
@ -265,3 +252,10 @@ specific_docroles = {
for rolename, func in specific_docroles.iteritems(): for rolename, func in specific_docroles.iteritems():
roles.register_local_role(rolename, func) roles.register_local_role(rolename, func)
# backwards compatibility alias
def xfileref_role(*args, **kwds):
warnings.warn('xfileref_role is deprecated, use XRefRole',
DeprecationWarning, stacklevel=2)
return XRefRole()(*args, **kwds)

View File

@ -119,8 +119,10 @@ class IndexBuilder(object):
self._titles = {} self._titles = {}
# stemmed word -> set(filenames) # stemmed word -> set(filenames)
self._mapping = {} self._mapping = {}
# desctypes -> index # objtype -> index
self._desctypes = {} self._objtypes = {}
# objtype index -> objname (localized)
self._objnames = {}
def load(self, stream, format): def load(self, stream, format):
"""Reconstruct from frozen data.""" """Reconstruct from frozen data."""
@ -138,7 +140,7 @@ class IndexBuilder(object):
self._mapping[k] = set([index2fn[v]]) self._mapping[k] = set([index2fn[v]])
else: else:
self._mapping[k] = set(index2fn[i] for i in v) self._mapping[k] = set(index2fn[i] for i in v)
# no need to load keywords/desctypes # no need to load keywords/objtypes
def dump(self, stream, format): def dump(self, stream, format):
"""Dump the frozen index to a stream.""" """Dump the frozen index to a stream."""
@ -146,27 +148,30 @@ class IndexBuilder(object):
format = self.formats[format] format = self.formats[format]
format.dump(self.freeze(), stream) format.dump(self.freeze(), stream)
def get_modules(self, fn2index): def get_objects(self, fn2index):
rv = {} rv = {}
for name, (doc, _, _, _) in self.env.modules.iteritems(): ot = self._objtypes
if doc in fn2index: on = self._objnames
rv[name] = fn2index[doc] for domainname, domain in self.env.domains.iteritems():
return rv for fullname, type, docname, anchor, prio in domain.get_objects():
if docname not in fn2index:
def get_descrefs(self, fn2index): continue
rv = {} if prio < 0:
dt = self._desctypes continue
for fullname, (doc, desctype) in self.env.descrefs.iteritems(): # XXX splitting at dot is kind of Python specific
if doc not in fn2index: prefix, name = rpartition(fullname, '.')
continue pdict = rv.setdefault(prefix, {})
prefix, name = rpartition(fullname, '.') try:
pdict = rv.setdefault(prefix, {}) i = ot[domainname, type]
try: except KeyError:
i = dt[desctype] i = len(ot)
except KeyError: ot[domainname, type] = i
i = len(dt) otype = domain.object_types.get(type)
dt[desctype] = i if otype:
pdict[name] = (fn2index[doc], i) on[i] = str(otype.lname) # fire translation proxies
else:
on[i] = type
pdict[name] = (fn2index[docname], i, prio)
return rv return rv
def get_terms(self, fn2index): def get_terms(self, fn2index):
@ -185,14 +190,13 @@ class IndexBuilder(object):
filenames = self._titles.keys() filenames = self._titles.keys()
titles = self._titles.values() titles = self._titles.values()
fn2index = dict((f, i) for (i, f) in enumerate(filenames)) fn2index = dict((f, i) for (i, f) in enumerate(filenames))
return dict( terms = self.get_terms(fn2index)
filenames=filenames, objects = self.get_objects(fn2index) # populates _objtypes
titles=titles, objtypes = dict((v, k[0] + ':' + k[1])
terms=self.get_terms(fn2index), for (k, v) in self._objtypes.iteritems())
descrefs=self.get_descrefs(fn2index), objnames = self._objnames
modules=self.get_modules(fn2index), return dict(filenames=filenames, titles=titles, terms=terms,
desctypes=dict((v, k) for (k, v) in self._desctypes.items()), objects=objects, objtypes=objtypes, objnames=objnames)
)
def prune(self, filenames): def prune(self, filenames):
"""Remove data for all filenames not in the list.""" """Remove data for all filenames not in the list."""

View File

@ -1,12 +1,11 @@
/// XXX: make it cross browser
/** /**
* make the code below compatible with browsers without * make the code below compatible with browsers without
* an installed firebug like debugger * an installed firebug like debugger
*/ */
if (!window.console || !console.firebug) { if (!window.console || !console.firebug) {
var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", var names = ["log", "debug", "info", "warn", "error", "assert", "dir",
"group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace",
"profile", "profileEnd"];
window.console = {}; window.console = {};
for (var i = 0; i < names.length; ++i) for (var i = 0; i < names.length; ++i)
window.console[names[i]] = function() {} window.console[names[i]] = function() {}

View File

@ -330,31 +330,33 @@ var Search = {
var filenames = this._index.filenames; var filenames = this._index.filenames;
var titles = this._index.titles; var titles = this._index.titles;
var terms = this._index.terms; var terms = this._index.terms;
var descrefs = this._index.descrefs; var objects = this._index.objects;
var modules = this._index.modules; var objtypes = this._index.objtypes;
var desctypes = this._index.desctypes; var objnames = this._index.objnames;
var fileMap = {}; var fileMap = {};
var files = null; var files = null;
// different result priorities
var importantResults = [];
var objectResults = []; var objectResults = [];
var regularResults = []; var regularResults = [];
var unimportantResults = [];
$('#search-progress').empty(); $('#search-progress').empty();
// lookup as object // lookup as object
if (object != null) { if (object != null) {
for (var module in modules) { for (var prefix in objects) {
if (module.indexOf(object) > -1) { for (var name in objects[prefix]) {
fn = modules[module];
descr = _('module, in ') + titles[fn];
objectResults.push([filenames[fn], module, '#module-'+module, descr]);
}
}
for (var prefix in descrefs) {
for (var name in descrefs[prefix]) {
var fullname = (prefix ? prefix + '.' : '') + name; var fullname = (prefix ? prefix + '.' : '') + name;
if (fullname.toLowerCase().indexOf(object) > -1) { if (fullname.toLowerCase().indexOf(object) > -1) {
match = descrefs[prefix][name]; match = objects[prefix][name];
descr = desctypes[match[1]] + _(', in ') + titles[match[0]]; descr = objnames[match[1]] + _(', in ') + titles[match[0]];
objectResults.push([filenames[match[0]], fullname, '#'+fullname, descr]); // XXX the generated anchors are not generally correct
result = [filenames[match[0]], fullname, '#'+fullname, descr];
switch (match[2]) {
case 1: objectResults.push(result); break;
case 0: importantResults.push(result); break;
case 2: unimportantResults.push(result); break;
}
} }
} }
} }
@ -365,6 +367,14 @@ var Search = {
return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0); return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
}); });
importantResults.sort(function(a, b) {
return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
});
unimportantResults.sort(function(a, b) {
return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
});
// perform the search on the required terms // perform the search on the required terms
for (var i = 0; i < searchterms.length; i++) { for (var i = 0; i < searchterms.length; i++) {
@ -420,8 +430,9 @@ var Search = {
return (left > right) ? -1 : ((left < right) ? 1 : 0); return (left > right) ? -1 : ((left < right) ? 1 : 0);
}); });
// combine both // combine all results
var results = regularResults.concat(objectResults); var results = unimportantResults.concat(regularResults)
.concat(objectResults).concat(importantResults);
// print the results // print the results
var resultCount = results.length; var resultCount = results.length;

View File

@ -24,6 +24,8 @@ import traceback
from os import path from os import path
import docutils import docutils
from docutils import nodes
import sphinx import sphinx
# Errnos that we need. # Errnos that we need.
@ -451,6 +453,21 @@ def split_explicit_title(text):
return True, match.group(1), match.group(2) return True, match.group(1), match.group(2)
return False, text, text return False, text, text
def make_refnode(builder, fromdocname, todocname, targetid, child, title=None):
"""Shortcut to create a reference node."""
node = nodes.reference('', '')
if fromdocname == todocname:
node['refid'] = targetid
else:
node['refuri'] = (builder.get_relative_uri(fromdocname, todocname)
+ '#' + targetid)
if title:
node['reftitle'] = title
node.append(child)
return node
# monkey-patch Node.traverse to get more speed # monkey-patch Node.traverse to get more speed
# traverse() is called so many times during a build that it saves # traverse() is called so many times during a build that it saves
# on average 20-25% overall build time! # on average 20-25% overall build time!
@ -480,8 +497,7 @@ def _new_traverse(self, condition=None,
return self._old_traverse(condition, include_self, return self._old_traverse(condition, include_self,
descend, siblings, ascend) descend, siblings, ascend)
import docutils.nodes nodes.Node._old_traverse = nodes.Node.traverse
docutils.nodes.Node._old_traverse = docutils.nodes.Node.traverse nodes.Node._all_traverse = _all_traverse
docutils.nodes.Node._all_traverse = _all_traverse nodes.Node._fast_traverse = _fast_traverse
docutils.nodes.Node._fast_traverse = _fast_traverse nodes.Node.traverse = _new_traverse
docutils.nodes.Node.traverse = _new_traverse

View File

@ -11,7 +11,7 @@
from docutils import nodes from docutils import nodes
# function missing in 0.5 SVN # function missing in docutils 0.5
def make_admonition(node_class, name, arguments, options, content, lineno, def make_admonition(node_class, name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine): content_offset, block_text, state, state_machine):
#if not content: #if not content:
@ -35,64 +35,13 @@ def make_admonition(node_class, name, arguments, options, content, lineno,
return [admonition_node] return [admonition_node]
# support the class-style Directive interface even when using docutils 0.4 # backwards-compatibility aliases for helpers in older Sphinx versions that
# supported the docutils 0.4 directive function interface
try: from docutils.parsers.rst import Directive
from docutils.parsers.rst import Directive
except ImportError: def directive_dwim(obj):
class Directive(object): import warnings
""" warnings.warn('directive_dwim is deprecated and no longer needed',
Fake Directive class to allow Sphinx directives to be written in DeprecationWarning, stacklevel=2)
class style. return obj
"""
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = False
option_spec = None
has_content = False
def __init__(self, name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
self.name = name
self.arguments = arguments
self.options = options
self.content = content
self.lineno = lineno
self.content_offset = content_offset
self.block_text = block_text
self.state = state
self.state_machine = state_machine
def run(self):
raise NotImplementedError('Must override run() is subclass.')
def directive_dwim(obj):
"""
Return something usable with register_directive(), regardless if
class or function. For that, we need to convert classes to a
function for docutils 0.4.
"""
if isinstance(obj, type) and issubclass(obj, Directive):
def _class_directive(name, arguments, options, content,
lineno, content_offset, block_text,
state, state_machine):
return obj(name, arguments, options, content,
lineno, content_offset, block_text,
state, state_machine).run()
_class_directive.options = obj.option_spec
_class_directive.content = obj.has_content
_class_directive.arguments = (obj.required_arguments,
obj.optional_arguments,
obj.final_argument_whitespace)
return _class_directive
return obj
else:
def directive_dwim(obj):
"""
Return something usable with register_directive(), regardless if
class or function. Nothing to do here, because docutils 0.5 takes
care of converting functions itself.
"""
return obj

View File

@ -61,7 +61,7 @@ class HTMLTranslator(BaseTranslator):
self.add_permalinks = builder.config.html_add_permalinks self.add_permalinks = builder.config.html_add_permalinks
def visit_desc(self, node): def visit_desc(self, node):
self.body.append(self.starttag(node, 'dl', CLASS=node['desctype'])) self.body.append(self.starttag(node, 'dl', CLASS=node['objtype']))
def depart_desc(self, node): def depart_desc(self, node):
self.body.append('</dl>\n\n') self.body.append('</dl>\n\n')
@ -69,7 +69,7 @@ class HTMLTranslator(BaseTranslator):
# the id is set automatically # the id is set automatically
self.body.append(self.starttag(node, 'dt')) self.body.append(self.starttag(node, 'dt'))
# anchor for per-desc interactive data # anchor for per-desc interactive data
if node.parent['desctype'] != 'describe' \ if node.parent['objtype'] != 'describe' \
and node['ids'] and node['first']: and node['ids'] and node['first']:
self.body.append('<!--[%s]-->' % node['ids'][0]) self.body.append('<!--[%s]-->' % node['ids'][0])
def depart_desc_signature(self, node): def depart_desc_signature(self, node):
@ -189,21 +189,10 @@ class HTMLTranslator(BaseTranslator):
numbers = self.builder.secnumbers[anchorname] numbers = self.builder.secnumbers[anchorname]
self.body.append('.'.join(map(str, numbers)) + '. ') self.body.append('.'.join(map(str, numbers)) + '. ')
# overwritten for docutils 0.4 # overwritten
if hasattr(BaseTranslator, 'start_tag_with_title'): def visit_title(self, node):
def visit_section(self, node): BaseTranslator.visit_title(self, node)
# the 0.5 version, to get the id attribute in the <div> tag self.add_secnumber(node)
self.section_level += 1
self.body.append(self.starttag(node, 'div', CLASS='section'))
def visit_title(self, node):
# don't move the id attribute inside the <h> tag
BaseTranslator.visit_title(self, node, move_ids=0)
self.add_secnumber(node)
else:
def visit_title(self, node):
BaseTranslator.visit_title(self, node)
self.add_secnumber(node)
# overwritten # overwritten
def visit_literal_block(self, node): def visit_literal_block(self, node):

View File

@ -123,7 +123,7 @@ class Table(object):
class Desc(object): class Desc(object):
def __init__(self, node): def __init__(self, node):
self.env = LaTeXTranslator.desc_map.get(node['desctype'], 'describe') self.env = LaTeXTranslator.desc_map.get(node['objtype'], 'describe')
self.type = self.cls = self.name = self.params = \ self.type = self.cls = self.name = self.params = \
self.annotation = self.returns = '' self.annotation = self.returns = ''
self.count = 0 self.count = 0
@ -428,6 +428,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def depart_subtitle(self, node): def depart_subtitle(self, node):
self.body.append(self.context.pop()) self.body.append(self.context.pop())
# XXX update this
desc_map = { desc_map = {
'function' : 'funcdesc', 'function' : 'funcdesc',
'class': 'classdesc', 'class': 'classdesc',
@ -462,7 +463,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def depart_desc_signature(self, node): def depart_desc_signature(self, node):
d = self.descstack[-1] d = self.descstack[-1]
d.cls = d.cls.rstrip('.') d.cls = d.cls.rstrip('.')
if node.parent['desctype'] != 'describe' and node['ids']: if node.parent['objtype'] != 'describe' and node['ids']:
hyper = '\\hypertarget{%s}{}' % node['ids'][0] hyper = '\\hypertarget{%s}{}' % node['ids'][0]
else: else:
hyper = '' hyper = ''

View File

@ -174,8 +174,8 @@ class TextTranslator(nodes.NodeVisitor):
def visit_desc_signature(self, node): def visit_desc_signature(self, node):
self.new_state(0) self.new_state(0)
if node.parent['desctype'] in ('class', 'exception'): if node.parent['objtype'] in ('class', 'exception'):
self.add_text('%s ' % node.parent['desctype']) self.add_text('%s ' % node.parent['objtype'])
def depart_desc_signature(self, node): def depart_desc_signature(self, node):
# XXX: wrap signatures in a way that makes sense # XXX: wrap signatures in a way that makes sense
self.end_state(wrap=False, end=None) self.end_state(wrap=False, end=None)

View File

@ -1,4 +1,5 @@
{% extends "!layout.html" %} {% extends "!layout.html" %}
{% block extrahead %} {% block extrahead %}
{# html_context variable from conf.py #} {# html_context variable from conf.py #}
<meta name="hc" content="{{ hckey }}" /> <meta name="hc" content="{{ hckey }}" />
@ -6,3 +7,9 @@
<meta name="hc_co" content="{{ hckey_co }}" /> <meta name="hc_co" content="{{ hckey_co }}" />
{{ super() }} {{ super() }}
{% endblock %} {% endblock %}
{% block sidebartoc %}
{# display global TOC in addition to local TOC #}
{{ super() }}
{{ toctree(collapse=False, maxdepth=-1) }}
{% endblock %}

View File

@ -25,6 +25,7 @@ today_fmt = '%B %d, %Y'
exclude_trees = ['_build'] exclude_trees = ['_build']
keep_warnings = True keep_warnings = True
pygments_style = 'sphinx' pygments_style = 'sphinx'
show_authors = True
rst_epilog = '.. |subst| replace:: global substitution' rst_epilog = '.. |subst| replace:: global substitution'
@ -49,6 +50,7 @@ latex_additional_files = ['svgimg.svg']
value_from_conf_py = 84 value_from_conf_py = 84
coverage_c_path = ['special/*.h'] coverage_c_path = ['special/*.h']
# XXX cfunction?
coverage_c_regexes = {'cfunction': r'^PyAPI_FUNC\(.*\)\s+([^_][\w_]+)'} coverage_c_regexes = {'cfunction': r'^PyAPI_FUNC\(.*\)\s+([^_][\w_]+)'}
autosummary_generate = ['autosummary'] autosummary_generate = ['autosummary']
@ -59,7 +61,12 @@ extlinks = {'issue': ('http://bugs.python.org/issue%s', 'issue '),
# modify tags from conf.py # modify tags from conf.py
tags.add('confpytag') tags.add('confpytag')
# -- extension API
from docutils import nodes
from sphinx import addnodes from sphinx import addnodes
from sphinx.util.compat import Directive
def userdesc_parse(env, sig, signode): def userdesc_parse(env, sig, signode):
x, y = sig.split(':') x, y = sig.split(':')
@ -68,7 +75,18 @@ def userdesc_parse(env, sig, signode):
signode[-1] += addnodes.desc_parameter(y, y) signode[-1] += addnodes.desc_parameter(y, y)
return x return x
def functional_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
return [nodes.strong(text='from function: %s' % options['opt'])]
class ClassDirective(Directive):
option_spec = {'opt': lambda x: x}
def run(self):
return [nodes.strong(text='from class: %s' % self.options['opt'])]
def setup(app): def setup(app):
app.add_config_value('value_from_conf_py', 42, False) app.add_config_value('value_from_conf_py', 42, False)
app.add_description_unit('userdesc', 'userdescrole', '%s (userdesc)', app.add_directive('funcdir', functional_directive, opt=lambda x: x)
userdesc_parse) app.add_directive('clsdir', ClassDirective)
app.add_object_type('userdesc', 'userdescrole', '%s (userdesc)',
userdesc_parse, objname='user desc')

View File

@ -11,12 +11,13 @@ Contents:
:maxdepth: 2 :maxdepth: 2
:numbered: :numbered:
extapi
images images
subdir/images subdir/images
subdir/includes subdir/includes
includes includes
markup markup
desc objects
bom bom
math math
autodoc autodoc

10
tests/root/extapi.txt Normal file
View File

@ -0,0 +1,10 @@
Extension API tests
===================
Testing directives:
.. funcdir::
:opt: Foo
.. clsdir::
:opt: Bar

View File

@ -5,7 +5,11 @@
Testing various markup Testing various markup
====================== ======================
Meta markup
-----------
.. sectionauthor:: Georg Brandl .. sectionauthor:: Georg Brandl
.. moduleauthor:: Georg Brandl
.. contents:: TOC .. contents:: TOC
@ -13,7 +17,11 @@ Testing various markup
:author: Me :author: Me
:keywords: docs, sphinx :keywords: docs, sphinx
A |subst|.
Generic reST
------------
A |subst| (the definition is in rst_epilog).
.. _label: .. _label:
@ -21,22 +29,14 @@ A |subst|.
some code some code
Admonitions Option list:
-----------
.. note:: Note -h help
Note text. --help also help
.. warning:: Warning
Warning text.
.. tip::
Tip text.
Body directives Body directives
--------------- ^^^^^^^^^^^^^^^
.. topic:: Title .. topic:: Title
@ -69,7 +69,67 @@ Body directives
b b
Admonitions
^^^^^^^^^^^
.. admonition:: My Admonition
Admonition text.
.. note::
Note text.
.. warning::
Warning text.
.. tip::
Tip text.
Inline markup
-------------
*Generic inline markup*
* :command:`command`
* :dfn:`dfn`
* :guilabel:`guilabel`
* :kbd:`kbd`
* :mailheader:`mailheader`
* :makevar:`makevar`
* :manpage:`manpage`
* :mimetype:`mimetype`
* :newsgroup:`newsgroup`
* :program:`program`
* :regexp:`regexp`
* :menuselection:`File --> Close`
* :file:`a/{varpart}/b`
* :samp:`print {i}`
*Linking inline markup*
* :pep:`8`
* :rfc:`1`
* :envvar:`HOME`
* :keyword:`with`
* :token:`try statement <try_stmt>`
* :doc:`subdir/includes`
* ``:download:`` is tested in includes.txt
* :option:`Python -c option <python -c>`
Test :abbr:`abbr (abbreviation)` and another :abbr:`abbr (abbreviation)`.
.. _with:
With
----
(Empty section.)
Tables Tables
------ ------
@ -96,6 +156,17 @@ Version markup
Boring stuff. Boring stuff.
Code blocks
-----------
.. code-block:: ruby
:linenos:
def ruby?
false
end
Misc stuff Misc stuff
---------- ----------
@ -124,11 +195,6 @@ This is a side note.
This tests :CLASS:`role names in uppercase`. This tests :CLASS:`role names in uppercase`.
Option list:
-h help
--help also help
.. centered:: LICENSE AGREEMENT .. centered:: LICENSE AGREEMENT
.. acks:: .. acks::
@ -146,7 +212,7 @@ Option list:
Particle with half-integer spin. Particle with half-integer spin.
.. productionlist:: .. productionlist::
try_stmt: try1_stmt | try2_stmt try_stmt: `try1_stmt` | `try2_stmt`
try1_stmt: "try" ":" `suite` try1_stmt: "try" ":" `suite`
: ("except" [`expression` ["," `target`]] ":" `suite`)+ : ("except" [`expression` ["," `target`]] ":" `suite`)+
: ["else" ":" `suite`] : ["else" ":" `suite`]
@ -154,7 +220,6 @@ Option list:
try2_stmt: "try" ":" `suite` try2_stmt: "try" ":" `suite`
: "finally" ":" `suite` : "finally" ":" `suite`
Test :abbr:`abbr (abbreviation)` and another :abbr:`abbr (abbreviation)`.
Index markup Index markup
------------ ------------
@ -180,11 +245,6 @@ Invalid index markup...
Testing öäü... Testing öäü...
Object markup
-------------
:cfunc:`CFunction`.
Only directive Only directive
-------------- --------------
@ -208,3 +268,4 @@ Only directive
.. rubric:: Footnotes .. rubric:: Footnotes
.. [#] Like footnotes. .. [#] Like footnotes.

View File

@ -1,5 +1,5 @@
Testing description units Testing object descriptions
========================= ===========================
.. function:: func_without_module(a, b, *c[, d]) .. function:: func_without_module(a, b, *c[, d])
@ -43,22 +43,42 @@ Testing description units
C items C items
======= =======
.. cfunction:: Sphinx_DoSomething() .. c:function:: Sphinx_DoSomething()
.. cmember:: SphinxStruct.member .. c:member:: SphinxStruct.member
.. cmacro:: SPHINX_USE_PYTHON .. c:macro:: SPHINX_USE_PYTHON
.. ctype:: SphinxType .. c:type:: SphinxType
.. cvar:: sphinx_global .. c:var:: sphinx_global
Testing references References
================== ==========
Referencing :class:`mod.Cls` or :Class:`mod.Cls` should be the same. Referencing :class:`mod.Cls` or :Class:`mod.Cls` should be the same.
With target: :c:func:`Sphinx_DoSomething()` (parentheses are handled),
:c:member:`SphinxStruct.member`, :c:macro:`SPHINX_USE_PYTHON`,
:c:type:`SphinxType *` (pointer is handled), :c:data:`sphinx_global`.
Without target: :c:func:`CFunction`. :c:func:`!malloc`.
Others
======
.. envvar:: HOME
.. program:: python
.. cmdoption:: -c command
.. program:: perl
.. cmdoption:: -c
User markup User markup
=========== ===========

View File

@ -97,28 +97,28 @@ def test_parse_name():
verify('function', 'util.raises', ('util', ['raises'], None, None)) verify('function', 'util.raises', ('util', ['raises'], None, None))
verify('function', 'util.raises(exc) -> None', verify('function', 'util.raises(exc) -> None',
('util', ['raises'], 'exc', 'None')) ('util', ['raises'], 'exc', 'None'))
directive.env.autodoc_current_module = 'util' directive.env.doc_read_data['autodoc_module'] = 'util'
verify('function', 'raises', ('util', ['raises'], None, None)) verify('function', 'raises', ('util', ['raises'], None, None))
directive.env.autodoc_current_module = None del directive.env.doc_read_data['autodoc_module']
directive.env.currmodule = 'util' directive.env.doc_read_data['py_module'] = 'util'
verify('function', 'raises', ('util', ['raises'], None, None)) verify('function', 'raises', ('util', ['raises'], None, None))
verify('class', 'TestApp', ('util', ['TestApp'], None, None)) verify('class', 'TestApp', ('util', ['TestApp'], None, None))
# for members # for members
directive.env.currmodule = 'foo' directive.env.doc_read_data['py_module'] = 'foo'
verify('method', 'util.TestApp.cleanup', verify('method', 'util.TestApp.cleanup',
('util', ['TestApp', 'cleanup'], None, None)) ('util', ['TestApp', 'cleanup'], None, None))
directive.env.currmodule = 'util' directive.env.doc_read_data['py_module'] = 'util'
directive.env.currclass = 'Foo' directive.env.doc_read_data['py_class'] = 'Foo'
directive.env.autodoc_current_class = 'TestApp' directive.env.doc_read_data['autodoc_class'] = 'TestApp'
verify('method', 'cleanup', ('util', ['TestApp', 'cleanup'], None, None)) verify('method', 'cleanup', ('util', ['TestApp', 'cleanup'], None, None))
verify('method', 'TestApp.cleanup', verify('method', 'TestApp.cleanup',
('util', ['TestApp', 'cleanup'], None, None)) ('util', ['TestApp', 'cleanup'], None, None))
# and clean up # and clean up
directive.env.currmodule = None del directive.env.doc_read_data['py_module']
directive.env.currclass = None del directive.env.doc_read_data['py_class']
directive.env.autodoc_current_class = None del directive.env.doc_read_data['autodoc_class']
def test_format_signature(): def test_format_signature():
@ -306,7 +306,7 @@ def test_new_documenter():
del directive.result[:] del directive.result[:]
options.members = ['integer'] options.members = ['integer']
assert_result_contains('.. data:: integer', 'module', 'test_autodoc') assert_result_contains('.. py:data:: integer', 'module', 'test_autodoc')
def test_generate(): def test_generate():
@ -353,7 +353,7 @@ def test_generate():
'function', 'util.foobar', more_content=None) 'function', 'util.foobar', more_content=None)
# test auto and given content mixing # test auto and given content mixing
directive.env.currmodule = 'test_autodoc' directive.env.doc_read_data['py_module'] = 'test_autodoc'
assert_result_contains(' Function.', 'method', 'Class.meth') assert_result_contains(' Function.', 'method', 'Class.meth')
add_content = ViewList() add_content = ViewList()
add_content.append('Content.', '', 0) add_content.append('Content.', '', 0)
@ -394,7 +394,8 @@ def test_generate():
options.members = [] options.members = []
# test module flags # test module flags
assert_result_contains('.. module:: test_autodoc', 'module', 'test_autodoc') assert_result_contains('.. py:module:: test_autodoc',
'module', 'test_autodoc')
options.synopsis = 'Synopsis' options.synopsis = 'Synopsis'
assert_result_contains(' :synopsis: Synopsis', 'module', 'test_autodoc') assert_result_contains(' :synopsis: Synopsis', 'module', 'test_autodoc')
options.deprecated = True options.deprecated = True
@ -403,9 +404,9 @@ def test_generate():
assert_result_contains(' :platform: Platform', 'module', 'test_autodoc') assert_result_contains(' :platform: Platform', 'module', 'test_autodoc')
# test if __all__ is respected for modules # test if __all__ is respected for modules
options.members = ALL options.members = ALL
assert_result_contains('.. class:: Class', 'module', 'test_autodoc') assert_result_contains('.. py:class:: Class', 'module', 'test_autodoc')
try: try:
assert_result_contains('.. exception:: CustomEx', assert_result_contains('.. py:exception:: CustomEx',
'module', 'test_autodoc') 'module', 'test_autodoc')
except AssertionError: except AssertionError:
pass pass
@ -419,7 +420,7 @@ def test_generate():
assert_result_contains(' :noindex:', 'class', 'Base') assert_result_contains(' :noindex:', 'class', 'Base')
# okay, now let's get serious about mixing Python and C signature stuff # okay, now let's get serious about mixing Python and C signature stuff
assert_result_contains('.. class:: CustomDict', 'class', 'CustomDict', assert_result_contains('.. py:class:: CustomDict', 'class', 'CustomDict',
all_members=True) all_members=True)
# test inner class handling # test inner class handling
@ -433,7 +434,7 @@ def test_generate():
'attribute', 'test_autodoc.Class.descr') 'attribute', 'test_autodoc.Class.descr')
# test generation for C modules (which have no source file) # test generation for C modules (which have no source file)
directive.env.currmodule = 'time' directive.env.doc_read_data['py_module'] = 'time'
assert_processes([('function', 'time.asctime')], 'function', 'asctime') assert_processes([('function', 'time.asctime')], 'function', 'asctime')
assert_processes([('function', 'time.asctime')], 'function', 'asctime') assert_processes([('function', 'time.asctime')], 'function', 'asctime')

View File

@ -13,28 +13,19 @@ import os
import re import re
import sys import sys
import difflib import difflib
import htmlentitydefs
from StringIO import StringIO from StringIO import StringIO
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from util import *
from etree13 import ElementTree as ET
try:
import pygments
except ImportError:
pygments = None
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.builders.latex import LaTeXBuilder from sphinx.builders.latex import LaTeXBuilder
from sphinx.writers.latex import LaTeXTranslator from sphinx.writers.latex import LaTeXTranslator
from util import *
def teardown_module(): def teardown_module():
(test_root / '_build').rmtree(True) (test_root / '_build').rmtree(True)
html_warnfile = StringIO()
latex_warnfile = StringIO() latex_warnfile = StringIO()
ENV_WARNINGS = """\ ENV_WARNINGS = """\
@ -46,164 +37,11 @@ included file u'wrongenc.inc' seems to be wrong, try giving an :encoding: option
%(root)s/includes.txt:4: WARNING: download file not readable: nonexisting.png %(root)s/includes.txt:4: WARNING: download file not readable: nonexisting.png
""" """
HTML_WARNINGS = ENV_WARNINGS + """\
%(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.*'
%(root)s/markup.txt:: WARNING: invalid index entry u''
%(root)s/markup.txt:: WARNING: invalid pair index entry u''
%(root)s/markup.txt:: WARNING: invalid pair index entry u'keyword; '
"""
LATEX_WARNINGS = ENV_WARNINGS + """\ LATEX_WARNINGS = ENV_WARNINGS + """\
None:None: WARNING: no matching candidate for image URI u'foo.*' None:None: WARNING: no matching candidate for image URI u'foo.*'
WARNING: invalid pair index entry u'' WARNING: invalid pair index entry u''
""" """
HTML_XPATH = {
'images.html': {
".//img[@src='_images/img.png']": '',
".//img[@src='_images/img1.png']": '',
".//img[@src='_images/simg.png']": '',
".//object[@data='_images/svgimg.svg']": '',
".//embed[@src='_images/svgimg.svg']": '',
},
'subdir/images.html': {
".//img[@src='../_images/img1.png']": '',
".//img[@src='../_images/rimg.png']": '',
},
'subdir/includes.html': {
".//a[@href='../_downloads/img.png']": '',
},
'includes.html': {
".//pre": u'Max Strauß',
".//a[@href='_downloads/img.png']": '',
".//a[@href='_downloads/img1.png']": '',
".//pre": u'"quotes"',
".//pre": u"'included'",
},
'autodoc.html': {
".//dt[@id='test_autodoc.Class']": '',
".//dt[@id='test_autodoc.function']/em": r'\*\*kwds',
".//dd": r'Return spam\.',
},
'markup.html': {
".//meta[@name='author'][@content='Me']": '',
".//meta[@name='keywords'][@content='docs, sphinx']": '',
".//a[@href='contents.html#ref1']": '',
".//div[@id='label']": '',
".//span[@class='option']": '--help',
".//p": 'A global substitution.',
".//p": 'In HTML.',
".//p": 'In both.',
".//p": 'Always present',
".//title": 'set by title directive',
".//span[@class='pre']": 'CFunction()',
},
'desc.html': {
".//dt[@id='mod.Cls.meth1']": '',
".//dt[@id='errmod.Error']": '',
".//a[@href='#mod.Cls']": '',
".//dl[@class='userdesc']": '',
".//dt[@id='userdescrole-myobj']": '',
".//a[@href='#userdescrole-myobj']": '',
},
'contents.html': {
".//meta[@name='hc'][@content='hcval']": '',
".//meta[@name='hc_co'][@content='hcval_co']": '',
".//meta[@name='testopt'][@content='testoverride']": '',
#".//td[@class='label']": r'\[Ref1\]', # docutils 0.5 only
".//td[@class='label']": '',
".//li[@class='toctree-l1']/a": 'Testing various markup',
".//li[@class='toctree-l2']/a": 'Admonitions',
".//title": 'Sphinx <Tests>',
".//div[@class='footer']": 'Georg Brandl & Team',
".//a[@href='http://python.org/']": '',
},
'bom.html': {
".//title": " File with UTF-8 BOM",
},
'extensions.html': {
".//a[@href='http://python.org/dev/']": "http://python.org/dev/",
".//a[@href='http://bugs.python.org/issue1000']": "issue 1000",
".//a[@href='http://bugs.python.org/issue1042']": "explicit caption",
},
'_static/statictmpl.html': {
".//project": 'Sphinx <Tests>',
},
}
if pygments:
HTML_XPATH['includes.html'].update({
".//pre/span[@class='s']": u'üöä',
".//div[@class='inc-pyobj1 highlight-text']/div/pre":
r'^class Foo:\n pass\n\s*$',
".//div[@class='inc-pyobj2 highlight-text']/div/pre":
r'^ def baz\(\):\n pass\n\s*$',
".//div[@class='inc-lines highlight-text']/div/pre":
r'^class Foo:\n pass\nclass Bar:\n$',
".//div[@class='inc-startend highlight-text']/div/pre":
ur'^foo = u"Including Unicode characters: üöä"\n$',
".//div[@class='inc-preappend highlight-text']/div/pre":
r'(?m)^START CODE$',
".//div[@class='inc-pyobj-dedent highlight-python']/div/pre/span":
r'def',
})
HTML_XPATH['subdir/includes.html'].update({
".//pre/span": 'line 1',
".//pre/span": 'line 2',
})
class NslessParser(ET.XMLParser):
"""XMLParser that throws away namespaces in tag names."""
def _fixname(self, key):
try:
return self._names[key]
except KeyError:
name = key
br = name.find('}')
if br > 0:
name = name[br+1:]
self._names[key] = name = self._fixtext(name)
return name
def check_xpath(etree, fname, path, check):
nodes = list(etree.findall(path))
assert nodes != [], ('did not find any node matching xpath '
'%r in file %s' % (path, fname))
if hasattr(check, '__call__'):
check(nodes)
elif not check:
# only check for node presence
pass
else:
rex = re.compile(check)
for node in nodes:
if node.text and rex.search(node.text):
break
else:
assert False, ('%r not found in any node matching '
'path %s in %s: %r' % (check, path, fname,
[node.text for node in nodes]))
@gen_with_app(buildername='html', warning=html_warnfile, cleanenv=True,
confoverrides={'html_context.hckey_co': 'hcval_co'},
tags=['testtag'])
def test_html(app):
app.builder.build_all()
html_warnings = html_warnfile.getvalue().replace(os.sep, '/')
html_warnings_exp = HTML_WARNINGS % {'root': app.srcdir}
assert html_warnings == html_warnings_exp, 'Warnings don\'t match:\n' + \
'\n'.join(difflib.ndiff(html_warnings_exp.splitlines(),
html_warnings.splitlines()))
for fname, paths in HTML_XPATH.iteritems():
parser = NslessParser()
parser.entity.update(htmlentitydefs.entitydefs)
etree = ET.parse(os.path.join(app.outdir, fname), parser)
for path, check in paths.iteritems():
yield check_xpath, etree, fname, path, check
@with_app(buildername='latex', warning=latex_warnfile, cleanenv=True) @with_app(buildername='latex', warning=latex_warnfile, cleanenv=True)
def test_latex(app): def test_latex(app):

252
tests/test_build_html.py Normal file
View File

@ -0,0 +1,252 @@
# -*- coding: utf-8 -*-
"""
test_build_html
~~~~~~~~~~~~~~~
Test the HTML builder and check output against XPath.
:copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os
import re
import sys
import difflib
import htmlentitydefs
from StringIO import StringIO
try:
import pygments
except ImportError:
pygments = None
from sphinx.builders.html import StandaloneHTMLBuilder
from util import *
from test_build import ENV_WARNINGS
from etree13 import ElementTree as ET
def teardown_module():
(test_root / '_build').rmtree(True)
html_warnfile = StringIO()
HTML_WARNINGS = ENV_WARNINGS + """\
%(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.*'
%(root)s/markup.txt:: WARNING: invalid index entry u''
%(root)s/markup.txt:: WARNING: invalid pair index entry u''
%(root)s/markup.txt:: WARNING: invalid pair index entry u'keyword; '
"""
HTML_XPATH = {
'images.html': {
".//img[@src='_images/img.png']": '',
".//img[@src='_images/img1.png']": '',
".//img[@src='_images/simg.png']": '',
".//object[@data='_images/svgimg.svg']": '',
".//embed[@src='_images/svgimg.svg']": '',
},
'subdir/images.html': {
".//img[@src='../_images/img1.png']": '',
".//img[@src='../_images/rimg.png']": '',
},
'subdir/includes.html': {
".//a[@href='../_downloads/img.png']": '',
},
'includes.html': {
".//pre": u'Max Strauß',
".//a[@href='_downloads/img.png']": '',
".//a[@href='_downloads/img1.png']": '',
".//pre": u'"quotes"',
".//pre": u"'included'",
},
'autodoc.html': {
".//dt[@id='test_autodoc.Class']": '',
".//dt[@id='test_autodoc.function']/em": r'\*\*kwds',
".//dd": r'Return spam\.',
},
'extapi.html': {
".//strong": 'from function: Foo',
".//strong": 'from class: Bar',
},
'markup.html': {
".//title": 'set by title directive',
".//p/em": 'Section author: Georg Brandl',
".//p/em": 'Module author: Georg Brandl',
# created by the meta directive
".//meta[@name='author'][@content='Me']": '',
".//meta[@name='keywords'][@content='docs, sphinx']": '',
# a label created by ``.. _label:``
".//div[@id='label']": '',
# code with standard code blocks
".//pre": '^some code$',
# an option list
".//span[@class='option']": '--help',
# admonitions
".//p[@class='first admonition-title']": 'My Admonition',
".//p[@class='last']": 'Note text.',
".//p[@class='last']": 'Warning text.',
# inline markup
".//li/strong": '^command$',
".//li/strong": '^program$',
".//li/em": '^dfn$',
".//li/tt/span[@class='pre']": '^kbd$',
".//li/em": u'File \N{TRIANGULAR BULLET} Close',
".//li/tt/span[@class='pre']": '^a/$',
".//li/tt/em/span[@class='pre']": '^varpart$',
".//li/tt/em/span[@class='pre']": '^i$',
".//a[@href='http://www.python.org/dev/peps/pep-0008']/strong": 'PEP 8',
".//a[@href='http://tools.ietf.org/html/rfc1.html']/strong": 'RFC 1',
".//a[@href='objects.html#envvar-HOME']/tt/span[@class='pre']": 'HOME',
".//a[@href='#with']/tt/span[@class='pre']": '^with$',
".//a[@href='#grammar-token-try_stmt']/tt/span": '^statement$',
".//a[@href='subdir/includes.html']/em": 'Including in subdir',
".//a[@href='objects.html#cmdoption-python-c']/em": 'Python -c option',
# abbreviations
".//abbr[@title='abbreviation']": '^abbr$',
# version stuff
".//span[@class='versionmodified']": 'New in version 0.6',
# footnote reference
".//a[@class='footnote-reference']": r'\[1\]',
# created by reference lookup
".//a[@href='contents.html#ref1']": '',
# ``seealso`` directive
".//div/p[@class='first admonition-title']": 'See also',
# a ``hlist`` directive
".//table[@class='hlist']/tr/td/ul/li": '^This$',
# a ``centered`` directive
".//p[@class='centered']/strong": 'LICENSE',
# a glossary
".//dl/dt[@id='term-boson']": 'boson',
# a production list
".//pre/strong": 'try_stmt',
".//pre/a[@href='#grammar-token-try1_stmt']/tt/span": 'try1_stmt',
# tests for ``only`` directive
".//p": 'A global substitution.',
".//p": 'In HTML.',
".//p": 'In both.',
".//p": 'Always present',
},
'objects.html': {
".//dt[@id='mod.Cls.meth1']": '',
".//dt[@id='errmod.Error']": '',
".//a[@href='#mod.Cls']": '',
".//dl[@class='userdesc']": '',
".//dt[@id='userdesc-myobj']": '',
".//a[@href='#userdesc-myobj']": '',
# C references
".//span[@class='pre']": 'CFunction()',
".//a[@href='#Sphinx_DoSomething']": '',
".//a[@href='#SphinxStruct.member']": '',
".//a[@href='#SPHINX_USE_PYTHON']": '',
".//a[@href='#SphinxType']": '',
".//a[@href='#sphinx_global']": '',
# test global TOC created by toctree()
".//ul[@class='current']/li[@class='toctree-l1 current']/a[@href='']":
'Testing object descriptions',
".//li[@class='toctree-l1']/a[@href='markup.html']":
'Testing various markup',
},
'contents.html': {
".//meta[@name='hc'][@content='hcval']": '',
".//meta[@name='hc_co'][@content='hcval_co']": '',
".//meta[@name='testopt'][@content='testoverride']": '',
#".//td[@class='label']": r'\[Ref1\]', # docutils 0.5 only
".//td[@class='label']": '',
".//li[@class='toctree-l1']/a": 'Testing various markup',
".//li[@class='toctree-l2']/a": 'Inline markup',
".//title": 'Sphinx <Tests>',
".//div[@class='footer']": 'Georg Brandl & Team',
".//a[@href='http://python.org/']": '',
".//li/a[@href='genindex.html']/em": 'Index',
".//li/a[@href='modindex.html']/em": 'Module Index',
".//li/a[@href='search.html']/em": 'Search Page',
},
'bom.html': {
".//title": " File with UTF-8 BOM",
},
'extensions.html': {
".//a[@href='http://python.org/dev/']": "http://python.org/dev/",
".//a[@href='http://bugs.python.org/issue1000']": "issue 1000",
".//a[@href='http://bugs.python.org/issue1042']": "explicit caption",
},
'_static/statictmpl.html': {
".//project": 'Sphinx <Tests>',
},
}
if pygments:
HTML_XPATH['includes.html'].update({
".//pre/span[@class='s']": u'üöä',
".//div[@class='inc-pyobj1 highlight-text']/div/pre":
r'^class Foo:\n pass\n\s*$',
".//div[@class='inc-pyobj2 highlight-text']/div/pre":
r'^ def baz\(\):\n pass\n\s*$',
".//div[@class='inc-lines highlight-text']/div/pre":
r'^class Foo:\n pass\nclass Bar:\n$',
".//div[@class='inc-startend highlight-text']/div/pre":
ur'^foo = u"Including Unicode characters: üöä"\n$',
".//div[@class='inc-preappend highlight-text']/div/pre":
r'(?m)^START CODE$',
".//div[@class='inc-pyobj-dedent highlight-python']/div/pre/span":
r'def',
})
HTML_XPATH['subdir/includes.html'].update({
".//pre/span": 'line 1',
".//pre/span": 'line 2',
})
class NslessParser(ET.XMLParser):
"""XMLParser that throws away namespaces in tag names."""
def _fixname(self, key):
try:
return self._names[key]
except KeyError:
name = key
br = name.find('}')
if br > 0:
name = name[br+1:]
self._names[key] = name = self._fixtext(name)
return name
def check_xpath(etree, fname, path, check):
nodes = list(etree.findall(path))
assert nodes != [], ('did not find any node matching xpath '
'%r in file %s' % (path, fname))
if hasattr(check, '__call__'):
check(nodes)
elif not check:
# only check for node presence
pass
else:
rex = re.compile(check)
for node in nodes:
if node.text and rex.search(node.text):
break
else:
assert False, ('%r not found in any node matching '
'path %s in %s: %r' % (check, path, fname,
[node.text for node in nodes]))
@gen_with_app(buildername='html', warning=html_warnfile, cleanenv=True,
confoverrides={'html_context.hckey_co': 'hcval_co'},
tags=['testtag'])
def test_html(app):
app.builder.build_all()
html_warnings = html_warnfile.getvalue().replace(os.sep, '/')
html_warnings_exp = HTML_WARNINGS % {'root': app.srcdir}
assert html_warnings == html_warnings_exp, 'Warnings don\'t match:\n' + \
'\n'.join(difflib.ndiff(html_warnings_exp.splitlines(),
html_warnings.splitlines()))
for fname, paths in HTML_XPATH.iteritems():
parser = NslessParser()
parser.entity.update(htmlentitydefs.entitydefs)
etree = ET.parse(os.path.join(app.outdir, fname), parser)
for path, check in paths.iteritems():
yield check_xpath, etree, fname, path, check

View File

@ -32,7 +32,7 @@ def test_core_config(app):
# simple default values # simple default values
assert 'exclude_dirs' not in cfg.__dict__ assert 'exclude_dirs' not in cfg.__dict__
assert cfg.exclude_dirs == [] assert cfg.exclude_dirs == []
assert cfg.show_authors == False assert cfg.trim_footnote_reference_space == False
# complex default values # complex default values
assert 'html_title' not in cfg.__dict__ assert 'html_title' not in cfg.__dict__

View File

@ -36,6 +36,7 @@ def test_build(app):
undoc_py, undoc_c = pickle.loads((app.outdir / 'undoc.pickle').text()) undoc_py, undoc_c = pickle.loads((app.outdir / 'undoc.pickle').text())
assert len(undoc_c) == 1 assert len(undoc_c) == 1
# the key is the full path to the header file, which isn't testable # the key is the full path to the header file, which isn't testable
# XXX this should fail right now
assert undoc_c.values()[0] == [('cfunction', 'Py_SphinxTest')] assert undoc_c.values()[0] == [('cfunction', 'Py_SphinxTest')]
assert 'test_autodoc' in undoc_py assert 'test_autodoc' in undoc_py

View File

@ -20,8 +20,8 @@ warnings = []
def setup_module(): def setup_module():
global app, env global app, env
app = TestApp(srcdir='(temp)') app = TestApp(srcdir='(temp)', freshenv=True)
env = BuildEnvironment(app.srcdir, app.doctreedir, app.config) env = app.env
env.set_warnfunc(lambda *args: warnings.append(args)) env.set_warnfunc(lambda *args: warnings.append(args))
def teardown_module(): def teardown_module():
@ -51,7 +51,7 @@ def test_images():
tree = env.get_doctree('images') tree = env.get_doctree('images')
app._warning.reset() app._warning.reset()
htmlbuilder = StandaloneHTMLBuilder(app, env) htmlbuilder = StandaloneHTMLBuilder(app)
htmlbuilder.post_process_images(tree) htmlbuilder.post_process_images(tree)
assert "no matching candidate for image URI u'foo.*'" in \ assert "no matching candidate for image URI u'foo.*'" in \
app._warning.content[-1] app._warning.content[-1]
@ -61,7 +61,7 @@ def test_images():
set(['img.png', 'img1.png', 'simg.png', 'svgimg.svg']) set(['img.png', 'img1.png', 'simg.png', 'svgimg.svg'])
app._warning.reset() app._warning.reset()
latexbuilder = LaTeXBuilder(app, env) latexbuilder = LaTeXBuilder(app)
latexbuilder.post_process_images(tree) latexbuilder.post_process_images(tree)
assert "no matching candidate for image URI u'foo.*'" in \ assert "no matching candidate for image URI u'foo.*'" in \
app._warning.content[-1] app._warning.content[-1]
@ -92,10 +92,10 @@ def test_second_update():
assert 'autodoc' not in env.found_docs assert 'autodoc' not in env.found_docs
def test_object_inventory(): def test_object_inventory():
refs = env.descrefs refs = env.domaindata['py']['objects']
assert 'func_without_module' in refs assert 'func_without_module' in refs
assert refs['func_without_module'] == ('desc', 'function') assert refs['func_without_module'] == ('objects', 'function')
assert 'func_without_module2' in refs assert 'func_without_module2' in refs
assert 'mod.func_in_module' in refs assert 'mod.func_in_module' in refs
assert 'mod.Cls' in refs assert 'mod.Cls' in refs
@ -109,5 +109,8 @@ def test_object_inventory():
assert 'func_in_module' not in refs assert 'func_in_module' not in refs
assert 'func_noindex' not in refs assert 'func_noindex' not in refs
assert 'mod' in env.modules assert env.domaindata['py']['modules']['mod'] == \
assert env.modules['mod'] == ('desc', 'Module synopsis.', 'UNIX', False) ('objects', 'Module synopsis.', 'UNIX', False)
assert env.domains['py'].data is env.domaindata['py']
assert env.domains['c'].data is env.domaindata['c']

112
tests/test_intersphinx.py Normal file
View File

@ -0,0 +1,112 @@
# -*- coding: utf-8 -*-
"""
test_intersphinx
~~~~~~~~~~~~~~~~
Test the intersphinx extension.
:copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import zlib
import posixpath
from cStringIO import StringIO
from docutils import nodes
from sphinx import addnodes
from sphinx.ext.intersphinx import read_inventory_v1, read_inventory_v2, \
fetch_inventory, load_mappings, missing_reference
from util import *
inventory_v1 = '''\
# Sphinx inventory version 1
# Project: foo
# Version: 1.0
module mod foo.html
module.cls class foo.html
'''
inventory_v2 = '''\
# Sphinx inventory version 2
# Project: foo
# Version: 2.0
# The remainder of this file is compressed with zlib.
''' + zlib.compress('''\
module1 py:module 0 foo.html#module-module1
module2 py:module 0 foo.html#module-$
module1.func py:function 1 sub/foo.html#$
CFunc c:function 2 cfunc.html#CFunc
''')
def test_read_inventory_v1():
f = StringIO(inventory_v1)
f.readline()
invdata = read_inventory_v1(f, '/util', posixpath.join)
assert invdata['py:module']['module'] == \
('foo', '1.0', '/util/foo.html#module-module')
assert invdata['py:class']['module.cls'] == \
('foo', '1.0', '/util/foo.html#module.cls')
def test_read_inventory_v2():
f = StringIO(inventory_v2)
f.readline()
invdata1 = read_inventory_v2(f, '/util', posixpath.join)
# try again with a small buffer size to test the chunking algorithm
f = StringIO(inventory_v2)
f.readline()
invdata2 = read_inventory_v2(f, '/util', posixpath.join, bufsize=5)
assert invdata1 == invdata2
assert len(invdata1['py:module']) == 2
assert invdata1['py:module']['module1'] == \
('foo', '2.0', '/util/foo.html#module-module1')
assert invdata1['py:module']['module2'] == \
('foo', '2.0', '/util/foo.html#module-module2')
assert invdata1['py:function']['module1.func'][2] == \
'/util/sub/foo.html#module1.func'
assert invdata1['c:function']['CFunc'][2] == '/util/cfunc.html#CFunc'
@with_app(confoverrides={'extensions': 'sphinx.ext.intersphinx'})
@with_tempdir
def test_missing_reference(tempdir, app):
inv_file = tempdir / 'inventory'
write_file(inv_file, inventory_v2)
app.config.intersphinx_mapping = {'http://docs.python.org/': inv_file}
app.config.intersphinx_cache_limit = 0
# load the inventory and check if it's done correctly
load_mappings(app)
inv = app.env.intersphinx_inventory
assert inv['py:module']['module2'] == \
('foo', '2.0', 'http://docs.python.org/foo.html#module-module2')
# create fake nodes and check referencing
contnode = nodes.emphasis('foo')
refnode = addnodes.pending_xref('')
refnode['reftarget'] = 'module1.func'
refnode['reftype'] = 'func'
refnode['refdomain'] = 'py'
rn = missing_reference(app, app.env, refnode, contnode)
assert isinstance(rn, nodes.reference)
assert rn['refuri'] == 'http://docs.python.org/sub/foo.html#module1.func'
assert rn['reftitle'] == '(in foo v2.0)'
assert rn[0] is contnode
# create unresolvable nodes and check None return value
refnode['reftype'] = 'foo'
assert missing_reference(app, app.env, refnode, contnode) is None
refnode['reftype'] = 'function'
refnode['reftarget'] = 'foo.func'
assert missing_reference(app, app.env, refnode, contnode) is None

View File

@ -29,6 +29,7 @@ def setup_module():
components=(rst.Parser, HTMLWriter, LaTeXWriter)) components=(rst.Parser, HTMLWriter, LaTeXWriter))
settings = optparser.get_default_values() settings = optparser.get_default_values()
settings.env = app.builder.env settings.env = app.builder.env
settings.env.patch_lookup_functions()
parser = rst.Parser() parser = rst.Parser()
def teardown_module(): def teardown_module():
@ -60,7 +61,7 @@ def verify_re(rst, html_expected, latex_expected):
html_translator = ForgivingHTMLTranslator(app.builder, document) html_translator = ForgivingHTMLTranslator(app.builder, document)
document.walkabout(html_translator) document.walkabout(html_translator)
html_translated = ''.join(html_translator.fragment).strip() html_translated = ''.join(html_translator.fragment).strip()
assert re.match(html_expected, html_translated), 'from' + rst assert re.match(html_expected, html_translated), 'from ' + rst
if latex_expected: if latex_expected:
latex_translator = ForgivingLaTeXTranslator(document, app.builder) latex_translator = ForgivingLaTeXTranslator(document, app.builder)

View File

@ -184,9 +184,9 @@ def gen_with_app(*args, **kwargs):
def with_tempdir(func): def with_tempdir(func):
def new_func(): def new_func(*args, **kwds):
tempdir = path(tempfile.mkdtemp()) tempdir = path(tempfile.mkdtemp())
func(tempdir) func(tempdir, *args, **kwds)
tempdir.rmtree() tempdir.rmtree()
new_func.__name__ = func.__name__ new_func.__name__ = func.__name__
return new_func return new_func