summaryrefslogtreecommitdiff
path: root/sphinx
diff options
context:
space:
mode:
authorGeorg Brandl <georg@python.org>2010-01-07 19:07:50 +0100
committerGeorg Brandl <georg@python.org>2010-01-07 19:07:50 +0100
commitf35d74d9cfa6c934b35af1e7e71efcbc63a3e3a5 (patch)
treedeb3ff63159b82b5d966600bf8c832cf8864f148 /sphinx
parenta5008da8ee1b8ada041851b390818714a67f0af2 (diff)
parent0afca45dbc97654e489df0367bf92d9b4803cf78 (diff)
downloadsphinx-f35d74d9cfa6c934b35af1e7e71efcbc63a3e3a5.tar.gz
merge with trunk
Diffstat (limited to 'sphinx')
-rw-r--r--sphinx/__init__.py2
-rw-r--r--sphinx/addnodes.py2
-rw-r--r--sphinx/application.py148
-rw-r--r--sphinx/builders/__init__.py77
-rw-r--r--sphinx/builders/devhelp.py10
-rw-r--r--sphinx/builders/html.py54
-rw-r--r--sphinx/config.py5
-rw-r--r--sphinx/directives/__init__.py269
-rw-r--r--sphinx/directives/code.py13
-rw-r--r--sphinx/directives/desc.py771
-rw-r--r--sphinx/directives/other.py261
-rw-r--r--sphinx/domains/__init__.py180
-rw-r--r--sphinx/domains/c.py204
-rw-r--r--sphinx/domains/python.py514
-rw-r--r--sphinx/domains/std.py384
-rw-r--r--sphinx/environment.py451
-rw-r--r--sphinx/ext/autodoc.py30
-rw-r--r--sphinx/ext/autosummary/__init__.py10
-rw-r--r--sphinx/ext/coverage.py14
-rw-r--r--sphinx/ext/inheritance_diagram.py7
-rw-r--r--sphinx/ext/intersphinx.py133
-rw-r--r--sphinx/ext/refcounting.py2
-rw-r--r--sphinx/ext/todo.py4
-rw-r--r--sphinx/jinja2glue.py5
-rw-r--r--sphinx/locale/__init__.py190
-rw-r--r--sphinx/locale/ca/LC_MESSAGES/sphinx.js1
-rw-r--r--sphinx/locale/ca/LC_MESSAGES/sphinx.mobin0 -> 8114 bytes
-rw-r--r--sphinx/locale/ca/LC_MESSAGES/sphinx.po606
-rw-r--r--sphinx/locale/sphinx.pot306
-rw-r--r--sphinx/roles.py274
-rw-r--r--sphinx/search.py66
-rw-r--r--sphinx/themes/basic/static/doctools.js7
-rw-r--r--sphinx/themes/basic/static/searchtools.js45
-rw-r--r--sphinx/util/__init__.py25
-rw-r--r--sphinx/util/compat.py69
-rw-r--r--sphinx/writers/html.py23
-rw-r--r--sphinx/writers/latex.py5
-rw-r--r--sphinx/writers/text.py4
38 files changed, 3242 insertions, 1929 deletions
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index 31726e4b..6fd96bdc 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -12,7 +12,7 @@
import sys
from os import path
-__version__ = '1.0'
+__version__ = '1.0pre'
__released__ = '1.0 (hg)' # used when Sphinx builds its own docs
package_dir = path.abspath(path.dirname(__file__))
diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py
index 83ba8a00..2b62633f 100644
--- a/sphinx/addnodes.py
+++ b/sphinx/addnodes.py
@@ -14,7 +14,7 @@ from docutils import nodes
# index markup
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
class desc(nodes.Admonition, nodes.Element): pass
diff --git a/sphinx/application.py b/sphinx/application.py
index ad3a0833..99753b73 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -14,21 +14,24 @@
import sys
import types
import posixpath
+from os import path
from cStringIO import StringIO
from docutils import nodes
-from docutils.parsers.rst import directives, roles
+from docutils.parsers.rst import Directive, convert_directive_function, \
+ directives, roles
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.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.directives import GenericDesc, Target, additional_xref_types
-from sphinx.environment import SphinxStandaloneReader
-from sphinx.util import pycompat # imported for side-effects
+from sphinx.environment import BuildEnvironment, SphinxStandaloneReader
+from sphinx.util import ENOENT, pycompat # pycompat imported for side-effects
from sphinx.util.tags import Tags
-from sphinx.util.compat import Directive, directive_dwim
from sphinx.util.console import bold
@@ -49,6 +52,7 @@ events = {
}
CONFIG_FILENAME = 'conf.py'
+ENV_PICKLE_FILENAME = 'environment.pickle'
class Sphinx(object):
@@ -61,6 +65,7 @@ class Sphinx(object):
self._listeners = {}
self.builderclasses = BUILTIN_BUILDERS.copy()
self.builder = None
+ self.env = None
self.srcdir = srcdir
self.confdir = confdir
@@ -103,8 +108,62 @@ class Sphinx(object):
# now that we know all config values, collect them from conf.py
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:
- print >>status, 'No builder selected, using default: html'
+ print >>self._status, 'No builder selected, using default: html'
buildername = 'html'
if buildername not in self.builderclasses:
raise SphinxError('Builder name %s not registered' % buildername)
@@ -115,9 +174,7 @@ class Sphinx(object):
mod, cls = builderclass
builderclass = getattr(
__import__('sphinx.builders.' + mod, None, None, [cls]), cls)
- self.builder = builderclass(self, freshenv=freshenv)
- self.builder.tags = self.tags
- self.builder.tags.add(self.builder.format)
+ self.builder = builderclass(self)
self.emit('builder-inited')
def build(self, all_files, filenames):
@@ -277,17 +334,21 @@ class Sphinx(object):
if 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 content or arguments or options:
raise ExtensionError('when adding directive classes, no '
'additional arguments may be given')
- directives.register_directive(name, directive_dwim(obj))
+ return obj
else:
obj.content = content
- obj.arguments = arguments
+ obj.arguments = arguments or (0, 0, False)
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):
roles.register_local_role(name, role)
@@ -298,23 +359,52 @@ class Sphinx(object):
role = roles.GenericRole(name, nodeclass)
roles.register_local_role(name, role)
- def add_description_unit(self, directivename, rolename, indextemplate='',
- parse_node=None, ref_nodeclass=None):
- additional_xref_types[directivename] = (rolename, indextemplate,
- parse_node)
- directives.register_directive(directivename,
- directive_dwim(GenericDesc))
- roles.register_local_role(rolename, xfileref_role)
- if ref_nodeclass is not None:
- innernodetypes[rolename] = ref_nodeclass
+ def add_domain(self, domain):
+ # XXX needs to be documented
+ # XXX what about subclassing and overriding?
+ if domain.name in all_domains:
+ raise ExtensionError('domain %s already registered' % domain.name)
+ all_domains[domain.name] = domain
+
+ def add_directive_to_domain(self, domain, name, obj,
+ 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='',
- ref_nodeclass=None):
- additional_xref_types[directivename] = (rolename, indextemplate, None)
- directives.register_directive(directivename, directive_dwim(Target))
- roles.register_local_role(rolename, xfileref_role)
- if ref_nodeclass is not None:
- innernodetypes[rolename] = ref_nodeclass
+ ref_nodeclass=None, objname=''):
+ StandardDomain.object_types[directivename] = \
+ ObjType(objname or directivename, rolename)
+ # create a subclass of Target as the new directive
+ new_directive = type(directivename, (Target, object),
+ {'indextemplate': indextemplate})
+ StandardDomain.directives[directivename] = new_directive
+ # XXX support more options?
+ StandardDomain.roles[rolename] = XRefRole(innernodeclass=ref_nodeclass)
def add_transform(self, transform):
SphinxStandaloneReader.transforms.append(transform)
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index 159d1f03..da9b54e7 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -15,9 +15,7 @@ from os import path
from docutils import nodes
-from sphinx import package_dir, locale
-from sphinx.util import SEP, ENOENT, relative_uri
-from sphinx.environment import BuildEnvironment
+from sphinx.util import SEP, relative_uri
from sphinx.util.console import bold, purple, darkgreen, term_width_line
# side effect: registers roles and directives
@@ -25,9 +23,6 @@ from sphinx import roles
from sphinx import directives
-ENV_PICKLE_FILENAME = 'environment.pickle'
-
-
class Builder(object):
"""
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
format = ''
- def __init__(self, app, env=None, freshenv=False):
+ def __init__(self, app):
+ self.env = app.env
self.srcdir = app.srcdir
self.confdir = app.confdir
self.outdir = app.outdir
@@ -50,21 +46,15 @@ class Builder(object):
self.warn = app.warn
self.info = app.info
self.config = app.config
-
- self.load_i18n()
+ self.tags = app.tags
+ self.tags.add(self.format)
# images that need to be copied over (source -> dest)
self.images = {}
- # if None, this is set in load_env()
- self.env = env
- self.freshenv = freshenv
-
self.init()
- self.load_env()
# helper methods
-
def init(self):
"""
Load necessary templates and perform initialization. The default
@@ -167,62 +157,6 @@ class Builder(object):
# 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):
"""Build all source files."""
self.build(None, summary='all source files', method='all')
@@ -302,6 +236,7 @@ class Builder(object):
if updated_docnames:
# save the environment
+ from sphinx.application import ENV_PICKLE_FILENAME
self.info(bold('pickling environment... '), nonl=True)
self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME))
self.info('done')
diff --git a/sphinx/builders/devhelp.py b/sphinx/builders/devhelp.py
index d7acf764..88fccadc 100644
--- a/sphinx/builders/devhelp.py
+++ b/sphinx/builders/devhelp.py
@@ -12,6 +12,7 @@
"""
import os
+import re
import cgi
import sys
from os import path
@@ -30,7 +31,7 @@ except ImportError:
try:
import elementtree.ElementTree as etree
except ImportError:
- import cElementTree.ElemenTree as etree
+ import cElementTree as etree
try:
import gzip
@@ -114,11 +115,14 @@ class DevhelpBuilder(StandaloneHTMLBuilder):
else:
for i, ref in enumerate(refs):
etree.SubElement(functions, 'function',
- name="%s [%d]" % (title, i), link=ref)
+ name="[%d] %s" % (i, title),
+ link=ref)
if subitems:
+ parent_title = re.sub(r'\s*\(.*\)\s*$', '', title)
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 title, (refs, subitems) in group:
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 719cc638..5c03bf80 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -10,6 +10,7 @@
"""
import os
+import zlib
import codecs
import posixpath
import cPickle as pickle
@@ -22,7 +23,7 @@ except ImportError:
from docutils import nodes
from docutils.io import DocTreeInput, StringOutput
-from docutils.core import Publisher, publish_parts
+from docutils.core import Publisher
from docutils.utils import new_document
from docutils.frontend import OptionParser
from docutils.readers.doctree import Reader as DoctreeReader
@@ -33,7 +34,8 @@ from sphinx.util import SEP, os_path, relative_uri, ensuredir, \
from sphinx.errors import SphinxError
from sphinx.search import js_index
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.util.console import bold
from sphinx.writers.html import HTMLWriter, HTMLTranslator, \
@@ -243,7 +245,9 @@ class StandaloneHTMLBuilder(Builder):
rellinks = []
if self.config.html_use_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'),
'M', _('modules')))
@@ -407,12 +411,13 @@ class StandaloneHTMLBuilder(Builder):
# 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
modules = sorted(((mn, (self.get_relative_uri('modindex', fn) +
'#module-' + mn, sy, pl, dep))
for (mn, (fn, sy, pl, dep)) in
- self.env.modules.iteritems()),
+ moduleindex.iteritems()),
key=lambda x: x[0].lower())
# collect all platforms
platforms = set()
@@ -646,7 +651,7 @@ class StandaloneHTMLBuilder(Builder):
if self.indexer is not None and title:
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(
docname, self, collapse))['fragment']
@@ -700,6 +705,28 @@ class StandaloneHTMLBuilder(Builder):
ensuredir(path.dirname(source_name))
copyfile(self.env.doc2path(pagename), source_name)
+ def dump_inventory(self):
+ self.info(bold('dumping object inventory... '), nonl=True)
+ f = open(path.join(self.outdir, INVENTORY_FILENAME), 'wb')
+ try:
+ f.write('# Sphinx inventory version 2\n')
+ f.write('# Project: %s\n' % self.config.project.encode('utf-8'))
+ f.write('# Version: %s\n' % self.config.version)
+ f.write('# The remainder of this file is compressed using zlib.\n')
+ compressor = zlib.compressobj(9)
+ for domainname, domain in self.env.domains.iteritems():
+ for name, type, docname, anchor, prio in domain.get_objects():
+ 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:
+ f.close()
+ self.info('done')
+
def handle_finish(self):
self.info(bold('dumping search index... '), nonl=True)
self.indexer.prune(self.env.all_docs)
@@ -714,20 +741,7 @@ class StandaloneHTMLBuilder(Builder):
movefile(searchindexfn + '.tmp', searchindexfn)
self.info('done')
- self.info(bold('dumping object inventory... '), nonl=True)
- f = open(path.join(self.outdir, INVENTORY_FILENAME), 'w')
- try:
- f.write('# Sphinx inventory version 1\n')
- f.write('# Project: %s\n' % self.config.project.encode('utf-8'))
- f.write('# Version: %s\n' % self.config.version)
- for modname, info in self.env.modules.iteritems():
- f.write('%s mod %s\n' % (modname, self.get_target_uri(info[0])))
- for refname, (docname, desctype) in self.env.descrefs.iteritems():
- f.write('%s %s %s\n' % (refname, desctype,
- self.get_target_uri(docname)))
- finally:
- f.close()
- self.info('done')
+ self.dump_inventory()
class DirectoryHTMLBuilder(StandaloneHTMLBuilder):
diff --git a/sphinx/config.py b/sphinx/config.py
index d8865d62..d14a03f6 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -55,6 +55,7 @@ class Config(object):
modindex_common_prefix = ([], 'html'),
rst_epilog = (None, 'env'),
trim_doctest_flags = (True, 'env'),
+ default_domain = ('py', 'env'),
# HTML options
html_theme = ('default', 'html'),
@@ -127,6 +128,10 @@ class Config(object):
latex_docclass = ({}, None),
# now deprecated - use latex_elements
latex_preamble = ('', None),
+
+ # text options
+ text_sectionchars = ('*=-~"+`', 'text'),
+ text_windows_newlines = (False, 'text'),
)
def __init__(self, dirname, filename, overrides, tags):
diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py
index 00bb55ec..0ee18d22 100644
--- a/sphinx/directives/__init__.py
+++ b/sphinx/directives/__init__.py
@@ -9,11 +9,16 @@
: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 sphinx import addnodes
+from sphinx.locale import l_
+
# import and register directives
-from sphinx.directives.desc import *
from sphinx.directives.code import *
from sphinx.directives.other import *
@@ -25,3 +30,263 @@ try:
except AttributeError:
images.figure.options['figwidth'] = \
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)
diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py
index 1ae8b3a8..3bf99019 100644
--- a/sphinx/directives/code.py
+++ b/sphinx/directives/code.py
@@ -13,11 +13,10 @@ import codecs
from os import path
from docutils import nodes
-from docutils.parsers.rst import directives
+from docutils.parsers.rst import Directive, directives
from sphinx import addnodes
from sphinx.util import parselinenos
-from sphinx.util.compat import Directive, directive_dwim
class Highlight(Directive):
@@ -184,8 +183,8 @@ class LiteralInclude(Directive):
return [retnode]
-directives.register_directive('highlight', directive_dwim(Highlight))
-directives.register_directive('highlightlang', directive_dwim(Highlight)) # old
-directives.register_directive('code-block', directive_dwim(CodeBlock))
-directives.register_directive('sourcecode', directive_dwim(CodeBlock))
-directives.register_directive('literalinclude', directive_dwim(LiteralInclude))
+directives.register_directive('highlight', Highlight)
+directives.register_directive('highlightlang', Highlight) # old
+directives.register_directive('code-block', CodeBlock)
+directives.register_directive('sourcecode', CodeBlock)
+directives.register_directive('literalinclude', LiteralInclude)
diff --git a/sphinx/directives/desc.py b/sphinx/directives/desc.py
deleted file mode 100644
index 5a8ed791..00000000
--- a/sphinx/directives/desc.py
+++ /dev/null
@@ -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))
diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py
index 1c831158..2c996053 100644
--- a/sphinx/directives/other.py
+++ b/sphinx/directives/other.py
@@ -10,13 +10,13 @@
import re
from docutils import nodes
-from docutils.parsers.rst import directives
+from docutils.parsers.rst import Directive, directives
from sphinx import addnodes
from sphinx.locale import pairindextypes
from sphinx.util import patfilter, ws_re, url_re, docname_join, \
explicit_title_re
-from sphinx.util.compat import Directive, directive_dwim, make_admonition
+from sphinx.util.compat import make_admonition
class TocTree(Directive):
@@ -104,75 +104,6 @@ class TocTree(Directive):
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):
"""
Directive to give the name of the author of the current document
@@ -205,27 +136,6 @@ class Author(Directive):
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):
"""
Directive to add entries to the index.
@@ -244,8 +154,7 @@ class Index(Directive):
def run(self):
arguments = self.arguments[0].split('\n')
env = self.state.document.settings.env
- targetid = 'index-%s' % env.index_num
- env.index_num += 1
+ targetid = 'index-%s' % env.new_serialno('index')
targetnode = nodes.target('', '', ids=[targetid])
self.state.document.note_explicit_target(targetnode)
indexnode = addnodes.index()
@@ -302,7 +211,11 @@ class VersionChange(Directive):
else:
ret = [node]
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
@@ -332,66 +245,6 @@ class SeeAlso(Directive):
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):
"""
Directive to give an explicit tabulary column definition to LaTeX.
@@ -409,57 +262,6 @@ class TabularColumns(Directive):
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):
"""
Directive to create a centered line of bold text.
@@ -562,36 +364,21 @@ class Only(Directive):
return [node]
-directives.register_directive('toctree', directive_dwim(TocTree))
-directives.register_directive('module', directive_dwim(Module))
-directives.register_directive('currentmodule', directive_dwim(CurrentModule))
-directives.register_directive('sectionauthor', directive_dwim(Author))
-directives.register_directive('moduleauthor', directive_dwim(Author))
-directives.register_directive('program', directive_dwim(Program))
-directives.register_directive('index', directive_dwim(Index))
-directives.register_directive('deprecated', directive_dwim(VersionChange))
-directives.register_directive('versionadded', directive_dwim(VersionChange))
-directives.register_directive('versionchanged', directive_dwim(VersionChange))
-directives.register_directive('seealso', directive_dwim(SeeAlso))
-directives.register_directive('productionlist', directive_dwim(ProductionList))
-directives.register_directive('tabularcolumns', directive_dwim(TabularColumns))
-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))
+directives.register_directive('toctree', TocTree)
+directives.register_directive('sectionauthor', Author)
+directives.register_directive('moduleauthor', Author)
+directives.register_directive('index', Index)
+directives.register_directive('deprecated', VersionChange)
+directives.register_directive('versionadded', VersionChange)
+directives.register_directive('versionchanged', VersionChange)
+directives.register_directive('seealso', SeeAlso)
+directives.register_directive('tabularcolumns', TabularColumns)
+directives.register_directive('centered', Centered)
+directives.register_directive('acks', Acks)
+directives.register_directive('hlist', HList)
+directives.register_directive('only', Only)
# register the standard rst class directive under a different name
-
-try:
- # docutils 0.4
- 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
+# only for backwards compatibility now
+from docutils.parsers.rst.directives.misc import Class
+directives.register_directive('cssclass', Class)
diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py
new file mode 100644
index 00000000..9e77cce8
--- /dev/null
+++ b/sphinx/domains/__init__.py
@@ -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-2010 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,
+}
diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py
new file mode 100644
index 00000000..7a29ff68
--- /dev/null
+++ b/sphinx/domains/c.py
@@ -0,0 +1,204 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.domains.c
+ ~~~~~~~~~~~~~~~~
+
+ The C language domain.
+
+ :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.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)
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
new file mode 100644
index 00000000..1540f90b
--- /dev/null
+++ b/sphinx/domains/python.py
@@ -0,0 +1,514 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.domains.python
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ The Python domain.
+
+ :copyright: Copyright 2007-2010 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)
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
new file mode 100644
index 00000000..4e7cca3a
--- /dev/null
+++ b/sphinx/domains/std.py
@@ -0,0 +1,384 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.domains.std
+ ~~~~~~~~~~~~~~~~~~
+
+ The standard domain.
+
+ :copyright: Copyright 2007-2010 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'])
diff --git a/sphinx/environment.py b/sphinx/environment.py
index 4c4c40e6..a7fcc38b 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -26,7 +26,7 @@ from docutils.io import FileInput, NullOutput
from docutils.core import Publisher
from docutils.utils import Reporter, relative_path
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.directives.html import MetaBody
from docutils.writers import UnfilteredWriter
@@ -35,9 +35,41 @@ from docutils.transforms.parts import ContentsFilter
from sphinx import addnodes
from sphinx.util import movefile, get_matching_docs, SEP, ustrftime, \
- docname_join, FilenameUniqDict, url_re, clean_astext
-from sphinx.errors import SphinxError
-from sphinx.directives import additional_xref_types
+ docname_join, FilenameUniqDict, url_re, make_refnode, clean_astext
+from sphinx.errors import SphinxError, ExtensionError
+
+
+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 = {
'embed_stylesheet': False,
@@ -51,7 +83,7 @@ default_settings = {
# This is increased every time an environment attribute is added
# or changed to properly invalidate pickle files.
-ENV_VERSION = 30
+ENV_VERSION = 32
default_substitutions = set([
@@ -202,9 +234,9 @@ class BuildEnvironment:
env = pickle.load(picklefile)
finally:
picklefile.close()
- env.config.values = config.values
if env.version != ENV_VERSION:
raise IOError('env version not current')
+ env.config.values = config.values
return env
def topickle(self, filename):
@@ -213,6 +245,8 @@ class BuildEnvironment:
self.set_warnfunc(None)
values = self.config.values
del self.config.values
+ domains = self.domains
+ del self.domains
# first write to a temporary file, so that if dumping fails,
# the existing environment won't be overwritten
picklefile = open(filename + '.tmp', 'wb')
@@ -229,6 +263,7 @@ class BuildEnvironment:
picklefile.close()
movefile(filename + '.tmp', filename)
# reset attributes
+ self.domains = domains
self.config.values = values
self.set_warnfunc(warnfunc)
@@ -242,6 +277,9 @@ class BuildEnvironment:
# the application object; only set while update() runs
self.app = None
+ # all the registered domains, set by the application
+ self.domains = {}
+
# the docutils settings for building
self.settings = default_settings.copy()
self.settings['env'] = self
@@ -280,16 +318,13 @@ class BuildEnvironment:
self.glob_toctrees = set() # docnames that have :glob: 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
- self.descrefs = {} # fullname -> docname, desctype
- self.filemodules = {} # docname -> [modules]
- self.modules = {} # modname -> docname, synopsis,
- # platform, deprecated
self.labels = {} # labelname -> docname, labelid, sectionname
self.anonlabels = {} # labelname -> docname, labelid
- self.progoptions = {} # (program, name) -> docname, labelid
- self.reftargets = {} # (type, name) -> docname, labelid
- # type: term, token, envvar, citation
+ self.citations = {} # citation name -> docname, labelid
# Other inventories
self.indexentries = {} # docname -> list of
@@ -301,14 +336,8 @@ class BuildEnvironment:
self.images = FilenameUniqDict()
self.dlfiles = FilenameUniqDict()
- # These are set while parsing a file
- self.docname = None # current document name
- 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
+ # temporary data storage while reading a document
+ self.doc_read_data = {}
# Some magically present labels
def add_magic_label(name, description):
@@ -342,7 +371,6 @@ class BuildEnvironment:
self.toc_secnumbers.pop(docname, None)
self.toc_num_entries.pop(docname, None)
self.toctree_includes.pop(docname, None)
- self.filemodules.pop(docname, None)
self.indexentries.pop(docname, None)
self.glob_toctrees.discard(docname)
self.numbered_toctrees.discard(docname)
@@ -353,25 +381,20 @@ class BuildEnvironment:
fnset.discard(docname)
if not fnset:
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():
if fn == docname:
del self.labels[labelname]
- for key, (fn, _) in self.reftargets.items():
- if fn == docname:
- del self.reftargets[key]
- for key, (fn, _) in self.progoptions.items():
+ for key, (fn, _) in self.citations.items():
if fn == docname:
- del self.progoptions[key]
+ del self.citations[key]
for version, changes in self.versionchanges.items():
new = [change for change in changes if change[1] != docname]
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):
"""
Return the filename for the document name.
@@ -543,6 +566,26 @@ class BuildEnvironment:
error.object[error.end:lineend]), lineno)
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):
"""
Parse a file and add/update inventory entries for the doctree.
@@ -551,6 +594,7 @@ class BuildEnvironment:
# remove all inventory entries for that file
if app:
app.emit('env-purge-doc', self, docname)
+
self.clear_doc(docname)
if src_path is None:
@@ -565,12 +609,16 @@ class BuildEnvironment:
self.warn(docname, 'default role %s not found' %
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['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)
@@ -603,6 +651,8 @@ class BuildEnvironment:
doctree = pub.document
except UnicodeError, err:
raise SphinxError(str(err))
+
+ # post-processing
self.filter_messages(doctree)
self.process_dependencies(docname, doctree)
self.process_images(docname, doctree)
@@ -614,12 +664,13 @@ class BuildEnvironment:
self.note_citations_from(docname, doctree)
self.build_toc_from(docname, doctree)
- # store time of reading, used to find outdated files
- self.all_docs[docname] = time.time()
-
+ # allow extension-specific post-processing
if app:
app.emit('doctree-read', doctree)
+ # store time of reading, used to find outdated files
+ self.all_docs[docname] = time.time()
+
# make it picklable
doctree.reporter = None
doctree.transformer = None
@@ -631,10 +682,7 @@ class BuildEnvironment:
metanode.__class__ = addnodes.meta
# cleanup
- self.docname = None
- self.currmodule = None
- self.currclass = None
- self.gloss_entries = set()
+ self.doc_read_data.clear()
if save_parsed:
# save the parsed doctree
@@ -651,6 +699,35 @@ class BuildEnvironment:
else:
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):
"""
Filter system messages from a doctree.
@@ -812,7 +889,7 @@ class BuildEnvironment:
if name.isdigit() or node.has_key('refuri') or \
node.tagname.startswith('desc_'):
# ignore footnote labels, labels automatically generated from a
- # link and description units
+ # link and object descriptions
continue
if name in self.labels:
self.warn(docname, 'duplicate label %s, ' % name +
@@ -842,11 +919,11 @@ class BuildEnvironment:
def note_citations_from(self, docname, document):
for node in document.traverse(nodes.citation):
label = node[0].astext()
- if ('citation', label) in self.reftargets:
+ if label in self.citations:
self.warn(docname, 'duplicate citation %s, ' % label +
'other instance in %s' % self.doc2path(
- self.reftargets['citation', label][0]), node.line)
- self.reftargets['citation', label] = (docname, node['ids'][0])
+ self.citations[label][0]), node.line)
+ self.citations[label] = (docname, node['ids'][0])
def note_toctree(self, docname, toctreenode):
"""Note a TOC tree directive in a document and gather information about
@@ -934,45 +1011,23 @@ class BuildEnvironment:
node['refuri'] = node['anchorname'] or '#'
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."""
doctree = self.get_doctree(self.config.master_doc)
for toctreenode in doctree.traverse(addnodes.toctree):
result = self.resolve_toctree(docname, builder, toctreenode,
- prune=True, collapse=collapse)
+ prune=True, collapse=collapse,
+ maxdepth=maxdepth)
if result is not None:
return result
- # -------
- # these are called from docutils directives and therefore use self.docname
- #
- def note_descref(self, fullname, desctype, line):
- if fullname in self.descrefs:
- self.warn(self.docname,
- 'duplicate canonical description name %s, ' % fullname +
- '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)
- # -------
+ def get_domain(self, domainname):
+ """Return the domain instance with the specified name.
+ Raises an ExtensionError if the domain is not registered."""
+ try:
+ return self.domains[domainname]
+ except KeyError:
+ raise ExtensionError('Domain %r is not registered' % domainname)
# --------- RESOLVING REFERENCES AND TOCTREES ------------------------------
@@ -1156,15 +1211,7 @@ class BuildEnvironment:
docname, refnode['refuri']) + refnode['anchorname']
return newnode
- descroles = frozenset(('data', 'exc', 'func', 'class', 'const',
- 'attr', 'obj', 'meth', 'cfunc', 'cmember',
- 'cdata', 'ctype', 'cmacro'))
-
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):
contnode = node[0].deepcopy()
newnode = None
@@ -1173,8 +1220,17 @@ class BuildEnvironment:
target = node['reftarget']
try:
- if typ == 'ref':
- if node['refcaption']:
+ if node.has_key('refdomain') and node['refdomain']:
+ # 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
# the supplied link caption
docname, labelid = self.anonlabels.get(target, ('',''))
@@ -1221,7 +1277,7 @@ class BuildEnvironment:
node.line)
newnode = contnode
else:
- if node['refcaption']:
+ if node['refexplicit']:
# reference with explicit title
caption = node.astext()
else:
@@ -1231,101 +1287,35 @@ class BuildEnvironment:
newnode['refuri'] = builder.get_relative_uri(
fromdocname, docname)
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':
- # keywords are referenced by named labels
+ # keywords are oddballs: they are referenced by named labels
docname, labelid, _ = self.labels.get(target, ('','',''))
if not docname:
#self.warn(fromdocname, 'unknown keyword: %s' % target)
- 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 == '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
+ newnode = None
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)
+ newnode = make_refnode(builder, fromdocname, docname,
+ labelid, contnode)
else:
raise RuntimeError('unknown xfileref node encountered: %s'
% 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:
newnode = contnode
- if newnode:
- node.replace_self(newnode)
+ node.replace_self(newnode or contnode)
for node in doctree.traverse(addnodes.only):
try:
@@ -1556,116 +1546,3 @@ class BuildEnvironment:
# the master file is not included anywhere ;)
continue
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)
diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py
index 5d71d25c..5b862ad8 100644
--- a/sphinx/ext/autodoc.py
+++ b/sphinx/ext/autodoc.py
@@ -392,9 +392,11 @@ class Documenter(object):
def add_directive_header(self, sig):
"""Add the directive header and options to the generated content."""
+ domain = getattr(self, 'domain', 'py')
directive = getattr(self, 'directivetype', self.objtype)
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:
self.add_line(u' :noindex:', '<autodoc>')
if self.objpath:
@@ -548,9 +550,9 @@ class Documenter(object):
do all members, else those given by *self.options.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:
- 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 \
self.options.members is ALL
@@ -591,8 +593,8 @@ class Documenter(object):
check_module=members_check_module)
# reset current objects
- self.env.autodoc_current_module = None
- self.env.autodoc_current_class = None
+ self.env.doc_read_data['autodoc_module'] = None
+ self.env.doc_read_data['autodoc_class'] = None
def generate(self, more_content=None, real_modname=None,
check_module=False, all_members=False):
@@ -750,11 +752,10 @@ class ModuleLevelDocumenter(Documenter):
else:
# if documenting a toplevel object without explicit module,
# it can be contained in another auto directive ...
- if hasattr(self.env, 'autodoc_current_module'):
- modname = self.env.autodoc_current_module
+ modname = self.env.doc_read_data.get('autodoc_module')
# ... or in the scope of a module directive
if not modname:
- modname = self.env.currmodule
+ modname = self.env.doc_read_data.get('py_module')
# ... else, it stays None, which means invalid
return modname, parents + [base]
@@ -773,21 +774,20 @@ class ClassLevelDocumenter(Documenter):
# if documenting a class-level object without path,
# there must be a current class, either from a parent
# auto directive ...
- if hasattr(self.env, 'autodoc_current_class'):
- mod_cls = self.env.autodoc_current_class
+ mod_cls = self.env.doc_read_data.get('autodoc_class')
# ... or from a class directive
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 mod_cls is None:
return None, []
modname, cls = rpartition(mod_cls, '.')
parents = [cls]
# 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:
- 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
return modname, parents + [base]
@@ -1091,7 +1091,7 @@ class AutoDirective(Directive):
# record all filenames as dependencies -- this will at least
# partially make automatic invalidation possible
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
# filename/description and lineno
diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py
index da41acb2..0e3bdf16 100644
--- a/sphinx/ext/autosummary/__init__.py
+++ b/sphinx/ext/autosummary/__init__.py
@@ -228,8 +228,9 @@ class Autosummary(Directive):
env = self.state.document.settings.env
prefixes = ['']
- if env.currmodule:
- prefixes.insert(0, env.currmodule)
+ currmodule = env.doc_read_data.get('py_module')
+ if currmodule:
+ prefixes.insert(0, currmodule)
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;
otherwise expands to '*text*'.
"""
- r = roles.xfileref_role('obj', rawtext, etext, lineno, inliner,
- options, content)
+ env = inliner.document.settings.env
+ r = env.get_domain('py').role('obj')(
+ 'obj', rawtext, etext, lineno, inliner, options, content)
pnode = r[0][0]
prefixes = [None]
diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py
index 0d2487f0..4924d30b 100644
--- a/sphinx/ext/coverage.py
+++ b/sphinx/ext/coverage.py
@@ -79,6 +79,7 @@ class CoverageBuilder(Builder):
def build_c_coverage(self):
# Fetch all the info from the header files
+ c_objects = self.env.domaindata['c']['objects']
for filename in self.c_sourcefiles:
undoc = []
f = open(filename, 'r')
@@ -88,7 +89,7 @@ class CoverageBuilder(Builder):
match = regex.match(line)
if match:
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, ()):
if exp.match(name):
break
@@ -116,7 +117,10 @@ class CoverageBuilder(Builder):
op.close()
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
for exp in self.mod_ignorexps:
if exp.match(mod_name):
@@ -151,7 +155,7 @@ class CoverageBuilder(Builder):
full_name = '%s.%s' % (mod_name, name)
if inspect.isfunction(obj):
- if full_name not in self.env.descrefs:
+ if full_name not in objects:
for exp in self.fun_ignorexps:
if exp.match(name):
break
@@ -162,7 +166,7 @@ class CoverageBuilder(Builder):
if exp.match(name):
break
else:
- if full_name not in self.env.descrefs:
+ if full_name not in objects:
# not documented at all
classes[name] = []
continue
@@ -176,7 +180,7 @@ class CoverageBuilder(Builder):
continue
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)
if attrs:
diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py
index a271e101..236a7933 100644
--- a/sphinx/ext/inheritance_diagram.py
+++ b/sphinx/ext/inheritance_diagram.py
@@ -47,7 +47,6 @@ except ImportError:
from docutils import nodes
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.util.compat import Directive
@@ -280,10 +279,12 @@ class InheritanceDiagram(Directive):
node.document = self.state.document
env = self.state.document.settings.env
class_names = self.arguments[0].split()
+ class_role = env.get_domain('py').role('class')
# Create a graph starting with the list of classes
try:
- graph = InheritanceGraph(class_names, env.currmodule)
+ graph = InheritanceGraph(class_names,
+ env.doc_read_data.get('py_module'))
except InheritanceException, err:
return [node.document.reporter.warning(err.args[0],
line=self.lineno)]
@@ -293,7 +294,7 @@ class InheritanceDiagram(Directive):
# references to real URLs later. These nodes will eventually be
# removed from the doctree after we're done with them.
for name in graph.get_all_class_names():
- refnodes, x = xfileref_role(
+ refnodes, x = class_role(
'class', ':class:`%s`' % name, name, 0, self.state)
node.extend(refnodes)
# Store the graph object so we can use it to generate the
diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py
index e20cc266..a264e94f 100644
--- a/sphinx/ext/intersphinx.py
+++ b/sphinx/ext/intersphinx.py
@@ -3,17 +3,17 @@
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:
- * Each Sphinx HTML build creates a file named "objects.inv" that contains
- a mapping from Python identifiers to URIs relative to the HTML set's root.
+ * Each Sphinx HTML build creates a file named "objects.inv" that contains a
+ mapping from object names to URIs relative to the HTML set's root.
* Projects using the Intersphinx extension can specify links to such mapping
files in the `intersphinx_mapping` config value. The mapping will then be
- used to resolve otherwise missing references to Python objects into links
- to the other documentation.
+ used to resolve otherwise missing references to objects into links to the
+ other documentation.
* By default, the mapping file is assumed to be at the same location as the
rest of the documentation; however, the location of the mapping file can
@@ -25,6 +25,7 @@
"""
import time
+import zlib
import urllib2
import posixpath
from os import path
@@ -41,12 +42,68 @@ if hasattr(urllib2, 'HTTPSHandler'):
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):
"""Fetch, parse and return an intersphinx inventory file."""
- invdata = {}
# both *uri* (base URI of the links to generate) and *inv* (actual
# location of the inventory file) can be local or remote URIs
localuri = uri.find('://') == -1
+ join = localuri and path.join or posixpath.join
try:
if inv.find('://') != -1:
f = urllib2.urlopen(inv)
@@ -57,24 +114,21 @@ def fetch_inventory(app, uri, inv):
'%s: %s' % (inv, err.__class__, err))
return
try:
- line = f.next()
- if line.rstrip() != '# Sphinx inventory version 1':
- raise ValueError('unknown or unsupported inventory version')
- line = f.next()
- projname = line.rstrip()[11:].decode('utf-8')
- line = f.next()
- version = line.rstrip()[11:]
- for line in f:
- name, type, location = line.rstrip().split(None, 2)
- if localuri:
- location = path.join(uri, location)
+ line = f.readline().rstrip()
+ try:
+ if line == '# Sphinx inventory version 1':
+ invdata = read_inventory_v1(f, uri, join)
+ elif line == '# Sphinx inventory version 2':
+ invdata = read_inventory_v2(f, uri, join)
else:
- location = posixpath.join(uri, location)
- invdata[name] = (type, projname, version, location)
- f.close()
+ raise ValueError
+ f.close()
+ except ValueError:
+ f.close()
+ raise ValueError('unknown or unsupported inventory version')
except Exception, err:
app.warn('intersphinx inventory %r not readable due to '
- '%s: %s' % (inv, err.__class__, err))
+ '%s: %s' % (inv, err.__class__.__name__, err))
else:
return invdata
@@ -110,31 +164,24 @@ def load_mappings(app):
def missing_reference(app, env, node, contnode):
"""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']
- if type == 'mod':
- type, proj, version, uri = env.intersphinx_inventory.get(target,
- ('','','',''))
- if type != 'mod':
- return None
- target = 'module-' + target # for link anchor
+ objtypes = env.domains[domain].objtypes_for_role(node['reftype'])
+ if not objtypes:
+ return
+ for objtype in objtypes:
+ fulltype = '%s:%s' % (domain, objtype)
+ if fulltype in env.intersphinx_inventory and \
+ target in env.intersphinx_inventory[fulltype]:
+ break
else:
- if target[-2:] == '()':
- target = target[:-2]
- target = target.rstrip(' *')
- # special case: exceptions and object methods
- if type == 'exc' and '.' not in target and \
- 'exceptions.' + target in env.intersphinx_inventory:
- target = 'exceptions.' + target
- elif type in ('func', 'meth') and '.' not in target and \
- 'object.' + target in env.intersphinx_inventory:
- target = 'object.' + target
- if target not in env.intersphinx_inventory:
- return None
- type, proj, version, uri = env.intersphinx_inventory[target]
- # print "Intersphinx hit:", target, uri
+ return
+ proj, version, uri = env.intersphinx_inventory[fulltype][target]
newnode = nodes.reference('', '')
- newnode['refuri'] = uri + '#' + target
+ newnode['refuri'] = uri
newnode['reftitle'] = '(in %s v%s)' % (proj, version)
newnode['class'] = 'external-xref'
newnode.append(contnode)
diff --git a/sphinx/ext/refcounting.py b/sphinx/ext/refcounting.py
index 398520b8..6ab9fe4b 100644
--- a/sphinx/ext/refcounting.py
+++ b/sphinx/ext/refcounting.py
@@ -69,7 +69,7 @@ class Refcounts(dict):
def add_refcount_annotations(self, app, doctree):
for node in doctree.traverse(addnodes.desc_content):
par = node.parent
- if par['desctype'] != 'cfunction':
+ if par['domain'] != 'c' or par['objtype'] != 'function':
continue
if not par[0].has_key('names') or not par[0]['names']:
continue
diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py
index 5d23a7ff..565e4bfa 100644
--- a/sphinx/ext/todo.py
+++ b/sphinx/ext/todo.py
@@ -34,9 +34,7 @@ class Todo(Directive):
def run(self):
env = self.state.document.settings.env
-
- targetid = "todo-%s" % env.index_num
- env.index_num += 1
+ targetid = 'index-%s' % env.new_serialno('index')
targetnode = nodes.target('', '', ids=[targetid])
ad = make_admonition(todo_node, self.name, [_('Todo')], self.options,
diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py
index baeb7c33..9993f174 100644
--- a/sphinx/jinja2glue.py
+++ b/sphinx/jinja2glue.py
@@ -93,7 +93,7 @@ class BuiltinTemplateLoader(TemplateBridge, BaseLoader):
# make the paths into loaders
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 []
self.environment = SandboxedEnvironment(loader=self,
extensions=extensions)
@@ -101,7 +101,8 @@ class BuiltinTemplateLoader(TemplateBridge, BaseLoader):
self.environment.globals['debug'] = contextfunction(pformat)
self.environment.globals['accesskey'] = contextfunction(accesskey)
if use_i18n:
- self.environment.install_gettext_translations(builder.translator)
+ self.environment.install_gettext_translations(
+ builder.app.translator)
def render(self, template, context):
return self.environment.get_template(template).render(context)
diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py
index 4ad3f4c6..bfa23dbb 100644
--- a/sphinx/locale/__init__.py
+++ b/sphinx/locale/__init__.py
@@ -8,41 +8,179 @@
:copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
: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 = {
- 'attention': _('Attention'),
- 'caution': _('Caution'),
- 'danger': _('Danger'),
- 'error': _('Error'),
- 'hint': _('Hint'),
- 'important': _('Important'),
- 'note': _('Note'),
- 'seealso': _('See Also'),
- 'tip': _('Tip'),
- 'warning': _('Warning'),
+ 'attention': l_('Attention'),
+ 'caution': l_('Caution'),
+ 'danger': l_('Danger'),
+ 'error': l_('Error'),
+ 'hint': l_('Hint'),
+ 'important': l_('Important'),
+ 'note': l_('Note'),
+ 'seealso': l_('See Also'),
+ 'tip': l_('Tip'),
+ 'warning': l_('Warning'),
}
versionlabels = {
- 'versionadded': _('New in version %s'),
- 'versionchanged': _('Changed in version %s'),
- 'deprecated': _('Deprecated since version %s'),
+ 'versionadded': l_('New in version %s'),
+ 'versionchanged': l_('Changed in version %s'),
+ 'deprecated': l_('Deprecated since version %s'),
}
pairindextypes = {
- 'module': _('module'),
- 'keyword': _('keyword'),
- 'operator': _('operator'),
- 'object': _('object'),
- 'exception': _('exception'),
- 'statement': _('statement'),
- 'builtin': _('built-in function'),
+ 'module': l_('module'),
+ 'keyword': l_('keyword'),
+ 'operator': l_('operator'),
+ 'object': l_('object'),
+ 'exception': l_('exception'),
+ 'statement': l_('statement'),
+ 'builtin': l_('built-in function'),
}
-del _
-def init():
- for dct in (admonitionlabels, versionlabels, pairindextypes):
- for key in dct:
- dct[key] = _(dct[key])
+def init(locale_dirs, language):
+ # the None entry is the system's default locale path
+ translator = None
+ 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
diff --git a/sphinx/locale/ca/LC_MESSAGES/sphinx.js b/sphinx/locale/ca/LC_MESSAGES/sphinx.js
new file mode 100644
index 00000000..8ad043b0
--- /dev/null
+++ b/sphinx/locale/ca/LC_MESSAGES/sphinx.js
@@ -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"}}); \ No newline at end of file
diff --git a/sphinx/locale/ca/LC_MESSAGES/sphinx.mo b/sphinx/locale/ca/LC_MESSAGES/sphinx.mo
new file mode 100644
index 00000000..d9cc44ce
--- /dev/null
+++ b/sphinx/locale/ca/LC_MESSAGES/sphinx.mo
Binary files differ
diff --git a/sphinx/locale/ca/LC_MESSAGES/sphinx.po b/sphinx/locale/ca/LC_MESSAGES/sphinx.po
new file mode 100644
index 00000000..440b0594
--- /dev/null
+++ b/sphinx/locale/ca/LC_MESSAGES/sphinx.po
@@ -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]"
+
diff --git a/sphinx/locale/sphinx.pot b/sphinx/locale/sphinx.pot
index d6337cf7..64bc69dd 100644
--- a/sphinx/locale/sphinx.pot
+++ b/sphinx/locale/sphinx.pot
@@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: Sphinx 1.0\n"
+"Project-Id-Version: Sphinx 1.0pre/[?1034h2e1ab15e035e\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"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,12 +17,12 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\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
msgid "%B %d, %Y"
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:5
#: sphinx/themes/basic/genindex.html:2 sphinx/themes/basic/genindex.html:5
@@ -31,60 +31,56 @@ msgstr ""
msgid "Index"
msgstr ""
-#: sphinx/environment.py:325 sphinx/writers/latex.py:189
+#: sphinx/environment.py:349 sphinx/writers/latex.py:189
msgid "Module Index"
msgstr ""
-#: sphinx/environment.py:326 sphinx/themes/basic/defindex.html:16
+#: sphinx/environment.py:350 sphinx/themes/basic/defindex.html:16
msgid "Search Page"
msgstr ""
-#: sphinx/roles.py:55 sphinx/directives/desc.py:747
-#, python-format
-msgid "environment variable; %s"
-msgstr ""
-
-#: sphinx/roles.py:62
+#: sphinx/roles.py:167
#, python-format
msgid "Python Enhancement Proposals!PEP %s"
msgstr ""
-#: sphinx/builders/changes.py:71
+#: sphinx/builders/changes.py:70
msgid "Builtins"
msgstr ""
-#: sphinx/builders/changes.py:73
+#: sphinx/builders/changes.py:72
msgid "Module level"
msgstr ""
-#: sphinx/builders/html.py:222
+#: sphinx/builders/html.py:224
#, python-format
msgid "%b %d, %Y"
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"
msgstr ""
-#: sphinx/builders/html.py:241
+#: sphinx/builders/html.py:243
msgid "index"
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/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"
msgstr ""
-#: sphinx/builders/html.py:244
+#: sphinx/builders/html.py:248
msgid "modules"
msgstr ""
-#: sphinx/builders/html.py:300
+#: sphinx/builders/html.py:304
msgid "next"
msgstr ""
-#: sphinx/builders/html.py:309
+#: sphinx/builders/html.py:313
msgid "previous"
msgstr ""
@@ -92,249 +88,323 @@ msgstr ""
msgid " (in "
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"
msgstr ""
-#: sphinx/directives/desc.py:101
+#: sphinx/directives/__init__.py:82 sphinx/directives/__init__.py:83
+#: sphinx/directives/__init__.py:84
msgid "Variable"
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"
msgstr ""
-#: sphinx/directives/desc.py:113
+#: sphinx/directives/__init__.py:94
msgid "Return type"
msgstr ""
-#: sphinx/directives/desc.py:186
+#: sphinx/directives/__init__.py:169
msgid "Parameter"
msgstr ""
-#: sphinx/directives/desc.py:190
+#: sphinx/directives/__init__.py:173
msgid "Parameters"
msgstr ""
-#: sphinx/directives/desc.py:418
+#: sphinx/directives/other.py:127
+msgid "Section author: "
+msgstr ""
+
+#: sphinx/directives/other.py:129
+msgid "Module author: "
+msgstr ""
+
+#: sphinx/directives/other.py:131
+msgid "Author: "
+msgstr ""
+
+#: sphinx/directives/other.py:233
+msgid "See also"
+msgstr ""
+
+#: sphinx/domains/c.py:124
+#, python-format
+msgid "%s (C function)"
+msgstr ""
+
+#: sphinx/domains/c.py:126
+#, python-format
+msgid "%s (C member)"
+msgstr ""
+
+#: sphinx/domains/c.py:128
+#, python-format
+msgid "%s (C macro)"
+msgstr ""
+
+#: sphinx/domains/c.py:130
+#, python-format
+msgid "%s (C type)"
+msgstr ""
+
+#: sphinx/domains/c.py:132
+#, python-format
+msgid "%s (C variable)"
+msgstr ""
+
+#: sphinx/domains/c.py:162
+msgid "C function"
+msgstr ""
+
+#: 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/directives/desc.py:419 sphinx/directives/desc.py:476
-#: sphinx/directives/desc.py:488
+#: 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/directives/desc.py:422
+#: sphinx/domains/python.py:190
#, python-format
msgid "%s (built-in variable)"
msgstr ""
-#: sphinx/directives/desc.py:423 sphinx/directives/desc.py:514
+#: sphinx/domains/python.py:191 sphinx/domains/python.py:282
#, python-format
msgid "%s (in module %s)"
msgstr ""
-#: sphinx/directives/desc.py:439
+#: sphinx/domains/python.py:207
#, python-format
msgid "%s (built-in class)"
msgstr ""
-#: sphinx/directives/desc.py:440
+#: sphinx/domains/python.py:208
#, python-format
msgid "%s (class in %s)"
msgstr ""
-#: sphinx/directives/desc.py:480
+#: sphinx/domains/python.py:248
#, python-format
msgid "%s() (%s.%s method)"
msgstr ""
-#: sphinx/directives/desc.py:482
+#: sphinx/domains/python.py:250
#, python-format
msgid "%s() (%s method)"
msgstr ""
-#: sphinx/directives/desc.py:492
+#: sphinx/domains/python.py:260
#, python-format
msgid "%s() (%s.%s static method)"
msgstr ""
-#: sphinx/directives/desc.py:495
+#: sphinx/domains/python.py:263
#, python-format
msgid "%s() (%s static method)"
msgstr ""
-#: sphinx/directives/desc.py:518
+#: sphinx/domains/python.py:273
#, python-format
-msgid "%s (%s.%s attribute)"
+msgid "%s() (%s.%s class method)"
msgstr ""
-#: sphinx/directives/desc.py:520
+#: sphinx/domains/python.py:276
#, python-format
-msgid "%s (%s attribute)"
+msgid "%s() (%s class method)"
msgstr ""
-#: sphinx/directives/desc.py:609
+#: sphinx/domains/python.py:286
#, python-format
-msgid "%s (C function)"
+msgid "%s (%s.%s attribute)"
msgstr ""
-#: sphinx/directives/desc.py:611
+#: sphinx/domains/python.py:288
#, python-format
-msgid "%s (C member)"
+msgid "%s (%s attribute)"
msgstr ""
-#: sphinx/directives/desc.py:613
-#, python-format
-msgid "%s (C macro)"
+#: sphinx/domains/python.py:334
+msgid "Platforms: "
msgstr ""
-#: sphinx/directives/desc.py:615
+#: sphinx/domains/python.py:340
#, python-format
-msgid "%s (C type)"
+msgid "%s (module)"
msgstr ""
-#: sphinx/directives/desc.py:617
-#, python-format
-msgid "%s (C variable)"
+#: sphinx/domains/python.py:396
+msgid "function"
msgstr ""
-#: sphinx/directives/desc.py:665
-#, python-format
-msgid "%scommand line option; %s"
+#: sphinx/domains/python.py:397
+msgid "data"
msgstr ""
-#: sphinx/directives/other.py:140
-msgid "Platforms: "
+#: sphinx/domains/python.py:398
+msgid "class"
msgstr ""
-#: sphinx/directives/other.py:146
+#: sphinx/domains/python.py:399 sphinx/locale/__init__.py:161
+msgid "exception"
+msgstr ""
+
+#: 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 "%s (module)"
+msgid "environment variable; %s"
msgstr ""
-#: sphinx/directives/other.py:195
-msgid "Section author: "
+#: sphinx/domains/std.py:156
+#, python-format
+msgid "%scommand line option; %s"
msgstr ""
-#: sphinx/directives/other.py:197
-msgid "Module author: "
+#: sphinx/domains/std.py:324
+msgid "glossary term"
msgstr ""
-#: sphinx/directives/other.py:199
-msgid "Author: "
+#: sphinx/domains/std.py:325
+msgid "grammar token"
msgstr ""
-#: sphinx/directives/other.py:319
-msgid "See also"
+#: sphinx/domains/std.py:326
+msgid "environment variable"
+msgstr ""
+
+#: sphinx/domains/std.py:327
+msgid "program option"
msgstr ""
-#: sphinx/ext/autodoc.py:889
+#: sphinx/ext/autodoc.py:892
#, python-format
msgid " Bases: %s"
msgstr ""
-#: sphinx/ext/autodoc.py:922
+#: sphinx/ext/autodoc.py:925
#, python-format
msgid "alias of :class:`%s`"
msgstr ""
-#: sphinx/ext/todo.py:41
+#: sphinx/ext/todo.py:40
msgid "Todo"
msgstr ""
-#: sphinx/ext/todo.py:99
+#: sphinx/ext/todo.py:98
#, python-format
msgid "(The original entry is located in %s, line %d and can be found "
msgstr ""
-#: sphinx/ext/todo.py:105
+#: sphinx/ext/todo.py:104
msgid "here"
msgstr ""
-#: sphinx/locale/__init__.py:15
+#: sphinx/locale/__init__.py:138
msgid "Attention"
msgstr ""
-#: sphinx/locale/__init__.py:16
+#: sphinx/locale/__init__.py:139
msgid "Caution"
msgstr ""
-#: sphinx/locale/__init__.py:17
+#: sphinx/locale/__init__.py:140
msgid "Danger"
msgstr ""
-#: sphinx/locale/__init__.py:18
+#: sphinx/locale/__init__.py:141
msgid "Error"
msgstr ""
-#: sphinx/locale/__init__.py:19
+#: sphinx/locale/__init__.py:142
msgid "Hint"
msgstr ""
-#: sphinx/locale/__init__.py:20
+#: sphinx/locale/__init__.py:143
msgid "Important"
msgstr ""
-#: sphinx/locale/__init__.py:21
+#: sphinx/locale/__init__.py:144
msgid "Note"
msgstr ""
-#: sphinx/locale/__init__.py:22
+#: sphinx/locale/__init__.py:145
msgid "See Also"
msgstr ""
-#: sphinx/locale/__init__.py:23
+#: sphinx/locale/__init__.py:146
msgid "Tip"
msgstr ""
-#: sphinx/locale/__init__.py:24
+#: sphinx/locale/__init__.py:147
msgid "Warning"
msgstr ""
-#: sphinx/locale/__init__.py:28
+#: sphinx/locale/__init__.py:151
#, python-format
msgid "New in version %s"
msgstr ""
-#: sphinx/locale/__init__.py:29
+#: sphinx/locale/__init__.py:152
#, python-format
msgid "Changed in version %s"
msgstr ""
-#: sphinx/locale/__init__.py:30
+#: sphinx/locale/__init__.py:153
#, python-format
msgid "Deprecated since version %s"
msgstr ""
-#: sphinx/locale/__init__.py:34
-msgid "module"
-msgstr ""
-
-#: sphinx/locale/__init__.py:35
+#: sphinx/locale/__init__.py:158
msgid "keyword"
msgstr ""
-#: sphinx/locale/__init__.py:36
+#: sphinx/locale/__init__.py:159
msgid "operator"
msgstr ""
-#: sphinx/locale/__init__.py:37
+#: sphinx/locale/__init__.py:160
msgid "object"
msgstr ""
-#: sphinx/locale/__init__.py:38
-msgid "exception"
-msgstr ""
-
-#: sphinx/locale/__init__.py:39
+#: sphinx/locale/__init__.py:162
msgid "statement"
msgstr ""
-#: sphinx/locale/__init__.py:40
+#: sphinx/locale/__init__.py:163
msgid "built-in function"
msgstr ""
@@ -448,29 +518,29 @@ msgstr ""
msgid "Copyright"
msgstr ""
-#: sphinx/themes/basic/layout.html:187
+#: sphinx/themes/basic/layout.html:187 sphinx/themes/scrolls/layout.html:83
#, python-format
msgid "&copy; <a href=\"%(path)s\">Copyright</a> %(copyright)s."
msgstr ""
-#: sphinx/themes/basic/layout.html:189
+#: sphinx/themes/basic/layout.html:189 sphinx/themes/scrolls/layout.html:85
#, python-format
msgid "&copy; Copyright %(copyright)s."
msgstr ""
-#: sphinx/themes/basic/layout.html:193
+#: sphinx/themes/basic/layout.html:193 sphinx/themes/scrolls/layout.html:89
#, python-format
msgid "Last updated on %(last_updated)s."
msgstr ""
-#: sphinx/themes/basic/layout.html:196
+#: sphinx/themes/basic/layout.html:196 sphinx/themes/scrolls/layout.html:92
#, python-format
msgid ""
"Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> "
"%(sphinx_version)s."
msgstr ""
-#: sphinx/themes/basic/modindex.html:36
+#: sphinx/themes/basic/modindex.html:36 sphinx/themes/scrolls/modindex.html:37
msgid "Deprecated"
msgstr ""
@@ -498,7 +568,7 @@ msgid "search"
msgstr ""
#: sphinx/themes/basic/search.html:25
-#: sphinx/themes/basic/static/searchtools.js:462
+#: sphinx/themes/basic/static/searchtools.js:473
msgid "Search Results"
msgstr ""
@@ -534,16 +604,16 @@ msgstr ""
msgid "Other changes"
msgstr ""
-#: sphinx/themes/basic/static/doctools.js:139 sphinx/writers/html.py:473
-#: sphinx/writers/html.py:478
+#: sphinx/themes/basic/static/doctools.js:138 sphinx/writers/html.py:462
+#: sphinx/writers/html.py:467
msgid "Permalink to this headline"
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"
msgstr ""
-#: sphinx/themes/basic/static/doctools.js:174
+#: sphinx/themes/basic/static/doctools.js:173
msgid "Hide Search Matches"
msgstr ""
@@ -555,21 +625,17 @@ msgstr ""
msgid "Preparing search..."
msgstr ""
-#: sphinx/themes/basic/static/searchtools.js:347
-msgid "module, in "
-msgstr ""
-
-#: sphinx/themes/basic/static/searchtools.js:356
+#: sphinx/themes/basic/static/searchtools.js:352
msgid ", in "
msgstr ""
-#: sphinx/themes/basic/static/searchtools.js:464
+#: sphinx/themes/basic/static/searchtools.js:475
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 ""
-#: sphinx/themes/basic/static/searchtools.js:466
+#: sphinx/themes/basic/static/searchtools.js:477
#, python-format
msgid "Search finished, found %s page(s) matching the search query."
msgstr ""
@@ -578,15 +644,15 @@ msgstr ""
msgid "Release"
msgstr ""
-#: sphinx/writers/latex.py:578
+#: sphinx/writers/latex.py:579
msgid "Footnotes"
msgstr ""
-#: sphinx/writers/latex.py:646
+#: sphinx/writers/latex.py:647
msgid "continued from previous page"
msgstr ""
-#: sphinx/writers/latex.py:651
+#: sphinx/writers/latex.py:652
msgid "Continued on next page"
msgstr ""
diff --git a/sphinx/roles.py b/sphinx/roles.py
index d3613c7a..9a50d736 100644
--- a/sphinx/roles.py
+++ b/sphinx/roles.py
@@ -10,6 +10,7 @@
"""
import re
+import warnings
from docutils import nodes, utils
from docutils.parsers.rst import roles
@@ -28,7 +29,7 @@ generic_docroles = {
'manpage' : addnodes.literal_emphasis,
'mimetype' : addnodes.literal_emphasis,
'newsgroup' : addnodes.literal_emphasis,
- 'program' : nodes.strong,
+ 'program' : nodes.strong, # XXX should be an x-ref
'regexp' : nodes.literal,
}
@@ -37,27 +38,131 @@ for rolename, nodeclass in generic_docroles.iteritems():
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,
options={}, content=[]):
+ """Role for PEP/RFC references that generate an index entry."""
env = inliner.document.settings.env
if not typ:
typ = env.config.default_role
else:
typ = typ.lower()
text = utils.unescape(etext)
- targetid = 'index-%s' % env.index_num
- env.index_num += 1
+ targetid = 'index-%s' % env.new_serialno('index')
indexnode = addnodes.index()
targetnode = nodes.target('', '', ids=[targetid])
inliner.document.note_explicit_target(targetnode)
- if typ == 'envvar':
- 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':
+ if typ == 'pep':
indexnode['entries'] = [
('single', _('Python Enhancement Proposals!PEP %s') % text,
targetid, 'PEP %s' % text)]
@@ -89,117 +194,12 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
rn += sn
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=[]):
return [nodes.emphasis(
rawtext,
utils.unescape(text).replace('-->', u'\N{TRIANGULAR BULLET}'))], []
+ return role
_litvar_re = re.compile('{([^}]+)}')
@@ -233,30 +233,17 @@ def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
specific_docroles = {
- 'data': xfileref_role,
- 'exc': xfileref_role,
- 'func': xfileref_role,
- 'class': xfileref_role,
- 'const': xfileref_role,
- 'attr': xfileref_role,
- 'meth': xfileref_role,
- 'obj': xfileref_role,
- '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,
+ # links to download references
+ 'download': XRefRole(nodeclass=addnodes.download_reference),
+ # links to headings or arbitrary labels
+ 'ref': XRefRole(lowercase=True, innernodeclass=nodes.emphasis),
+ # links to documents
+ 'doc': XRefRole(),
+ # links to labels, without a different title
+ 'keyword': XRefRole(),
+ 'pep': indexmarkup_role,
+ 'rfc': indexmarkup_role,
'menuselection': menusel_role,
'file': emph_literal_role,
'samp': emph_literal_role,
@@ -265,3 +252,10 @@ specific_docroles = {
for rolename, func in specific_docroles.iteritems():
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)
diff --git a/sphinx/search.py b/sphinx/search.py
index 7161bc4c..592f4cc6 100644
--- a/sphinx/search.py
+++ b/sphinx/search.py
@@ -119,8 +119,10 @@ class IndexBuilder(object):
self._titles = {}
# stemmed word -> set(filenames)
self._mapping = {}
- # desctypes -> index
- self._desctypes = {}
+ # objtype -> index
+ self._objtypes = {}
+ # objtype index -> objname (localized)
+ self._objnames = {}
def load(self, stream, format):
"""Reconstruct from frozen data."""
@@ -138,7 +140,7 @@ class IndexBuilder(object):
self._mapping[k] = set([index2fn[v]])
else:
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):
"""Dump the frozen index to a stream."""
@@ -146,27 +148,30 @@ class IndexBuilder(object):
format = self.formats[format]
format.dump(self.freeze(), stream)
- def get_modules(self, fn2index):
+ def get_objects(self, fn2index):
rv = {}
- for name, (doc, _, _, _) in self.env.modules.iteritems():
- if doc in fn2index:
- rv[name] = fn2index[doc]
- return rv
-
- def get_descrefs(self, fn2index):
- rv = {}
- dt = self._desctypes
- for fullname, (doc, desctype) in self.env.descrefs.iteritems():
- if doc not in fn2index:
- continue
- prefix, name = rpartition(fullname, '.')
- pdict = rv.setdefault(prefix, {})
- try:
- i = dt[desctype]
- except KeyError:
- i = len(dt)
- dt[desctype] = i
- pdict[name] = (fn2index[doc], i)
+ ot = self._objtypes
+ on = self._objnames
+ for domainname, domain in self.env.domains.iteritems():
+ for fullname, type, docname, anchor, prio in domain.get_objects():
+ if docname not in fn2index:
+ continue
+ if prio < 0:
+ continue
+ # XXX splitting at dot is kind of Python specific
+ prefix, name = rpartition(fullname, '.')
+ pdict = rv.setdefault(prefix, {})
+ try:
+ i = ot[domainname, type]
+ except KeyError:
+ i = len(ot)
+ ot[domainname, type] = i
+ otype = domain.object_types.get(type)
+ if otype:
+ on[i] = str(otype.lname) # fire translation proxies
+ else:
+ on[i] = type
+ pdict[name] = (fn2index[docname], i, prio)
return rv
def get_terms(self, fn2index):
@@ -185,14 +190,13 @@ class IndexBuilder(object):
filenames = self._titles.keys()
titles = self._titles.values()
fn2index = dict((f, i) for (i, f) in enumerate(filenames))
- return dict(
- filenames=filenames,
- titles=titles,
- terms=self.get_terms(fn2index),
- descrefs=self.get_descrefs(fn2index),
- modules=self.get_modules(fn2index),
- desctypes=dict((v, k) for (k, v) in self._desctypes.items()),
- )
+ terms = self.get_terms(fn2index)
+ objects = self.get_objects(fn2index) # populates _objtypes
+ objtypes = dict((v, k[0] + ':' + k[1])
+ for (k, v) in self._objtypes.iteritems())
+ objnames = self._objnames
+ return dict(filenames=filenames, titles=titles, terms=terms,
+ objects=objects, objtypes=objtypes, objnames=objnames)
def prune(self, filenames):
"""Remove data for all filenames not in the list."""
diff --git a/sphinx/themes/basic/static/doctools.js b/sphinx/themes/basic/static/doctools.js
index 9447678c..12afe01b 100644
--- a/sphinx/themes/basic/static/doctools.js
+++ b/sphinx/themes/basic/static/doctools.js
@@ -1,12 +1,11 @@
-/// XXX: make it cross browser
-
/**
* make the code below compatible with browsers without
* an installed firebug like debugger
*/
if (!window.console || !console.firebug) {
- var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
- "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
+ var names = ["log", "debug", "info", "warn", "error", "assert", "dir",
+ "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace",
+ "profile", "profileEnd"];
window.console = {};
for (var i = 0; i < names.length; ++i)
window.console[names[i]] = function() {}
diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js
index 36acb12e..2dbda10e 100644
--- a/sphinx/themes/basic/static/searchtools.js
+++ b/sphinx/themes/basic/static/searchtools.js
@@ -330,31 +330,33 @@ var Search = {
var filenames = this._index.filenames;
var titles = this._index.titles;
var terms = this._index.terms;
- var descrefs = this._index.descrefs;
- var modules = this._index.modules;
- var desctypes = this._index.desctypes;
+ var objects = this._index.objects;
+ var objtypes = this._index.objtypes;
+ var objnames = this._index.objnames;
var fileMap = {};
var files = null;
+ // different result priorities
+ var importantResults = [];
var objectResults = [];
var regularResults = [];
+ var unimportantResults = [];
$('#search-progress').empty();
// lookup as object
if (object != null) {
- for (var module in modules) {
- if (module.indexOf(object) > -1) {
- 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]) {
+ for (var prefix in objects) {
+ for (var name in objects[prefix]) {
var fullname = (prefix ? prefix + '.' : '') + name;
if (fullname.toLowerCase().indexOf(object) > -1) {
- match = descrefs[prefix][name];
- descr = desctypes[match[1]] + _(', in ') + titles[match[0]];
- objectResults.push([filenames[match[0]], fullname, '#'+fullname, descr]);
+ match = objects[prefix][name];
+ descr = objnames[match[1]] + _(', in ') + titles[match[0]];
+ // 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);
});
+ 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
for (var i = 0; i < searchterms.length; i++) {
@@ -420,8 +430,9 @@ var Search = {
return (left > right) ? -1 : ((left < right) ? 1 : 0);
});
- // combine both
- var results = regularResults.concat(objectResults);
+ // combine all results
+ var results = unimportantResults.concat(regularResults)
+ .concat(objectResults).concat(importantResults);
// print the results
var resultCount = results.length;
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py
index b719b391..3ceafa5e 100644
--- a/sphinx/util/__init__.py
+++ b/sphinx/util/__init__.py
@@ -24,6 +24,8 @@ import traceback
from os import path
import docutils
+from docutils import nodes
+
import sphinx
# Errnos that we need.
@@ -459,6 +461,20 @@ def split_explicit_title(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
# traverse() is called so many times during a build that it saves
# on average 20-25% overall build time!
@@ -488,8 +504,7 @@ def _new_traverse(self, condition=None,
return self._old_traverse(condition, include_self,
descend, siblings, ascend)
-import docutils.nodes
-docutils.nodes.Node._old_traverse = docutils.nodes.Node.traverse
-docutils.nodes.Node._all_traverse = _all_traverse
-docutils.nodes.Node._fast_traverse = _fast_traverse
-docutils.nodes.Node.traverse = _new_traverse
+nodes.Node._old_traverse = nodes.Node.traverse
+nodes.Node._all_traverse = _all_traverse
+nodes.Node._fast_traverse = _fast_traverse
+nodes.Node.traverse = _new_traverse
diff --git a/sphinx/util/compat.py b/sphinx/util/compat.py
index 885a458e..3fbfe4b2 100644
--- a/sphinx/util/compat.py
+++ b/sphinx/util/compat.py
@@ -11,7 +11,7 @@
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,
content_offset, block_text, state, state_machine):
#if not content:
@@ -35,64 +35,13 @@ def make_admonition(node_class, name, arguments, options, content, lineno,
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:
- class Directive(object):
- """
- Fake Directive class to allow Sphinx directives to be written in
- class style.
- """
- 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
+def directive_dwim(obj):
+ import warnings
+ warnings.warn('directive_dwim is deprecated and no longer needed',
+ DeprecationWarning, stacklevel=2)
+ return obj
diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py
index c99fc28e..d0ff86c9 100644
--- a/sphinx/writers/html.py
+++ b/sphinx/writers/html.py
@@ -61,7 +61,7 @@ class HTMLTranslator(BaseTranslator):
self.add_permalinks = builder.config.html_add_permalinks
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):
self.body.append('</dl>\n\n')
@@ -69,7 +69,7 @@ class HTMLTranslator(BaseTranslator):
# the id is set automatically
self.body.append(self.starttag(node, 'dt'))
# anchor for per-desc interactive data
- if node.parent['desctype'] != 'describe' \
+ if node.parent['objtype'] != 'describe' \
and node['ids'] and node['first']:
self.body.append('<!--[%s]-->' % node['ids'][0])
def depart_desc_signature(self, node):
@@ -189,21 +189,10 @@ class HTMLTranslator(BaseTranslator):
numbers = self.builder.secnumbers[anchorname]
self.body.append('.'.join(map(str, numbers)) + '. ')
- # overwritten for docutils 0.4
- if hasattr(BaseTranslator, 'start_tag_with_title'):
- def visit_section(self, node):
- # the 0.5 version, to get the id attribute in the <div> tag
- 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
+ def visit_title(self, node):
+ BaseTranslator.visit_title(self, node)
+ self.add_secnumber(node)
# overwritten
def visit_literal_block(self, node):
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index a0f8f55e..63e498df 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -123,7 +123,7 @@ class Table(object):
class Desc(object):
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.annotation = self.returns = ''
self.count = 0
@@ -428,6 +428,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def depart_subtitle(self, node):
self.body.append(self.context.pop())
+ # XXX update this
desc_map = {
'function' : 'funcdesc',
'class': 'classdesc',
@@ -462,7 +463,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def depart_desc_signature(self, node):
d = self.descstack[-1]
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]
else:
hyper = ''
diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py
index 84dc4b38..61a412bc 100644
--- a/sphinx/writers/text.py
+++ b/sphinx/writers/text.py
@@ -174,8 +174,8 @@ class TextTranslator(nodes.NodeVisitor):
def visit_desc_signature(self, node):
self.new_state(0)
- if node.parent['desctype'] in ('class', 'exception'):
- self.add_text('%s ' % node.parent['desctype'])
+ if node.parent['objtype'] in ('class', 'exception'):
+ self.add_text('%s ' % node.parent['objtype'])
def depart_desc_signature(self, node):
# XXX: wrap signatures in a way that makes sense
self.end_state(wrap=False, end=None)