diff options
| author | Georg Brandl <georg@python.org> | 2010-01-07 19:07:50 +0100 |
|---|---|---|
| committer | Georg Brandl <georg@python.org> | 2010-01-07 19:07:50 +0100 |
| commit | f35d74d9cfa6c934b35af1e7e71efcbc63a3e3a5 (patch) | |
| tree | deb3ff63159b82b5d966600bf8c832cf8864f148 /sphinx | |
| parent | a5008da8ee1b8ada041851b390818714a67f0af2 (diff) | |
| parent | 0afca45dbc97654e489df0367bf92d9b4803cf78 (diff) | |
| download | sphinx-f35d74d9cfa6c934b35af1e7e71efcbc63a3e3a5.tar.gz | |
merge with trunk
Diffstat (limited to 'sphinx')
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 Binary files differnew file mode 100644 index 00000000..d9cc44ce --- /dev/null +++ b/sphinx/locale/ca/LC_MESSAGES/sphinx.mo 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 – %(key)s" +msgstr "Índes – %(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 "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." +msgstr "© <a href=\\\"%(path)s\\\">Copyright</a> %(copyright)s." + +#: sphinx/themes/basic/layout.html:186 +#, python-format +msgid "© Copyright %(copyright)s." +msgstr "© 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 — %(docstitle)s" +msgstr "Canvis a la Versió %(version)s — %(docstitle)s" + +#: sphinx/themes/basic/changes/rstsource.html:5 +#, python-format +msgid "%(filename)s — %(docstitle)s" +msgstr "%(filename)s — %(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 "© <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 "© 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) |
