Move domain-specific code around a bit; builtin domains are now completely in sphinx.domains. Test suite does not pass yet.

This commit is contained in:
Georg Brandl committed 2009-07-05 12:24:27 +02:00
1 parent 9199b21f85
commit c02b7149aa
7 files changed
+573 -614

No files matched your search

+2 -2
View File
@@ -119,8 +119,8 @@ The directives are:
Describes a "simple" C macro. Simple macros are macros which are used
for code expansion, but which do not take arguments so cannot be described as
functions. This is not to be used for simple constant definitions. Examples
of its use in the Python documentation include :cmacro:`PyObject_HEAD` and
:cmacro:`Py_BEGIN_ALLOW_THREADS`.
of its use in the Python documentation include :c:macro:`PyObject_HEAD` and
:c:macro:`Py_BEGIN_ALLOW_THREADS`.
.. directive:: .. ctype:: name
+5 -3
View File
@@ -20,7 +20,7 @@ from docutils import nodes
from docutils.parsers.rst import directives, roles
import sphinx
from sphinx.roles import make_xref_role, simple_link_func
from sphinx.roles import XRefRole
from sphinx.config import Config
from sphinx.errors import SphinxError, SphinxWarning, ExtensionError
from sphinx.domains import domains
@@ -310,14 +310,16 @@ class Sphinx(object):
parse_node)
directives.register_directive(directivename,
directive_dwim(GenericDesc))
role_func = make_xref_role(simple_link_func, innernodeclass=ref_nodeclass)
# XXX support more options?
role_func = XRefRole('', innernodeclass=ref_nodeclass)
roles.register_local_role(rolename, role_func)
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))
role_func = make_xref_role(simple_link_func, innernodeclass=ref_nodeclass)
# XXX support more options
role_func = XRefRole('', innernodeclass=ref_nodeclass)
roles.register_local_role(rolename, role_func)
def add_transform(self, transform):
-414
View File
@@ -14,7 +14,6 @@ from docutils import nodes
from docutils.parsers.rst import directives
from sphinx import addnodes
from sphinx.domains import Domain, domains
from sphinx.locale import l_
from sphinx.util import ws_re
from sphinx.util.compat import Directive, directive_dwim
@@ -33,39 +32,10 @@ def _is_only_paragraph(node):
return False
# REs for Python signatures
py_sig_re = re.compile(
r'''^ ([\w.]*\.)? # class name(s)
(\w+) \s* # thing name
(?: \((.*)\) # optional: arguments
(?:\s* -> \s* (.*))? # return annotation
)? $ # and nothing more
''', re.VERBOSE)
py_paramlist_re = re.compile(r'([\[\],])') # split at '[', ']' and ','
# REs for C signatures
c_sig_re = re.compile(
r'''^([^(]*?) # return type
([\w:]+) \s* # thing name (colon allowed for C++ class names)
(?: \((.*)\) )? # optionally arguments
(\s+const)? $ # const specifier
''', re.VERBOSE)
c_funcptr_sig_re = re.compile(
r'''^([^(]+?) # return type
(\( [^()]+ \)) \s* # name in parentheses
\( (.*) \) # arguments
(\s+const)? $ # const specifier
''', re.VERBOSE)
c_funcptr_name_re = re.compile(r'^\(\s*\*\s*(.*?)\s*\)$')
# RE for option descriptions
option_desc_re = re.compile(
r'((?:/|-|--)[-_a-zA-Z0-9]+)(\s*.*?)(?=,\s+(?:/|-|--)|$)')
# RE to split at word boundaries
wsplit_re = re.compile(r'(\W+)')
# RE to strip backslash escapes
strip_backslash_re = re.compile(r'\\(?=[^\\])')
@@ -278,360 +248,6 @@ class DescDirective(Directive):
return [self.indexnode, node]
class PythonDesc(DescDirective):
"""
Description of a general Python object.
"""
def get_signature_prefix(self, sig):
"""
May return a prefix to put before the object name in the signature.
"""
return ''
def needs_arglist(self):
"""
May return true if an empty argument list is to be generated even if
the document contains none.
"""
return False
def parse_signature(self, sig, signode):
"""
Transform a Python signature into RST nodes.
Returns (fully qualified name of the thing, classname if any).
If inside a class, the current class name is handled intelligently:
* it is stripped from the displayed name if present
* it is added to the full name (return value) if not present
"""
m = py_sig_re.match(sig)
if m is None:
raise ValueError
classname, name, arglist, retann = m.groups()
if self.env.currclass:
add_module = False
if classname and classname.startswith(self.env.currclass):
fullname = classname + name
# class name is given again in the signature
classname = classname[len(self.env.currclass):].lstrip('.')
elif classname:
# class name is given in the signature, but different
# (shouldn't happen)
fullname = self.env.currclass + '.' + classname + name
else:
# class name is not given in the signature
fullname = self.env.currclass + '.' + name
else:
add_module = True
fullname = classname and classname + name or name
prefix = self.get_signature_prefix(sig)
if prefix:
signode += addnodes.desc_annotation(prefix, prefix)
if classname:
signode += addnodes.desc_addname(classname, classname)
# exceptions are a special case, since they are documented in the
# 'exceptions' module.
elif add_module and self.env.config.add_module_names:
modname = self.options.get('module', self.env.currmodule)
if modname and modname != 'exceptions':
nodetext = modname + '.'
signode += addnodes.desc_addname(nodetext, nodetext)
signode += addnodes.desc_name(name, name)
if not arglist:
if self.needs_arglist():
# for callables, add an empty parameter list
signode += addnodes.desc_parameterlist()
if retann:
signode += addnodes.desc_returns(retann, retann)
return fullname, classname
signode += addnodes.desc_parameterlist()
stack = [signode[-1]]
for token in py_paramlist_re.split(arglist):
if token == '[':
opt = addnodes.desc_optional()
stack[-1] += opt
stack.append(opt)
elif token == ']':
try:
stack.pop()
except IndexError:
raise ValueError
elif not token or token == ',' or token.isspace():
pass
else:
token = token.strip()
stack[-1] += addnodes.desc_parameter(token, token)
if len(stack) != 1:
raise ValueError
if retann:
signode += addnodes.desc_returns(retann, retann)
return fullname, classname
def get_index_text(self, modname, name):
"""
Return the text for the index entry of the object.
"""
raise NotImplementedError('must be implemented in subclasses')
def add_target_and_index(self, name_cls, sig, signode):
modname = self.options.get('module', self.env.currmodule)
fullname = (modname and modname + '.' or '') + name_cls[0]
# note target
if fullname not in self.state.document.ids:
signode['names'].append(fullname)
signode['ids'].append(fullname)
signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
self.env.note_descref(fullname, self.desctype, self.lineno)
indextext = self.get_index_text(modname, name_cls)
if indextext:
self.indexnode['entries'].append(('single', indextext,
fullname, fullname))
def before_content(self):
# needed for automatic qualification of members (reset in subclasses)
self.clsname_set = False
def after_content(self):
if self.clsname_set:
self.env.currclass = None
class ModulelevelDesc(PythonDesc):
"""
Description of an object on module level (functions, data).
"""
def needs_arglist(self):
return self.desctype == 'function'
def get_index_text(self, modname, name_cls):
if self.desctype == 'function':
if not modname:
return _('%s() (built-in function)') % name_cls[0]
return _('%s() (in module %s)') % (name_cls[0], modname)
elif self.desctype == 'data':
if not modname:
return _('%s (built-in variable)') % name_cls[0]
return _('%s (in module %s)') % (name_cls[0], modname)
else:
return ''
class ClasslikeDesc(PythonDesc):
"""
Description of a class-like object (classes, interfaces, exceptions).
"""
def get_signature_prefix(self, sig):
return self.desctype + ' '
def get_index_text(self, modname, name_cls):
if self.desctype == 'class':
if not modname:
return _('%s (built-in class)') % name_cls[0]
return _('%s (class in %s)') % (name_cls[0], modname)
elif self.desctype == 'exception':
return name_cls[0]
else:
return ''
def before_content(self):
PythonDesc.before_content(self)
if self.names:
self.env.currclass = self.names[0][0]
self.clsname_set = True
class ClassmemberDesc(PythonDesc):
"""
Description of a class member (methods, attributes).
"""
def needs_arglist(self):
return self.desctype.endswith('method')
def get_signature_prefix(self, sig):
if self.desctype == 'staticmethod':
return 'static '
elif self.desctype == 'classmethod':
return 'classmethod '
return ''
def get_index_text(self, modname, name_cls):
name, cls = name_cls
add_modules = self.env.config.add_module_names
if self.desctype == 'method':
try:
clsname, methname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s() (in module %s)') % (name, modname)
else:
return '%s()' % name
if modname and add_modules:
return _('%s() (%s.%s method)') % (methname, modname, clsname)
else:
return _('%s() (%s method)') % (methname, clsname)
elif self.desctype == 'staticmethod':
try:
clsname, methname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s() (in module %s)') % (name, modname)
else:
return '%s()' % name
if modname and add_modules:
return _('%s() (%s.%s static method)') % (methname, modname,
clsname)
else:
return _('%s() (%s static method)') % (methname, clsname)
elif self.desctype == 'classmethod':
try:
clsname, methname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s() (in module %s)') % (name, modname)
else:
return '%s()' % name
if modname:
return _('%s() (%s.%s class method)') % (methname, modname,
clsname)
else:
return _('%s() (%s class method)') % (methname, clsname)
elif self.desctype == 'attribute':
try:
clsname, attrname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s (in module %s)') % (name, modname)
else:
return name
if modname and add_modules:
return _('%s (%s.%s attribute)') % (attrname, modname, clsname)
else:
return _('%s (%s attribute)') % (attrname, clsname)
else:
return ''
def before_content(self):
PythonDesc.before_content(self)
if self.names and self.names[-1][1] and not self.env.currclass:
self.env.currclass = self.names[-1][1].strip('.')
self.clsname_set = True
class CDesc(DescDirective):
"""
Description of a C language object.
"""
# These C types aren't described anywhere, so don't try to create
# a cross-reference to them
stopwords = set(('const', 'void', 'char', 'int', 'long', 'FILE', 'struct'))
def _parse_type(self, node, ctype):
# add cross-ref nodes for all words
for part in filter(None, wsplit_re.split(ctype)):
tnode = nodes.Text(part, part)
if part[0] in string.ascii_letters+'_' and \
part not in self.stopwords:
pnode = addnodes.pending_xref(
'', reftype='ctype', reftarget=part,
modname=None, classname=None)
pnode += tnode
node += pnode
else:
node += tnode
def parse_signature(self, sig, signode):
"""Transform a C (or C++) signature into RST nodes."""
# first try the function pointer signature regex, it's more specific
m = c_funcptr_sig_re.match(sig)
if m is None:
m = c_sig_re.match(sig)
if m is None:
raise ValueError('no match')
rettype, name, arglist, const = m.groups()
signode += addnodes.desc_type('', '')
self._parse_type(signode[-1], rettype)
try:
classname, funcname = name.split('::', 1)
classname += '::'
signode += addnodes.desc_addname(classname, classname)
signode += addnodes.desc_name(funcname, funcname)
# name (the full name) is still both parts
except ValueError:
signode += addnodes.desc_name(name, name)
# clean up parentheses from canonical name
m = c_funcptr_name_re.match(name)
if m:
name = m.group(1)
if not arglist:
if self.desctype == 'cfunction':
# for functions, add an empty parameter list
signode += addnodes.desc_parameterlist()
if const:
signode += addnodes.desc_addname(const, const)
return name
paramlist = addnodes.desc_parameterlist()
arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup
# this messes up function pointer types, but not too badly ;)
args = arglist.split(',')
for arg in args:
arg = arg.strip()
param = addnodes.desc_parameter('', '', noemph=True)
try:
ctype, argname = arg.rsplit(' ', 1)
except ValueError:
# no argument name given, only the type
self._parse_type(param, arg)
else:
self._parse_type(param, ctype)
param += nodes.emphasis(' '+argname, ' '+argname)
paramlist += param
signode += paramlist
if const:
signode += addnodes.desc_addname(const, const)
return name
def get_index_text(self, name):
if self.desctype == 'cfunction':
return _('%s (C function)') % name
elif self.desctype == 'cmember':
return _('%s (C member)') % name
elif self.desctype == 'cmacro':
return _('%s (C macro)') % name
elif self.desctype == 'ctype':
return _('%s (C type)') % name
elif self.desctype == 'cvar':
return _('%s (C variable)') % name
else:
return ''
def add_target_and_index(self, name, sig, signode):
# note target
if name not in self.state.document.ids:
signode['names'].append(name)
signode['ids'].append(name)
signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
self.env.note_descref(name, self.desctype, self.lineno)
indextext = self.get_index_text(name)
if indextext:
self.indexnode['entries'].append(('single', indextext, name, name))
class CmdoptionDesc(DescDirective):
"""
Description of a command-line option (.. cmdoption).
@@ -768,33 +384,3 @@ 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))
class PythonDomain(Domain):
name = 'py'
label = 'Python'
directives = {
'function': ModulelevelDesc,
'data': ModulelevelDesc,
'class': ClasslikeDesc,
'exception': ClasslikeDesc,
'method': ClassmemberDesc,
'classmethod': ClassmemberDesc,
'staticmethod': ClassmemberDesc,
'attribute': ClassmemberDesc,
}
class CDomain(Domain):
name = 'c'
label = 'C'
directives = {
'function': CDesc,
'member': CDesc,
'macro': CDesc,
'type': CDesc,
'var': CDesc,
}
domains['py'] = PythonDomain
domains['c'] = CDomain
+466 -1
View File
@@ -10,11 +10,476 @@
:license: BSD, see LICENSE for details.
"""
import re
from sphinx import addnodes
from sphinx.roles import XRefRole
from sphinx.directives import DescDirective
class Domain(object):
name = ''
directives = {}
roles = {}
label = ''
# REs for Python signatures
py_sig_re = re.compile(
r'''^ ([\w.]*\.)? # class name(s)
(\w+) \s* # thing name
(?: \((.*)\) # optional: arguments
(?:\s* -> \s* (.*))? # return annotation
)? $ # and nothing more
''', re.VERBOSE)
py_paramlist_re = re.compile(r'([\[\],])') # split at '[', ']' and ','
class PythonDesc(DescDirective):
"""
Description of a general Python object.
"""
def get_signature_prefix(self, sig):
"""
May return a prefix to put before the object name in the signature.
"""
return ''
def needs_arglist(self):
"""
May return true if an empty argument list is to be generated even if
the document contains none.
"""
return False
def parse_signature(self, sig, signode):
"""
Transform a Python signature into RST nodes.
Returns (fully qualified name of the thing, classname if any).
If inside a class, the current class name is handled intelligently:
* it is stripped from the displayed name if present
* it is added to the full name (return value) if not present
"""
m = py_sig_re.match(sig)
if m is None:
raise ValueError
classname, name, arglist, retann = m.groups()
if self.env.currclass:
add_module = False
if classname and classname.startswith(self.env.currclass):
fullname = classname + name
# class name is given again in the signature
classname = classname[len(self.env.currclass):].lstrip('.')
elif classname:
# class name is given in the signature, but different
# (shouldn't happen)
fullname = self.env.currclass + '.' + classname + name
else:
# class name is not given in the signature
fullname = self.env.currclass + '.' + name
else:
add_module = True
fullname = classname and classname + name or name
prefix = self.get_signature_prefix(sig)
if prefix:
signode += addnodes.desc_annotation(prefix, prefix)
if classname:
signode += addnodes.desc_addname(classname, classname)
# exceptions are a special case, since they are documented in the
# 'exceptions' module.
elif add_module and self.env.config.add_module_names:
modname = self.options.get('module', self.env.currmodule)
if modname and modname != 'exceptions':
nodetext = modname + '.'
signode += addnodes.desc_addname(nodetext, nodetext)
signode += addnodes.desc_name(name, name)
if not arglist:
if self.needs_arglist():
# for callables, add an empty parameter list
signode += addnodes.desc_parameterlist()
if retann:
signode += addnodes.desc_returns(retann, retann)
return fullname, classname
signode += addnodes.desc_parameterlist()
stack = [signode[-1]]
for token in py_paramlist_re.split(arglist):
if token == '[':
opt = addnodes.desc_optional()
stack[-1] += opt
stack.append(opt)
elif token == ']':
try:
stack.pop()
except IndexError:
raise ValueError
elif not token or token == ',' or token.isspace():
pass
else:
token = token.strip()
stack[-1] += addnodes.desc_parameter(token, token)
if len(stack) != 1:
raise ValueError
if retann:
signode += addnodes.desc_returns(retann, retann)
return fullname, classname
def get_index_text(self, modname, name):
"""
Return the text for the index entry of the object.
"""
raise NotImplementedError('must be implemented in subclasses')
def add_target_and_index(self, name_cls, sig, signode):
modname = self.options.get('module', self.env.currmodule)
fullname = (modname and modname + '.' or '') + name_cls[0]
# note target
if fullname not in self.state.document.ids:
signode['names'].append(fullname)
signode['ids'].append(fullname)
signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
self.env.note_descref(fullname, self.desctype, self.lineno)
indextext = self.get_index_text(modname, name_cls)
if indextext:
self.indexnode['entries'].append(('single', indextext,
fullname, fullname))
def before_content(self):
# needed for automatic qualification of members (reset in subclasses)
self.clsname_set = False
def after_content(self):
if self.clsname_set:
self.env.currclass = None
class ModulelevelDesc(PythonDesc):
"""
Description of an object on module level (functions, data).
"""
def needs_arglist(self):
return self.desctype == 'function'
def get_index_text(self, modname, name_cls):
if self.desctype == 'function':
if not modname:
return _('%s() (built-in function)') % name_cls[0]
return _('%s() (in module %s)') % (name_cls[0], modname)
elif self.desctype == 'data':
if not modname:
return _('%s (built-in variable)') % name_cls[0]
return _('%s (in module %s)') % (name_cls[0], modname)
else:
return ''
class ClasslikeDesc(PythonDesc):
"""
Description of a class-like object (classes, interfaces, exceptions).
"""
def get_signature_prefix(self, sig):
return self.desctype + ' '
def get_index_text(self, modname, name_cls):
if self.desctype == 'class':
if not modname:
return _('%s (built-in class)') % name_cls[0]
return _('%s (class in %s)') % (name_cls[0], modname)
elif self.desctype == 'exception':
return name_cls[0]
else:
return ''
def before_content(self):
PythonDesc.before_content(self)
if self.names:
self.env.currclass = self.names[0][0]
self.clsname_set = True
class ClassmemberDesc(PythonDesc):
"""
Description of a class member (methods, attributes).
"""
def needs_arglist(self):
return self.desctype.endswith('method')
def get_signature_prefix(self, sig):
if self.desctype == 'staticmethod':
return 'static '
elif self.desctype == 'classmethod':
return 'classmethod '
return ''
def get_index_text(self, modname, name_cls):
name, cls = name_cls
add_modules = self.env.config.add_module_names
if self.desctype == 'method':
try:
clsname, methname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s() (in module %s)') % (name, modname)
else:
return '%s()' % name
if modname and add_modules:
return _('%s() (%s.%s method)') % (methname, modname, clsname)
else:
return _('%s() (%s method)') % (methname, clsname)
elif self.desctype == 'staticmethod':
try:
clsname, methname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s() (in module %s)') % (name, modname)
else:
return '%s()' % name
if modname and add_modules:
return _('%s() (%s.%s static method)') % (methname, modname,
clsname)
else:
return _('%s() (%s static method)') % (methname, clsname)
elif self.desctype == 'classmethod':
try:
clsname, methname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s() (in module %s)') % (name, modname)
else:
return '%s()' % name
if modname:
return _('%s() (%s.%s class method)') % (methname, modname,
clsname)
else:
return _('%s() (%s class method)') % (methname, clsname)
elif self.desctype == 'attribute':
try:
clsname, attrname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s (in module %s)') % (name, modname)
else:
return name
if modname and add_modules:
return _('%s (%s.%s attribute)') % (attrname, modname, clsname)
else:
return _('%s (%s attribute)') % (attrname, clsname)
else:
return ''
def before_content(self):
PythonDesc.before_content(self)
if self.names and self.names[-1][1] and not self.env.currclass:
self.env.currclass = self.names[-1][1].strip('.')
self.clsname_set = True
class PyXRefRole(XRefRole):
def process_link(self, env, pnode, has_explicit_title, title, target):
pnode['modname'] = env.currmodule
pnode['classname'] = env.currclass
if not has_explicit_title:
title = title.lstrip('.') # only has a meaning for the target
target = target.lstrip('~') # only has a meaning for the title
# if the first character is a tilde, don't display the module/class
# parts of the contents
if title[0:1] == '~':
title = title[1:]
dot = title.rfind('.')
if dot != -1:
title = title[dot+1:]
# if the first character is a dot, search more specific namespaces first
# else search builtins first
if target[0:1] == '.':
target = target[1:]
pnode['refspecific'] = True
return title, target
class PythonDomain(Domain):
"""Python language domain."""
name = 'py'
label = 'Python'
directives = {
'function': ModulelevelDesc,
'data': ModulelevelDesc,
'class': ClasslikeDesc,
'exception': ClasslikeDesc,
'method': ClassmemberDesc,
'classmethod': ClassmemberDesc,
'staticmethod': ClassmemberDesc,
'attribute': ClassmemberDesc,
}
roles = {
'data': PyXRefRole('py'),
'exc': PyXRefRole('py'),
'func': PyXRefRole('py', True),
'class': PyXRefRole('py'),
'const': PyXRefRole('py'),
'attr': PyXRefRole('py'),
'meth': PyXRefRole('py', True),
'mod': PyXRefRole('py'),
'obj': PyXRefRole('py'),
}
# 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(
'', reftype='ctype', reftarget=part,
modname=None, classname=None)
pnode += tnode
node += pnode
else:
node += tnode
def parse_signature(self, sig, signode):
"""Transform a C (or C++) signature into RST nodes."""
# first try the function pointer signature regex, it's more specific
m = c_funcptr_sig_re.match(sig)
if m is None:
m = c_sig_re.match(sig)
if m is None:
raise ValueError('no match')
rettype, name, arglist, const = m.groups()
signode += addnodes.desc_type('', '')
self._parse_type(signode[-1], rettype)
try:
classname, funcname = name.split('::', 1)
classname += '::'
signode += addnodes.desc_addname(classname, classname)
signode += addnodes.desc_name(funcname, funcname)
# name (the full name) is still both parts
except ValueError:
signode += addnodes.desc_name(name, name)
# clean up parentheses from canonical name
m = c_funcptr_name_re.match(name)
if m:
name = m.group(1)
if not arglist:
if self.desctype == 'cfunction':
# for functions, add an empty parameter list
signode += addnodes.desc_parameterlist()
if const:
signode += addnodes.desc_addname(const, const)
return name
paramlist = addnodes.desc_parameterlist()
arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup
# this messes up function pointer types, but not too badly ;)
args = arglist.split(',')
for arg in args:
arg = arg.strip()
param = addnodes.desc_parameter('', '', noemph=True)
try:
ctype, argname = arg.rsplit(' ', 1)
except ValueError:
# no argument name given, only the type
self._parse_type(param, arg)
else:
self._parse_type(param, ctype)
param += nodes.emphasis(' '+argname, ' '+argname)
paramlist += param
signode += paramlist
if const:
signode += addnodes.desc_addname(const, const)
return name
def get_index_text(self, name):
if self.desctype == 'cfunction':
return _('%s (C function)') % name
elif self.desctype == 'cmember':
return _('%s (C member)') % name
elif self.desctype == 'cmacro':
return _('%s (C macro)') % name
elif self.desctype == 'ctype':
return _('%s (C type)') % name
elif self.desctype == 'cvar':
return _('%s (C variable)') % name
else:
return ''
def add_target_and_index(self, name, sig, signode):
# note target
if name not in self.state.document.ids:
signode['names'].append(name)
signode['ids'].append(name)
signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
self.env.note_descref(name, self.desctype, self.lineno)
indextext = self.get_index_text(name)
if indextext:
self.indexnode['entries'].append(('single', indextext, name, name))
class CDomain(Domain):
"""C language domain."""
name = 'c'
label = 'C'
directives = {
'function': CDesc,
'member': CDesc,
'macro': CDesc,
'type': CDesc,
'var': CDesc,
}
roles = {
'member': XRefRole('c'),
'macro': XRefRole('c'),
'func' : XRefRole('c', True),
'data': XRefRole('c'),
'type': XRefRole('c'),
}
# this contains all registered domains
domains = {}
domains = {
'py': PythonDomain,
'c': CDomain,
}
+16 -64
View File
@@ -48,6 +48,7 @@ from sphinx.errors import SphinxError
from sphinx.domains import domains
from sphinx.directives import additional_xref_types
orig_role_function = roles.role
orig_directive_function = directives.directive
default_settings = {
@@ -617,6 +618,21 @@ class BuildEnvironment:
document)
directives.directive = directive
def role(role_name, language_module, lineno, reporter):
if ':' in role_name:
domain_name, role_name = role_name.split(':', 1)
if domain_name in domains:
domain = domains[domain_name]
if role_name in domain.roles:
return domain.roles[role_name], []
elif self.default_domain is not None:
role = self.default_domain.roles.get(role_name)
if role is not None:
return role, []
return orig_role_function(role_name, language_module,
lineno, reporter)
roles.role = role
# publish manually
pub = Publisher(reader=SphinxStandaloneReader(),
writer=SphinxDummyWriter(),
@@ -1625,67 +1641,3 @@ class BuildEnvironment:
if newname is None:
return None, None
return newname, self.descrefs[newname]
def find_keyword(self, keyword, avoid_fuzzy=False, cutoff=0.6, n=20):
"""
Find keyword matches for a keyword. If there's an exact match,
just return it, else return a list of fuzzy matches if avoid_fuzzy
isn't True.
Keywords searched are: first modules, then descrefs.
Returns: None if nothing found
(type, docname, anchorname) if exact match found
list of (quality, type, docname, anchorname, description)
if fuzzy
"""
if keyword in self.modules:
docname, title, system, deprecated = self.modules[keyword]
return 'module', docname, 'module-' + keyword
if keyword in self.descrefs:
docname, ref_type = self.descrefs[keyword]
return ref_type, docname, keyword
# special cases
if '.' not in keyword:
# exceptions are documented in the exceptions module
if 'exceptions.'+keyword in self.descrefs:
docname, ref_type = self.descrefs['exceptions.'+keyword]
return ref_type, docname, 'exceptions.'+keyword
# special methods are documented as object methods
if 'object.'+keyword in self.descrefs:
docname, ref_type = self.descrefs['object.'+keyword]
return ref_type, docname, 'object.'+keyword
if avoid_fuzzy:
return
# find fuzzy matches
s = difflib.SequenceMatcher()
s.set_seq2(keyword.lower())
def possibilities():
for title, (fn, desc, _, _) in self.modules.iteritems():
yield ('module', fn, 'module-'+title, desc)
for title, (fn, desctype) in self.descrefs.iteritems():
yield (desctype, fn, title, '')
def dotsearch(string):
parts = string.lower().split('.')
for idx in xrange(0, len(parts)):
yield '.'.join(parts[idx:])
result = []
for type, docname, title, desc in possibilities():
best_res = 0
for part in dotsearch(title):
s.set_seq1(part)
if s.real_quick_ratio() >= cutoff and \
s.quick_ratio() >= cutoff and \
s.ratio() >= cutoff and \
s.ratio() > best_res:
best_res = s.ratio()
if best_res:
result.append((best_res, type, docname, title, desc))
return heapq.nlargest(n, result)
+81 -129
View File
@@ -94,36 +94,6 @@ roles.register_local_role('pep', indexmarkup_role)
roles.register_local_role('rfc', indexmarkup_role)
def make_xref_role(link_func, nodeclass=None, innernodeclass=None):
if nodeclass is None:
nodeclass = addnodes.pending_xref
if innernodeclass is None:
innernodeclass = nodes.literal
def func(typ, rawtext, text, lineno, inliner, options={}, content=[]):
env = inliner.document.settings.env
if not typ:
typ = env.config.default_role
else:
typ = typ.lower()
text = utils.unescape(text)
# if the first character is a bang, don't cross-reference at all
if text[0:1] == '!':
text = _fix_parens(typ, text[1:], env)
return [innernodeclass(rawtext, text, classes=['xref'])], []
# we want a cross-reference, create the reference node
pnode = nodeclass(rawtext, reftype=typ, refcaption=False,
modname=env.currmodule, classname=env.currclass)
# we may need the line number for warnings
pnode.line = lineno
has_explicit_title, title, target = split_explicit_title(text)
target, title = link_func(env, pnode, has_explicit_title, title, target)
pnode['reftarget'] = target
pnode += innernodeclass(rawtext, title, classes=['xref'])
return [pnode], []
return func
def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
return [nodes.emphasis(
rawtext,
@@ -161,112 +131,92 @@ def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
return [addnodes.abbreviation(abbr, abbr, explanation=expl)], []
def normalize_func_parens(env, has_explicit_title, title, target):
if has_explicit_title:
if title.endswith('()'):
# remove parentheses
title = title[:-2]
if env.config.add_function_parentheses:
# add them back to all occurrences if configured
title += '()'
# remove parentheses from the target too
if target.endswith('()'):
target = target[:-2]
return target, title
# -- generic cross-reference roles ---------------------------------------------
class XRefRole(object):
nodeclass = addnodes.pending_xref
innernodeclass = nodes.literal
def __init__(self, domain_name, fix_parens=False, lowercase=False,
nodeclass=None, innernodeclass=None):
self.domain_name = domain_name
self.fix_parens = fix_parens
if nodeclass is not None:
self.nodeclass = nodeclass
if innernodeclass is not None:
self.innernodeclass = innernodeclass
def process_link(self, env, pnode, has_explicit_title, title, target):
return title, ws_re.sub(' ', target)
def normalize_func_parens(self, env, has_explicit_title, title, target):
if not has_explicit_title:
if title.endswith('()'):
# remove parentheses
title = title[:-2]
if env.config.add_function_parentheses:
# add them back to all occurrences if configured
title += '()'
# remove parentheses from the target too
if target.endswith('()'):
target = target[:-2]
return title, target
def __call__(self, typ, rawtext, text, lineno, inliner, options={}, content=[]):
env = inliner.document.settings.env
if not typ:
typ = env.config.default_role
else:
typ = typ.lower()
if ":" in typ:
domain, role = typ.split(":", 1)
else:
domain, role = self.domain_name, typ
text = utils.unescape(text)
# if the first character is a bang, don't cross-reference at all
if text[0:1] == '!':
if self.fix_parens:
text, _ = self.normalize_func_parens(env, False, text[1:], "")
return [self.innernodeclass(rawtext, text, classes=['xref'])], []
# split title and target in role content
has_explicit_title, title, target = split_explicit_title(text)
# we want a cross-reference, create the reference node
pnode = self.nodeclass(rawtext, reftype=role, refdomain=domain,
refcaption=has_explicit_title)
# we may need the line number for warnings
pnode.line = lineno
title, target = self.process_link(env, pnode, has_explicit_title, title, target)
pnode['reftarget'] = target
pnode += self.innernodeclass(rawtext, title, classes=['xref'])
return [pnode], []
def generic_link_func(env, pnode, has_explicit_title, title, target):
if has_explicit_title:
pnode['refcaption'] = True
return target, title
class OptionXRefRole(XRefRole):
innernodeclass = addnodes.literal_emphasis
def pyref_link_func(env, pnode, has_explicit_title, title, target):
if has_explicit_title:
pnode['refcaption'] = True
# fix-up parentheses in link title
else:
title = title.lstrip('.') # only has a meaning for the target
target = target.lstrip('~') # only has a meaning for the title
# if the first character is a tilde, don't display the module/class
# parts of the contents
if title[0:1] == '~':
title = title[1:]
dot = title.rfind('.')
if dot != -1:
title = title[dot+1:]
# if the first character is a dot, search more specific namespaces first
# else search builtins first
if target[0:1] == '.':
target = target[1:]
pnode['refspecific'] = True
return target, title
def pyref_callable_link_func(env, pnode, has_explicit_title, title, target):
target, title = pyref_link_func(env, pnode, has_explicit_title, title, target)
target, title = normalize_func_parens(env, has_explicit_title, title, target)
return target, title
def option_link_func(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)
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)
target = target.strip()
elif ' ' in target:
program, target = re.split(' (?=-|--|/)', target, 1)
program = ws_re.sub('-', program)
pnode['refprogram'] = program
return target, title
pnode['refprogram'] = program
return title, target
def simple_link_func(env, pnode, has_explicit_title, title, target):
# normalize all whitespace to avoid referencing problems
target = ws_re.sub(' ', target)
return target, title
def lowercase_link_func(env, pnode, has_explicit_title, title, target):
target, title = simple_link_func(env, pnode, has_explicit_title, title, target)
return target.lower(), title
def cfunc_link_func(env, pnode, has_explicit_title, title, target):
return normalize_func_parens(env, has_explicit_title, title, target)
generic_pyref_role = make_xref_role(pyref_link_func)
callable_pyref_role = make_xref_role(pyref_callable_link_func)
simple_xref_role = make_xref_role(simple_link_func)
specific_docroles = {
'data': generic_pyref_role,
'exc': generic_pyref_role,
'func': callable_pyref_role,
'class': generic_pyref_role,
'const': generic_pyref_role,
'attr': generic_pyref_role,
'meth': callable_pyref_role,
'mod': generic_pyref_role,
'obj': generic_pyref_role,
'keyword': simple_xref_role,
'ref': make_xref_role(lowercase_link_func, None, nodes.emphasis),
'token': simple_xref_role,
'term': make_xref_role(lowercase_link_func, None, nodes.emphasis),
'option': make_xref_role(option_link_func, None, addnodes.literal_emphasis),
'doc': simple_xref_role,
'download': make_xref_role(simple_link_func, addnodes.download_reference),
'cmember': simple_xref_role,
'cmacro': simple_xref_role,
'cfunc' : make_xref_role(cfunc_link_func),
'cdata': simple_xref_role,
'ctype': simple_xref_role,
'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),
'menuselection': menusel_role,
'file': emph_literal_role,
@@ -276,3 +226,5 @@ specific_docroles = {
for rolename, func in specific_docroles.iteritems():
roles.register_local_role(rolename, func)
+3 -1
View File
@@ -182,7 +182,8 @@ Testing öäü...
Object markup
-------------
:cfunc:`CFunction`.
:c:func:`CFunction`. :c:func:`!malloc`.
Only directive
--------------
@@ -207,3 +208,4 @@ Only directive
.. rubric:: Footnotes
.. [#] Like footnotes.