mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
More refactoring for language-independent domain support.
* Renamed "desc"ription unit to "object" wherever possible. * Added standard x-ref types to a StandardDomain which is always consulted. * Split domains module into a subpackage. * Removed additional_xref_types in favor of new directive classes in StandardDomain. * Implemented x-ref inventory version 2, for all object types. * Added env.doc_read_data which is for temporary data stored while reading. * Minimally updated extension tutorial. * Started to implement changes to interactive search. * Test suite passes again.
This commit is contained in:
@@ -201,8 +201,7 @@ The ``todo`` directive function looks like this::
|
||||
def run(self):
|
||||
env = self.state.document.settings.env
|
||||
|
||||
targetid = "todo-%s" % env.index_num
|
||||
env.index_num += 1
|
||||
targetid = "todo-%s" % env.new_serialno('todo')
|
||||
targetnode = nodes.target('', '', ids=[targetid])
|
||||
|
||||
ad = make_admonition(todo, self.name, [_('Todo')], self.options,
|
||||
@@ -225,9 +224,10 @@ to the build environment instance using ``self.state.document.settings.env``.
|
||||
|
||||
Then, to act as a link target (from the todolist), the todo directive needs to
|
||||
return a target node in addition to the todo node. The target ID (in HTML, this
|
||||
will be the anchor name) is generated by using ``env.index_num`` which is
|
||||
persistent between directive calls and therefore leads to unique target names.
|
||||
The target node is instantiated without any text (the first two arguments).
|
||||
will be the anchor name) is generated by using ``env.new_serialno`` which is
|
||||
returns a new integer directive on each call and therefore leads to unique
|
||||
target names. The target node is instantiated without any text (the first two
|
||||
arguments).
|
||||
|
||||
An admonition is created using a standard docutils function (wrapped in Sphinx
|
||||
for docutils cross-version compatibility). The first argument gives the node
|
||||
|
@@ -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 description units (class, function etc.)
|
||||
|
||||
# parent node for signature and content
|
||||
class desc(nodes.Admonition, nodes.Element): pass
|
||||
|
@@ -25,9 +25,9 @@ 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 all_domains
|
||||
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 BuildEnvironment, SphinxStandaloneReader
|
||||
from sphinx.util import pycompat # imported for side-effects
|
||||
from sphinx.util.tags import Tags
|
||||
@@ -361,6 +361,7 @@ class Sphinx(object):
|
||||
|
||||
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
|
||||
@@ -377,23 +378,31 @@ class Sphinx(object):
|
||||
raise ExtensionError('domain %s not yet registered' % domain)
|
||||
all_domains[domain].roles[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))
|
||||
def add_object_type(self, directivename, rolename, indextemplate='',
|
||||
parse_node=None, ref_nodeclass=None):
|
||||
StandardDomain.object_types[directivename] = \
|
||||
ObjType(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?
|
||||
role_func = XRefRole(innernodeclass=ref_nodeclass)
|
||||
roles.register_local_role(rolename, role_func)
|
||||
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))
|
||||
# XXX support more options
|
||||
role_func = XRefRole(innernodeclass=ref_nodeclass)
|
||||
roles.register_local_role(rolename, role_func)
|
||||
StandardDomain.object_types[directivename] = \
|
||||
ObjType(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)
|
||||
|
@@ -713,15 +713,13 @@ class StandaloneHTMLBuilder(Builder):
|
||||
self.info(bold('dumping object inventory... '), nonl=True)
|
||||
f = open(path.join(self.outdir, INVENTORY_FILENAME), 'w')
|
||||
try:
|
||||
# XXX inventory version 2
|
||||
f.write('# Sphinx inventory version 1\n')
|
||||
f.write('# Sphinx inventory version 2\n')
|
||||
f.write('# Project: %s\n' % self.config.project.encode('utf-8'))
|
||||
f.write('# 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)))
|
||||
for domain in self.env.domains.itervalues():
|
||||
for name, type, docname, anchor, prio in domain.get_objects():
|
||||
f.write('%s %s:%s %s %s\n' % (name, domain, type, prio,
|
||||
self.get_target_uri(docname) + '#' + anchor))
|
||||
finally:
|
||||
f.close()
|
||||
self.info('done')
|
||||
|
@@ -15,7 +15,6 @@ from docutils.parsers.rst import directives
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.locale import l_
|
||||
from sphinx.util import ws_re
|
||||
from sphinx.util.compat import Directive, directive_dwim
|
||||
|
||||
|
||||
@@ -32,15 +31,11 @@ def _is_only_paragraph(node):
|
||||
return False
|
||||
|
||||
|
||||
# RE for option descriptions
|
||||
option_desc_re = re.compile(
|
||||
r'((?:/|-|--)[-_a-zA-Z0-9]+)(\s*.*?)(?=,\s+(?:/|-|--)|$)')
|
||||
|
||||
# RE to strip backslash escapes
|
||||
strip_backslash_re = re.compile(r'\\(?=[^\\])')
|
||||
|
||||
|
||||
class DescDirective(Directive):
|
||||
class ObjectDescription(Directive):
|
||||
"""
|
||||
Directive to describe a class, function or similar object. Not used
|
||||
directly, but subclassed to add custom behavior.
|
||||
@@ -133,10 +128,12 @@ class DescDirective(Directive):
|
||||
nfield += nfieldname
|
||||
node = nfieldname
|
||||
if typ in self.doc_fields_with_linked_arg:
|
||||
# XXX currmodule/currclass
|
||||
node = addnodes.pending_xref(
|
||||
obj, reftype='obj', refcaption=False,
|
||||
reftarget=obj, modname=self.env.currmodule,
|
||||
classname=self.env.currclass)
|
||||
reftarget=obj)
|
||||
#, modname=self.env.currmodule
|
||||
#, classname=self.env.currclass
|
||||
nfieldname += node
|
||||
node += nodes.Text(obj, obj)
|
||||
nfield += nodes.field_body()
|
||||
@@ -205,15 +202,17 @@ class DescDirective(Directive):
|
||||
|
||||
def run(self):
|
||||
if ':' in self.name:
|
||||
self.domain, self.desctype = self.name.split(':', 1)
|
||||
self.domain, self.objtype = self.name.split(':', 1)
|
||||
else:
|
||||
self.domain, self.desctype = '', self.name
|
||||
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['desctype'] = self.desctype
|
||||
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 = []
|
||||
@@ -242,117 +241,16 @@ class DescDirective(Directive):
|
||||
node.append(contentnode)
|
||||
if self.names:
|
||||
# needed for association of version{added,changed} directives
|
||||
self.env.currdesc = self.names[0]
|
||||
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.currdesc = None
|
||||
self.env.doc_read_data['object'] = None
|
||||
self.after_content()
|
||||
return [self.indexnode, node]
|
||||
|
||||
|
||||
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 XRefRole 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 XRefRole 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
|
||||
# backwards compatible old name
|
||||
DescDirective = ObjectDescription
|
||||
|
||||
|
||||
class DefaultDomain(Directive):
|
||||
@@ -372,18 +270,7 @@ class DefaultDomain(Directive):
|
||||
env.default_domain = env.domains.get(domain_name)
|
||||
|
||||
|
||||
# Note: the target directive is not registered here, it is used by the
|
||||
# application when registering additional xref types.
|
||||
|
||||
# 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', l_('environment variable; %s'), None),
|
||||
}
|
||||
|
||||
|
||||
directives.register_directive('default-domain', directive_dwim(DefaultDomain))
|
||||
directives.register_directive('describe', directive_dwim(DescDirective))
|
||||
directives.register_directive('cmdoption', directive_dwim(CmdoptionDesc))
|
||||
directives.register_directive('envvar', directive_dwim(GenericDesc))
|
||||
directives.register_directive('describe', directive_dwim(ObjectDescription))
|
||||
# new, more consistent, name
|
||||
directives.register_directive('object', directive_dwim(ObjectDescription))
|
||||
|
@@ -136,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.
|
||||
@@ -175,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()
|
||||
@@ -231,6 +209,7 @@ class VersionChange(Directive):
|
||||
else:
|
||||
ret = [node]
|
||||
env = self.state.document.settings.env
|
||||
# XXX replace? what with?
|
||||
env.note_versionchange(node['type'], node['version'], node, self.lineno)
|
||||
return ret
|
||||
|
||||
@@ -261,66 +240,6 @@ class SeeAlso(Directive):
|
||||
return ret
|
||||
|
||||
|
||||
token_re = re.compile('`([a-z_][a-z0-9_]*)`')
|
||||
|
||||
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.
|
||||
@@ -338,57 +257,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.
|
||||
@@ -494,22 +362,19 @@ class Only(Directive):
|
||||
directives.register_directive('toctree', directive_dwim(TocTree))
|
||||
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))
|
||||
|
||||
# register the standard rst class directive under a different name
|
||||
|
||||
# only for backwards compatibility now
|
||||
try:
|
||||
# docutils 0.4
|
||||
from docutils.parsers.rst.directives.misc import class_directive
|
||||
|
176
sphinx/domains/__init__.py
Normal file
176
sphinx/domains/__init__.py
Normal file
@@ -0,0 +1,176 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinx.domains
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Support for domains, which are groupings of description directives
|
||||
and roles describing e.g. constructs of one programming language.
|
||||
|
||||
:copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
class ObjType(object):
|
||||
"""
|
||||
XXX add docstring
|
||||
"""
|
||||
|
||||
known_attrs = {
|
||||
'search': True,
|
||||
}
|
||||
|
||||
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[rolename] = name
|
||||
self.object_type = self._role2type.get # XXX necessary?
|
||||
|
||||
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)
|
||||
# XXX what about function-style directives?
|
||||
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; an integer
|
||||
(XXX assign meanings)
|
||||
"""
|
||||
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,
|
||||
}
|
203
sphinx/domains/c.py
Normal file
203
sphinx/domains/c.py
Normal file
@@ -0,0 +1,203 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinx.domains.c
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
The C language domain.
|
||||
|
||||
:copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
import string
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import directives
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.roles import XRefRole
|
||||
from sphinx.locale import l_
|
||||
from sphinx.domains import Domain, ObjType
|
||||
from sphinx.directives import ObjectDescription
|
||||
from sphinx.util import make_refnode
|
||||
from sphinx.util.compat import Directive
|
||||
|
||||
|
||||
# RE to split at word boundaries
|
||||
wsplit_re = re.compile(r'(\W+)')
|
||||
|
||||
# REs for C signatures
|
||||
c_sig_re = re.compile(
|
||||
r'''^([^(]*?) # return type
|
||||
([\w:]+) \s* # thing name (colon allowed for C++ class names)
|
||||
(?: \((.*)\) )? # optionally arguments
|
||||
(\s+const)? $ # const specifier
|
||||
''', re.VERBOSE)
|
||||
c_funcptr_sig_re = re.compile(
|
||||
r'''^([^(]+?) # return type
|
||||
(\( [^()]+ \)) \s* # name in parentheses
|
||||
\( (.*) \) # arguments
|
||||
(\s+const)? $ # const specifier
|
||||
''', re.VERBOSE)
|
||||
c_funcptr_name_re = re.compile(r'^\(\s*\*\s*(.*?)\s*\)$')
|
||||
|
||||
|
||||
class CObject(ObjectDescription):
|
||||
"""
|
||||
Description of a C language object.
|
||||
"""
|
||||
|
||||
# These C types aren't described anywhere, so don't try to create
|
||||
# a cross-reference to them
|
||||
stopwords = set(('const', 'void', 'char', 'int', 'long', 'FILE', 'struct'))
|
||||
|
||||
def _parse_type(self, node, ctype):
|
||||
# add cross-ref nodes for all words
|
||||
for part in filter(None, wsplit_re.split(ctype)):
|
||||
tnode = nodes.Text(part, part)
|
||||
if part[0] in string.ascii_letters+'_' and \
|
||||
part not in self.stopwords:
|
||||
pnode = addnodes.pending_xref(
|
||||
'', refdomain='c', reftype='type', reftarget=part,
|
||||
modname=None, classname=None)
|
||||
pnode += tnode
|
||||
node += pnode
|
||||
else:
|
||||
node += tnode
|
||||
|
||||
def parse_signature(self, sig, signode):
|
||||
"""Transform a C (or C++) signature into RST nodes."""
|
||||
# first try the function pointer signature regex, it's more specific
|
||||
m = c_funcptr_sig_re.match(sig)
|
||||
if m is None:
|
||||
m = c_sig_re.match(sig)
|
||||
if m is None:
|
||||
raise ValueError('no match')
|
||||
rettype, name, arglist, const = m.groups()
|
||||
|
||||
signode += addnodes.desc_type('', '')
|
||||
self._parse_type(signode[-1], rettype)
|
||||
try:
|
||||
classname, funcname = name.split('::', 1)
|
||||
classname += '::'
|
||||
signode += addnodes.desc_addname(classname, classname)
|
||||
signode += addnodes.desc_name(funcname, funcname)
|
||||
# name (the full name) is still both parts
|
||||
except ValueError:
|
||||
signode += addnodes.desc_name(name, name)
|
||||
# clean up parentheses from canonical name
|
||||
m = c_funcptr_name_re.match(name)
|
||||
if m:
|
||||
name = m.group(1)
|
||||
if not arglist:
|
||||
if self.objtype == '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.objtype == 'cfunction':
|
||||
return _('%s (C function)') % name
|
||||
elif self.objtype == 'cmember':
|
||||
return _('%s (C member)') % name
|
||||
elif self.objtype == 'cmacro':
|
||||
return _('%s (C macro)') % name
|
||||
elif self.objtype == 'ctype':
|
||||
return _('%s (C type)') % name
|
||||
elif self.objtype == '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)
|
||||
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:
|
||||
return None
|
||||
obj = self.data[target]
|
||||
return make_refnode(builder, fromdocname, obj[0], contnode, target)
|
||||
|
||||
def get_objects(self):
|
||||
for refname, (docname, type) in self.data['objects'].iteritems():
|
||||
yield (refname, type, docname, refname, 0)
|
@@ -1,135 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinx.domains
|
||||
~~~~~~~~~~~~~~
|
||||
sphinx.domains.python
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Support for domains, which are groupings of description directives
|
||||
and roles describing e.g. constructs of one programming language.
|
||||
The Python domain.
|
||||
|
||||
:copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
import string
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import directives
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.roles import XRefRole
|
||||
from sphinx.directives import DescDirective
|
||||
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
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
name = ''
|
||||
directives = {}
|
||||
roles = {}
|
||||
label = ''
|
||||
|
||||
# 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 = {}
|
||||
|
||||
def clear_doc(self, docname):
|
||||
"""
|
||||
Remove traces of a document in the domain-specific inventories.
|
||||
"""
|
||||
pass
|
||||
|
||||
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)
|
||||
# XXX what about function-style directives?
|
||||
BaseDirective = self.directives[name]
|
||||
class DirectiveAdapter(BaseDirective):
|
||||
def run(self):
|
||||
self.name = fullname
|
||||
return BaseDirective.run(self)
|
||||
self._directive_cache[name] = DirectiveAdapter
|
||||
return DirectiveAdapter
|
||||
|
||||
def resolve_xref(self, 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
|
||||
|
||||
|
||||
# REs for Python signatures
|
||||
py_sig_re = re.compile(
|
||||
r'''^ ([\w.]*\.)? # class name(s)
|
||||
@@ -142,7 +35,7 @@ py_sig_re = re.compile(
|
||||
py_paramlist_re = re.compile(r'([\[\],])') # split at '[', ']' and ','
|
||||
|
||||
|
||||
class PythonDesc(DescDirective):
|
||||
class PyObject(ObjectDescription):
|
||||
"""
|
||||
Description of a general Python object.
|
||||
"""
|
||||
@@ -174,19 +67,20 @@ class PythonDesc(DescDirective):
|
||||
raise ValueError
|
||||
classname, name, arglist, retann = m.groups()
|
||||
|
||||
if self.env.currclass:
|
||||
currclass = self.env.doc_read_data.get('py_class')
|
||||
if currclass:
|
||||
add_module = False
|
||||
if classname and classname.startswith(self.env.currclass):
|
||||
if classname and classname.startswith(currclass):
|
||||
fullname = classname + name
|
||||
# class name is given again in the signature
|
||||
classname = classname[len(self.env.currclass):].lstrip('.')
|
||||
classname = classname[len(currclass):].lstrip('.')
|
||||
elif classname:
|
||||
# class name is given in the signature, but different
|
||||
# (shouldn't happen)
|
||||
fullname = self.env.currclass + '.' + classname + name
|
||||
fullname = currclass + '.' + classname + name
|
||||
else:
|
||||
# class name is not given in the signature
|
||||
fullname = self.env.currclass + '.' + name
|
||||
fullname = currclass + '.' + name
|
||||
else:
|
||||
add_module = True
|
||||
fullname = classname and classname + name or name
|
||||
@@ -200,7 +94,8 @@ class PythonDesc(DescDirective):
|
||||
# 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)
|
||||
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)
|
||||
@@ -244,7 +139,8 @@ class PythonDesc(DescDirective):
|
||||
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)
|
||||
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:
|
||||
@@ -260,7 +156,7 @@ class PythonDesc(DescDirective):
|
||||
'other instance in ' +
|
||||
self.env.doc2path(objects[fullname][0]),
|
||||
self.lineno)
|
||||
objects[fullname] = (self.env.docname, self.desctype)
|
||||
objects[fullname] = (self.env.docname, self.objtype)
|
||||
|
||||
indextext = self.get_index_text(modname, name_cls)
|
||||
if indextext:
|
||||
@@ -273,23 +169,23 @@ class PythonDesc(DescDirective):
|
||||
|
||||
def after_content(self):
|
||||
if self.clsname_set:
|
||||
self.env.currclass = None
|
||||
self.env.doc_read_data['py_class'] = None
|
||||
|
||||
|
||||
class ModulelevelDesc(PythonDesc):
|
||||
class PyModulelevel(PyObject):
|
||||
"""
|
||||
Description of an object on module level (functions, data).
|
||||
"""
|
||||
|
||||
def needs_arglist(self):
|
||||
return self.desctype == 'function'
|
||||
return self.objtype == 'function'
|
||||
|
||||
def get_index_text(self, modname, name_cls):
|
||||
if self.desctype == 'function':
|
||||
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.desctype == 'data':
|
||||
elif self.objtype == 'data':
|
||||
if not modname:
|
||||
return _('%s (built-in variable)') % name_cls[0]
|
||||
return _('%s (in module %s)') % (name_cls[0], modname)
|
||||
@@ -297,50 +193,50 @@ class ModulelevelDesc(PythonDesc):
|
||||
return ''
|
||||
|
||||
|
||||
class ClasslikeDesc(PythonDesc):
|
||||
class PyClasslike(PyObject):
|
||||
"""
|
||||
Description of a class-like object (classes, interfaces, exceptions).
|
||||
"""
|
||||
|
||||
def get_signature_prefix(self, sig):
|
||||
return self.desctype + ' '
|
||||
return self.objtype + ' '
|
||||
|
||||
def get_index_text(self, modname, name_cls):
|
||||
if self.desctype == 'class':
|
||||
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.desctype == 'exception':
|
||||
elif self.objtype == 'exception':
|
||||
return name_cls[0]
|
||||
else:
|
||||
return ''
|
||||
|
||||
def before_content(self):
|
||||
PythonDesc.before_content(self)
|
||||
PyObject.before_content(self)
|
||||
if self.names:
|
||||
self.env.currclass = self.names[0][0]
|
||||
self.env.doc_read_data['py_class'] = self.names[0][0]
|
||||
self.clsname_set = True
|
||||
|
||||
|
||||
class ClassmemberDesc(PythonDesc):
|
||||
class PyClassmember(PyObject):
|
||||
"""
|
||||
Description of a class member (methods, attributes).
|
||||
"""
|
||||
|
||||
def needs_arglist(self):
|
||||
return self.desctype.endswith('method')
|
||||
return self.objtype.endswith('method')
|
||||
|
||||
def get_signature_prefix(self, sig):
|
||||
if self.desctype == 'staticmethod':
|
||||
if self.objtype == 'staticmethod':
|
||||
return 'static '
|
||||
elif self.desctype == 'classmethod':
|
||||
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.desctype == 'method':
|
||||
if self.objtype == 'method':
|
||||
try:
|
||||
clsname, methname = name.rsplit('.', 1)
|
||||
except ValueError:
|
||||
@@ -352,7 +248,7 @@ class ClassmemberDesc(PythonDesc):
|
||||
return _('%s() (%s.%s method)') % (methname, modname, clsname)
|
||||
else:
|
||||
return _('%s() (%s method)') % (methname, clsname)
|
||||
elif self.desctype == 'staticmethod':
|
||||
elif self.objtype == 'staticmethod':
|
||||
try:
|
||||
clsname, methname = name.rsplit('.', 1)
|
||||
except ValueError:
|
||||
@@ -365,7 +261,7 @@ class ClassmemberDesc(PythonDesc):
|
||||
clsname)
|
||||
else:
|
||||
return _('%s() (%s static method)') % (methname, clsname)
|
||||
elif self.desctype == 'classmethod':
|
||||
elif self.objtype == 'classmethod':
|
||||
try:
|
||||
clsname, methname = name.rsplit('.', 1)
|
||||
except ValueError:
|
||||
@@ -378,7 +274,7 @@ class ClassmemberDesc(PythonDesc):
|
||||
clsname)
|
||||
else:
|
||||
return _('%s() (%s class method)') % (methname, clsname)
|
||||
elif self.desctype == 'attribute':
|
||||
elif self.objtype == 'attribute':
|
||||
try:
|
||||
clsname, attrname = name.rsplit('.', 1)
|
||||
except ValueError:
|
||||
@@ -394,9 +290,10 @@ class ClassmemberDesc(PythonDesc):
|
||||
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('.')
|
||||
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
|
||||
|
||||
|
||||
@@ -420,7 +317,7 @@ class PyModule(Directive):
|
||||
env = self.state.document.settings.env
|
||||
modname = self.arguments[0].strip()
|
||||
noindex = 'noindex' in self.options
|
||||
env.currmodule = modname
|
||||
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)
|
||||
@@ -463,16 +360,16 @@ class PyCurrentModule(Directive):
|
||||
env = self.state.document.settings.env
|
||||
modname = self.arguments[0].strip()
|
||||
if modname == 'None':
|
||||
env.currmodule = None
|
||||
env.doc_read_data['py_module'] = None
|
||||
else:
|
||||
env.currmodule = modname
|
||||
env.doc_read_data['py_module'] = modname
|
||||
return []
|
||||
|
||||
|
||||
class PyXRefRole(XRefRole):
|
||||
def process_link(self, env, pnode, has_explicit_title, title, target):
|
||||
pnode['modname'] = env.currmodule
|
||||
pnode['classname'] = env.currclass
|
||||
pnode['modname'] = env.doc_read_data.get('py_module')
|
||||
pnode['classname'] = 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
|
||||
@@ -495,31 +392,41 @@ 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': ModulelevelDesc,
|
||||
'data': ModulelevelDesc,
|
||||
'class': ClasslikeDesc,
|
||||
'exception': ClasslikeDesc,
|
||||
'method': ClassmemberDesc,
|
||||
'classmethod': ClassmemberDesc,
|
||||
'staticmethod': ClassmemberDesc,
|
||||
'attribute': ClassmemberDesc,
|
||||
'module': PyModule,
|
||||
'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),
|
||||
'data': PyXRefRole(),
|
||||
'exc': PyXRefRole(),
|
||||
'func': PyXRefRole(fix_parens=True),
|
||||
'class': PyXRefRole(),
|
||||
'const': PyXRefRole(),
|
||||
'attr': PyXRefRole(),
|
||||
'meth': PyXRefRole(fix_parens=True),
|
||||
'mod': PyXRefRole(),
|
||||
'obj': PyXRefRole(),
|
||||
'attr': PyXRefRole(),
|
||||
'meth': PyXRefRole(fix_parens=True),
|
||||
'mod': PyXRefRole(),
|
||||
'obj': PyXRefRole(),
|
||||
}
|
||||
initial_data = {
|
||||
'objects': {}, # fullname -> docname, desctype
|
||||
'objects': {}, # fullname -> docname, objtype
|
||||
'modules': {}, # modname -> docname, synopsis, platform, deprecated
|
||||
}
|
||||
|
||||
@@ -531,10 +438,10 @@ class PythonDomain(Domain):
|
||||
if fn == docname:
|
||||
del self.data['modules'][modname]
|
||||
|
||||
def find_desc(self, env, modname, classname, name, type, searchorder=0):
|
||||
def find_obj(self, env, modname, classname, name, type, searchorder=0):
|
||||
"""
|
||||
Find a Python object description for "name", perhaps using the given
|
||||
module and/or classname.
|
||||
Find a Python object for "name", perhaps using the given module and/or
|
||||
classname.
|
||||
"""
|
||||
# skip parens
|
||||
if name[-2:] == '()':
|
||||
@@ -592,188 +499,19 @@ class PythonDomain(Domain):
|
||||
return make_refnode(builder, fromdocname, docname,
|
||||
'module-' + target, contnode, title)
|
||||
else:
|
||||
modname = node['modname']
|
||||
clsname = node['classname']
|
||||
modname = node.get('modname')
|
||||
clsname = node.get('classname')
|
||||
searchorder = node.hasattr('refspecific') and 1 or 0
|
||||
name, desc = self.find_desc(env, modname, clsname,
|
||||
target, typ, searchorder)
|
||||
if not desc:
|
||||
name, obj = self.find_obj(env, modname, clsname,
|
||||
target, typ, searchorder)
|
||||
if not obj:
|
||||
return None
|
||||
else:
|
||||
return make_refnode(builder, fromdocname, desc[0], name,
|
||||
return make_refnode(builder, fromdocname, obj[0], name,
|
||||
contnode, name)
|
||||
|
||||
|
||||
|
||||
# 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 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(
|
||||
'', 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.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)
|
||||
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.desctype)
|
||||
|
||||
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'
|
||||
directives = {
|
||||
'function': CDesc,
|
||||
'member': CDesc,
|
||||
'macro': CDesc,
|
||||
'type': CDesc,
|
||||
'var': CDesc,
|
||||
}
|
||||
roles = {
|
||||
'member': XRefRole(),
|
||||
'macro': XRefRole(),
|
||||
'func' : XRefRole(fix_parens=True),
|
||||
'data': XRefRole(),
|
||||
'type': XRefRole(),
|
||||
}
|
||||
initial_data = {
|
||||
'objects': {}, # fullname -> docname, desctype
|
||||
}
|
||||
|
||||
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:
|
||||
return None
|
||||
desc = self.data[target]
|
||||
return make_refnode(builder, fromdocname, desc[0], contnode, target)
|
||||
|
||||
|
||||
# this contains all registered domains
|
||||
all_domains = {
|
||||
'py': PythonDomain,
|
||||
'c': CDomain,
|
||||
}
|
||||
def get_objects(self):
|
||||
for modname, info in self.data['modules'].iteritems():
|
||||
yield (modname, 'module', info[0], 'module-' + modname, 1)
|
||||
for refname, (docname, type) in self.data['objects'].iteritems():
|
||||
yield (refname, type, docname, refname, 0)
|
361
sphinx/domains/std.py
Normal file
361
sphinx/domains/std.py
Normal file
@@ -0,0 +1,361 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinx.domains.std
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The standard domain.
|
||||
|
||||
:copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from docutils import nodes
|
||||
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_description_unit().
|
||||
"""
|
||||
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 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, pnode, 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)
|
||||
pnode['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', search=False),
|
||||
'token': ObjType(l_('grammar token'), 'token', search=False),
|
||||
'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': XRefRole(), # XXX add index entries
|
||||
'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):
|
||||
return [] # XXX implement
|
@@ -39,7 +39,6 @@ from sphinx import addnodes
|
||||
from sphinx.util import movefile, get_matching_docs, SEP, ustrftime, \
|
||||
docname_join, FilenameUniqDict, url_re, make_refnode
|
||||
from sphinx.errors import SphinxError, ExtensionError
|
||||
from sphinx.directives import additional_xref_types
|
||||
|
||||
orig_role_function = roles.role
|
||||
orig_directive_function = directives.directive
|
||||
@@ -56,7 +55,7 @@ default_settings = {
|
||||
|
||||
# This is increased every time an environment attribute is added
|
||||
# or changed to properly invalidate pickle files.
|
||||
ENV_VERSION = 31
|
||||
ENV_VERSION = 32
|
||||
|
||||
|
||||
default_substitutions = set([
|
||||
@@ -292,14 +291,12 @@ class BuildEnvironment:
|
||||
self.numbered_toctrees = set() # docnames that have :numbered: toctrees
|
||||
|
||||
# domain-specific inventories, here to be pickled
|
||||
self.domaindata = {} # domainname -> domain-specific object
|
||||
self.domaindata = {} # domainname -> domain-specific dict
|
||||
|
||||
# X-ref target inventory
|
||||
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
|
||||
@@ -311,15 +308,8 @@ class BuildEnvironment:
|
||||
self.images = FilenameUniqDict()
|
||||
self.dlfiles = FilenameUniqDict()
|
||||
|
||||
# These are set while parsing a file
|
||||
self.docname = None # current document name
|
||||
# XXX remove currmodule and currclass from here
|
||||
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):
|
||||
@@ -366,12 +356,9 @@ class BuildEnvironment:
|
||||
for labelname, (fn, _, _) in self.labels.items():
|
||||
if fn == docname:
|
||||
del self.labels[labelname]
|
||||
for key, (fn, _) in self.reftargets.items():
|
||||
for key, (fn, _) in self.citations.items():
|
||||
if fn == docname:
|
||||
del self.reftargets[key]
|
||||
for key, (fn, _) in self.progoptions.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
|
||||
@@ -574,7 +561,7 @@ class BuildEnvironment:
|
||||
self.warn(docname, 'default role %s not found' %
|
||||
self.config.default_role)
|
||||
|
||||
self.docname = docname
|
||||
self.doc_read_data['docname'] = docname
|
||||
self.settings['input_encoding'] = self.config.source_encoding
|
||||
self.settings['trim_footnote_reference_space'] = \
|
||||
self.config.trim_footnote_reference_space
|
||||
@@ -603,7 +590,9 @@ class BuildEnvironment:
|
||||
|
||||
# monkey-patch, so that domain directives take precedence
|
||||
def directive(directive_name, language_module, document):
|
||||
"""Lookup a directive."""
|
||||
directive_name = directive_name.lower()
|
||||
# explicit domain given?
|
||||
if ':' in directive_name:
|
||||
domain_name, directive_name = directive_name.split(':', 1)
|
||||
if domain_name in self.domains:
|
||||
@@ -611,16 +600,25 @@ class BuildEnvironment:
|
||||
directive = domain.directive(directive_name)
|
||||
if directive is not None:
|
||||
return directive, []
|
||||
# else look in the default domain
|
||||
elif self.default_domain is not None:
|
||||
directive = self.default_domain.directive(directive_name)
|
||||
if directive is not None:
|
||||
return directive, []
|
||||
# always look in the std domain
|
||||
# (XXX or register them in the docutils namespace?)
|
||||
directive = self.domains['std'].directive(directive_name)
|
||||
if directive is not None:
|
||||
return directive, []
|
||||
# last, look in the default docutils namespace
|
||||
return orig_directive_function(directive_name, language_module,
|
||||
document)
|
||||
directives.directive = directive
|
||||
|
||||
def role(role_name, language_module, lineno, reporter):
|
||||
"""Lookup a role name."""
|
||||
role_name = role_name.lower()
|
||||
# explicit domain given?
|
||||
if ':' in role_name:
|
||||
domain_name, role_name = role_name.split(':', 1)
|
||||
if domain_name in self.domains:
|
||||
@@ -628,10 +626,16 @@ class BuildEnvironment:
|
||||
role = domain.role(role_name)
|
||||
if role is not None:
|
||||
return role, []
|
||||
# else look in the default domain
|
||||
elif self.default_domain is not None:
|
||||
role = self.default_domain.role(role_name)
|
||||
if role is not None:
|
||||
return role, []
|
||||
# always look in the std domain
|
||||
role = self.domains['std'].role(role_name)
|
||||
if role is not None:
|
||||
return role, []
|
||||
# last, look in the default docutils namespace
|
||||
return orig_role_function(role_name, language_module,
|
||||
lineno, reporter)
|
||||
roles.role = role
|
||||
@@ -678,11 +682,8 @@ class BuildEnvironment:
|
||||
metanode.__class__ = addnodes.meta
|
||||
|
||||
# cleanup
|
||||
self.docname = None
|
||||
self.currmodule = None
|
||||
self.currclass = None
|
||||
self.doc_read_data.clear()
|
||||
self.default_domain = None
|
||||
self.gloss_entries = set()
|
||||
|
||||
if save_parsed:
|
||||
# save the parsed doctree
|
||||
@@ -699,6 +700,33 @@ 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)
|
||||
|
||||
def filter_messages(self, doctree):
|
||||
"""
|
||||
Filter system messages from a doctree.
|
||||
@@ -887,11 +915,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
|
||||
@@ -988,24 +1016,14 @@ class BuildEnvironment:
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
# -------
|
||||
# these are called from docutils directives and therefore use self.docname
|
||||
#
|
||||
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)
|
||||
|
||||
# XXX remove
|
||||
def note_versionchange(self, type, version, node, lineno):
|
||||
self.versionchanges.setdefault(version, []).append(
|
||||
(type, self.docname, lineno, self.currmodule, self.currdesc,
|
||||
(type, self.docname, lineno,
|
||||
self.doc_read_data.get('py_module'),
|
||||
self.doc_read_data.get('object'),
|
||||
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."""
|
||||
@@ -1197,11 +1215,6 @@ class BuildEnvironment:
|
||||
return newnode
|
||||
|
||||
def resolve_references(self, doctree, fromdocname, builder):
|
||||
# XXX remove this
|
||||
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
|
||||
@@ -1218,6 +1231,7 @@ class BuildEnvironment:
|
||||
raise NoUri
|
||||
newnode = domain.resolve_xref(self, fromdocname, builder,
|
||||
typ, target, node, contnode)
|
||||
# really hardwired reference types
|
||||
elif typ == 'ref':
|
||||
if node['refcaption']:
|
||||
# reference to anonymous label; the reference uses
|
||||
@@ -1276,37 +1290,21 @@ 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 = make_refnode(builder, fromdocname, docname,
|
||||
labelid, contnode)
|
||||
elif typ == 'option':
|
||||
progname = node['refprogram']
|
||||
docname, labelid = self.progoptions.get((progname, target),
|
||||
('', ''))
|
||||
if not docname:
|
||||
newnode = contnode
|
||||
else:
|
||||
newnode = make_refnode(builder, fromdocname, docname,
|
||||
labelid, 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 = make_refnode(builder, fromdocname, docname,
|
||||
labelid, contnode)
|
||||
|
@@ -546,9 +546,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
|
||||
@@ -589,8 +589,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):
|
||||
@@ -748,11 +748,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]
|
||||
|
||||
@@ -771,21 +770,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]
|
||||
|
||||
@@ -1082,7 +1080,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
|
||||
|
@@ -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 = []
|
||||
|
||||
|
@@ -283,7 +283,8 @@ class InheritanceDiagram(Directive):
|
||||
|
||||
# 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)]
|
||||
|
@@ -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
|
||||
@@ -41,12 +41,44 @@ if hasattr(urllib2, 'HTTPSHandler'):
|
||||
urllib2.install_opener(urllib2.build_opener(*handlers))
|
||||
|
||||
|
||||
def fetch_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:
|
||||
location += '#' + name
|
||||
invdata.setdefault(type, {})[name] = (projname, version, location)
|
||||
return invdata
|
||||
|
||||
|
||||
def fetch_inventory_v2(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, prio, location = line.rstrip().split(None, 3)
|
||||
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 +89,18 @@ def fetch_inventory(app, uri, inv):
|
||||
'%s: %s' % (inv, err.__class__, err))
|
||||
return
|
||||
try:
|
||||
line = f.next()
|
||||
if line.rstrip() != '# Sphinx inventory version 1':
|
||||
line = f.next().rstrip()
|
||||
if line == '# Sphinx inventory version 1':
|
||||
invdata = fetch_inventory_v1(f, uri, join)
|
||||
elif line == '# Sphinx inventory version 2':
|
||||
invdata = fetch_inventory_v2(f, uri, join)
|
||||
else:
|
||||
f.close()
|
||||
raise ValueError('unknown or unsupported inventory version')
|
||||
line = f.next()
|
||||
projname = line.rstrip()[11:].decode('utf-8')
|
||||
line = f.next()
|
||||
version = line.rstrip()[11:]
|
||||
for line in f:
|
||||
name, type, location = line.rstrip().split(None, 2)
|
||||
if localuri:
|
||||
location = path.join(uri, location)
|
||||
else:
|
||||
location = posixpath.join(uri, location)
|
||||
invdata[name] = (type, projname, version, location)
|
||||
f.close()
|
||||
except Exception, err:
|
||||
app.warn('intersphinx inventory %r not readable due to '
|
||||
'%s: %s' % (inv, err.__class__, err))
|
||||
'%s: %s' % (inv, err.__class__.__name__, err))
|
||||
else:
|
||||
return invdata
|
||||
|
||||
@@ -111,27 +137,29 @@ 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['refdomain']
|
||||
fulltype = domain + ':' + type
|
||||
target = node['reftarget']
|
||||
if type == 'mod':
|
||||
type, proj, version, uri = env.intersphinx_inventory.get(target,
|
||||
('','','',''))
|
||||
if type != 'mod':
|
||||
return None
|
||||
target = 'module-' + target # for link anchor
|
||||
else:
|
||||
if target[-2:] == '()':
|
||||
target = target[:-2]
|
||||
target = target.rstrip(' *')
|
||||
# special case: exceptions and object methods
|
||||
if type == 'exc' and '.' not in target and \
|
||||
'exceptions.' + target in env.intersphinx_inventory:
|
||||
target = 'exceptions.' + target
|
||||
elif type in ('func', 'meth') and '.' not in target and \
|
||||
'object.' + target in env.intersphinx_inventory:
|
||||
target = 'object.' + target
|
||||
if target not in env.intersphinx_inventory:
|
||||
return None
|
||||
type, proj, version, uri = env.intersphinx_inventory[target]
|
||||
if type not in env.intersphinx_inventory:
|
||||
return
|
||||
if target not in env.intersphinx_inventory[type]:
|
||||
return
|
||||
proj, version, uri = env.intersphinx_inventory[type][target]
|
||||
|
||||
# XXX
|
||||
if target[-2:] == '()':
|
||||
target = target[:-2]
|
||||
target = target.rstrip(' *')
|
||||
# special case: exceptions and object methods
|
||||
if type == 'exc' and '.' not in target and \
|
||||
'exceptions.' + target in env.intersphinx_inventory:
|
||||
target = 'exceptions.' + target
|
||||
elif type in ('func', 'meth') and '.' not in target and \
|
||||
'object.' + target in env.intersphinx_inventory:
|
||||
target = 'object.' + target
|
||||
if target not in env.intersphinx_inventory:
|
||||
return None
|
||||
type, proj, version, uri = env.intersphinx_inventory[target]
|
||||
# print "Intersphinx hit:", target, uri
|
||||
newnode = nodes.reference('', '')
|
||||
newnode['refuri'] = uri + '#' + target
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
|
@@ -41,7 +41,7 @@ for rolename, nodeclass in generic_docroles.iteritems():
|
||||
# -- generic cross-reference roles ---------------------------------------------
|
||||
|
||||
class XRefRole(object):
|
||||
"""XXX add docstring"""
|
||||
"""XXX add docstrings"""
|
||||
|
||||
nodeclass = addnodes.pending_xref
|
||||
innernodeclass = nodes.literal
|
||||
@@ -101,24 +101,6 @@ class XRefRole(object):
|
||||
return [pnode], []
|
||||
|
||||
|
||||
class OptionXRefRole(XRefRole):
|
||||
innernodeclass = addnodes.literal_emphasis
|
||||
|
||||
def process_link(self, env, pnode, has_explicit_title, title, target):
|
||||
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
|
||||
return title, target
|
||||
|
||||
|
||||
_EnvvarXrefRole = XRefRole()
|
||||
|
||||
def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
|
||||
@@ -129,11 +111,11 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
|
||||
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)
|
||||
# XXX remove
|
||||
if typ == 'envvar':
|
||||
indexnode['entries'] = [('single', text, targetid, text),
|
||||
('single', _('environment variable; %s') % text,
|
||||
@@ -214,9 +196,6 @@ def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
specific_docroles = {
|
||||
'keyword': XRefRole(),
|
||||
'ref': XRefRole(lowercase=True, innernodeclass=nodes.emphasis),
|
||||
'token': XRefRole(),
|
||||
'term': XRefRole(lowercase=True, innernodeclass=nodes.emphasis),
|
||||
'option': OptionXRefRole(innernodeclass=addnodes.literal_emphasis),
|
||||
'doc': XRefRole(),
|
||||
'download': XRefRole(nodeclass=addnodes.download_reference),
|
||||
|
||||
@@ -233,7 +212,7 @@ for rolename, func in specific_docroles.iteritems():
|
||||
roles.register_local_role(rolename, func)
|
||||
|
||||
|
||||
# compatibility alias
|
||||
# backwards compatibility alias
|
||||
def xfileref_role(*args, **kwds):
|
||||
warnings.warn('xfileref_role is deprecated, use XRefRole',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
|
@@ -119,8 +119,8 @@ class IndexBuilder(object):
|
||||
self._titles = {}
|
||||
# stemmed word -> set(filenames)
|
||||
self._mapping = {}
|
||||
# desctypes -> index
|
||||
self._desctypes = {}
|
||||
# objtype -> index
|
||||
self._objtypes = {}
|
||||
|
||||
def load(self, stream, format):
|
||||
"""Reconstruct from frozen data."""
|
||||
@@ -138,7 +138,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."""
|
||||
@@ -157,7 +157,7 @@ class IndexBuilder(object):
|
||||
|
||||
def get_descrefs(self, fn2index):
|
||||
rv = {}
|
||||
dt = self._desctypes
|
||||
dt = self._objtypes
|
||||
# XXX implement search capability
|
||||
return rv
|
||||
for fullname, (doc, desctype) in self.env.descrefs.iteritems():
|
||||
@@ -193,9 +193,10 @@ class IndexBuilder(object):
|
||||
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()),
|
||||
# XXX
|
||||
#descrefs=self.get_descrefs(fn2index),
|
||||
#modules=self.get_modules(fn2index),
|
||||
objtypes=dict((v, k) for (k, v) in self._objtypes.items()),
|
||||
)
|
||||
|
||||
def prune(self, filenames):
|
||||
|
@@ -330,9 +330,8 @@ 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 fileMap = {};
|
||||
var files = null;
|
||||
var objectResults = [];
|
||||
@@ -341,6 +340,7 @@ var Search = {
|
||||
|
||||
// lookup as object
|
||||
if (object != null) {
|
||||
// XXX must be adapted
|
||||
for (var module in modules) {
|
||||
if (module.indexOf(object) > -1) {
|
||||
fn = modules[module];
|
||||
@@ -348,12 +348,12 @@ var Search = {
|
||||
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]];
|
||||
match = objects[prefix][name];
|
||||
descr = objtypes[match[1]] + _(', in ') + titles[match[0]];
|
||||
objectResults.push([filenames[match[0]], fullname, '#'+fullname, descr]);
|
||||
}
|
||||
}
|
||||
|
@@ -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):
|
||||
|
@@ -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
|
||||
@@ -462,7 +462,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 = ''
|
||||
|
@@ -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)
|
||||
|
@@ -16,7 +16,7 @@ Contents:
|
||||
subdir/includes
|
||||
includes
|
||||
markup
|
||||
desc
|
||||
objects
|
||||
bom
|
||||
math
|
||||
autodoc
|
||||
|
@@ -97,28 +97,28 @@ def test_parse_name():
|
||||
verify('function', 'util.raises', ('util', ['raises'], None, None))
|
||||
verify('function', 'util.raises(exc) -> None',
|
||||
('util', ['raises'], 'exc', 'None'))
|
||||
directive.env.autodoc_current_module = 'util'
|
||||
directive.env.doc_read_data['autodoc_module'] = 'util'
|
||||
verify('function', 'raises', ('util', ['raises'], None, None))
|
||||
directive.env.autodoc_current_module = None
|
||||
directive.env.currmodule = 'util'
|
||||
del directive.env.doc_read_data['autodoc_module']
|
||||
directive.env.doc_read_data['py_module'] = 'util'
|
||||
verify('function', 'raises', ('util', ['raises'], None, None))
|
||||
verify('class', 'TestApp', ('util', ['TestApp'], None, None))
|
||||
|
||||
# for members
|
||||
directive.env.currmodule = 'foo'
|
||||
directive.env.doc_read_data['py_module'] = 'foo'
|
||||
verify('method', 'util.TestApp.cleanup',
|
||||
('util', ['TestApp', 'cleanup'], None, None))
|
||||
directive.env.currmodule = 'util'
|
||||
directive.env.currclass = 'Foo'
|
||||
directive.env.autodoc_current_class = 'TestApp'
|
||||
directive.env.doc_read_data['py_module'] = 'util'
|
||||
directive.env.doc_read_data['py_class'] = 'Foo'
|
||||
directive.env.doc_read_data['autodoc_class'] = 'TestApp'
|
||||
verify('method', 'cleanup', ('util', ['TestApp', 'cleanup'], None, None))
|
||||
verify('method', 'TestApp.cleanup',
|
||||
('util', ['TestApp', 'cleanup'], None, None))
|
||||
|
||||
# and clean up
|
||||
directive.env.currmodule = None
|
||||
directive.env.currclass = None
|
||||
directive.env.autodoc_current_class = None
|
||||
del directive.env.doc_read_data['py_module']
|
||||
del directive.env.doc_read_data['py_class']
|
||||
del directive.env.doc_read_data['autodoc_class']
|
||||
|
||||
|
||||
def test_format_signature():
|
||||
@@ -353,7 +353,7 @@ def test_generate():
|
||||
'function', 'util.foobar', more_content=None)
|
||||
|
||||
# test auto and given content mixing
|
||||
directive.env.currmodule = 'test_autodoc'
|
||||
directive.env.doc_read_data['py_module'] = 'test_autodoc'
|
||||
assert_result_contains(' Function.', 'method', 'Class.meth')
|
||||
add_content = ViewList()
|
||||
add_content.append('Content.', '', 0)
|
||||
@@ -428,7 +428,7 @@ def test_generate():
|
||||
'class', 'Outer', all_members=True)
|
||||
|
||||
# test generation for C modules (which have no source file)
|
||||
directive.env.currmodule = 'time'
|
||||
directive.env.doc_read_data['py_module'] = 'time'
|
||||
assert_processes([('function', 'time.asctime')], 'function', 'asctime')
|
||||
assert_processes([('function', 'time.asctime')], 'function', 'asctime')
|
||||
|
||||
|
@@ -111,11 +111,11 @@ HTML_XPATH = {
|
||||
".//li/tt/em/span[@class='pre']": '^i$',
|
||||
".//a[@href='http://www.python.org/dev/peps/pep-0008']/strong": 'PEP 8',
|
||||
".//a[@href='http://tools.ietf.org/html/rfc1.html']/strong": 'RFC 1',
|
||||
".//a[@href='desc.html#envvar-HOME']/tt/span[@class='pre']": 'HOME',
|
||||
".//a[@href='objects.html#envvar-HOME']/tt/span[@class='pre']": 'HOME',
|
||||
".//a[@href='#with']/tt/span[@class='pre']": '^with$',
|
||||
".//a[@href='#grammar-token-try_stmt']/tt/span": '^statement$',
|
||||
".//a[@href='subdir/includes.html']/em": 'Including in subdir',
|
||||
".//a[@href='desc.html#cmdoption-python-c']/em": 'Python -c option',
|
||||
".//a[@href='objects.html#cmdoption-python-c']/em": 'Python -c option',
|
||||
# abbreviations
|
||||
".//abbr[@title='abbreviation']": '^abbr$',
|
||||
# version stuff
|
||||
@@ -141,13 +141,13 @@ HTML_XPATH = {
|
||||
".//p": 'In both.',
|
||||
".//p": 'Always present',
|
||||
},
|
||||
'desc.html': {
|
||||
'objects.html': {
|
||||
".//dt[@id='mod.Cls.meth1']": '',
|
||||
".//dt[@id='errmod.Error']": '',
|
||||
".//a[@href='#mod.Cls']": '',
|
||||
".//dl[@class='userdesc']": '',
|
||||
".//dt[@id='userdescrole-myobj']": '',
|
||||
".//a[@href='#userdescrole-myobj']": '',
|
||||
".//dt[@id='userdesc-myobj']": '',
|
||||
".//a[@href='#userdesc-myobj']": '',
|
||||
".//span[@class='pre']": 'CFunction()',
|
||||
},
|
||||
'contents.html': {
|
||||
|
@@ -95,7 +95,7 @@ def test_object_inventory():
|
||||
refs = env.domaindata['py']['objects']
|
||||
|
||||
assert 'func_without_module' in refs
|
||||
assert refs['func_without_module'] == ('desc', 'function')
|
||||
assert refs['func_without_module'] == ('objects', 'function')
|
||||
assert 'func_without_module2' in refs
|
||||
assert 'mod.func_in_module' in refs
|
||||
assert 'mod.Cls' in refs
|
||||
@@ -110,7 +110,7 @@ def test_object_inventory():
|
||||
assert 'func_noindex' not in refs
|
||||
|
||||
assert env.domaindata['py']['modules']['mod'] == \
|
||||
('desc', 'Module synopsis.', 'UNIX', False)
|
||||
('objects', 'Module synopsis.', 'UNIX', False)
|
||||
|
||||
assert env.domains['py'].data is env.domaindata['py']
|
||||
assert env.domains['c'].data is env.domaindata['c']
|
||||
|
Reference in New Issue
Block a user