summaryrefslogtreecommitdiff
path: root/sphinx/domains/python.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/domains/python.py')
-rw-r--r--sphinx/domains/python.py147
1 files changed, 104 insertions, 43 deletions
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
index 792cffd8..692010c1 100644
--- a/sphinx/domains/python.py
+++ b/sphinx/domains/python.py
@@ -11,6 +11,7 @@
import re
+from six import iteritems
from docutils import nodes
from docutils.parsers.rst import directives
@@ -81,6 +82,32 @@ def _pseudo_parse_arglist(signode, arglist):
signode += paramlist
+# This override allows our inline type specifiers to behave like :class: link
+# when it comes to handling "." and "~" prefixes.
+class PyXrefMixin(object):
+ def make_xref(self, rolename, domain, target, innernode=nodes.emphasis,
+ contnode=None):
+ result = super(PyXrefMixin, self).make_xref(rolename, domain, target,
+ innernode, contnode)
+ result['refspecific'] = True
+ if target.startswith('.'):
+ result['reftarget'] = target[1:]
+ result[0][0] = nodes.Text(target[1:])
+ if target.startswith('~'):
+ result['reftarget'] = target[1:]
+ title = target.split('.')[-1]
+ result[0][0] = nodes.Text(title)
+ return result
+
+
+class PyField(PyXrefMixin, Field):
+ pass
+
+
+class PyTypedField(PyXrefMixin, TypedField):
+ pass
+
+
class PyObject(ObjectDescription):
"""
Description of a general Python object.
@@ -92,22 +119,22 @@ class PyObject(ObjectDescription):
}
doc_field_types = [
- TypedField('parameter', label=l_('Parameters'),
- names=('param', 'parameter', 'arg', 'argument',
- 'keyword', 'kwarg', 'kwparam'),
- typerolename='obj', typenames=('paramtype', 'type'),
- can_collapse=True),
- TypedField('variable', label=l_('Variables'), rolename='obj',
- names=('var', 'ivar', 'cvar'),
- typerolename='obj', typenames=('vartype',),
- can_collapse=True),
+ PyTypedField('parameter', label=l_('Parameters'),
+ names=('param', 'parameter', 'arg', 'argument',
+ 'keyword', 'kwarg', 'kwparam'),
+ typerolename='obj', typenames=('paramtype', 'type'),
+ can_collapse=True),
+ PyTypedField('variable', label=l_('Variables'), rolename='obj',
+ names=('var', 'ivar', 'cvar'),
+ typerolename='obj', typenames=('vartype',),
+ can_collapse=True),
GroupedField('exceptions', label=l_('Raises'), rolename='exc',
names=('raises', 'raise', 'exception', 'except'),
can_collapse=True),
Field('returnvalue', label=l_('Returns'), has_arg=False,
names=('returns', 'return')),
- Field('returntype', label=l_('Return type'), has_arg=False,
- names=('rtype',)),
+ PyField('returntype', label=l_('Return type'), has_arg=False,
+ names=('rtype',), bodyrolename='obj'),
]
def get_signature_prefix(self, sig):
@@ -138,8 +165,8 @@ class PyObject(ObjectDescription):
# determine module and class name (if applicable), as well as full name
modname = self.options.get(
- 'module', self.env.temp_data.get('py:module'))
- classname = self.env.temp_data.get('py:class')
+ 'module', self.env.ref_context.get('py:module'))
+ classname = self.env.ref_context.get('py:class')
if classname:
add_module = False
if name_prefix and name_prefix.startswith(classname):
@@ -176,7 +203,7 @@ class PyObject(ObjectDescription):
# 'exceptions' module.
elif add_module and self.env.config.add_module_names:
modname = self.options.get(
- 'module', self.env.temp_data.get('py:module'))
+ 'module', self.env.ref_context.get('py:module'))
if modname and modname != 'exceptions':
nodetext = modname + '.'
signode += addnodes.desc_addname(nodetext, nodetext)
@@ -207,7 +234,7 @@ class PyObject(ObjectDescription):
def add_target_and_index(self, name_cls, sig, signode):
modname = self.options.get(
- 'module', self.env.temp_data.get('py:module'))
+ 'module', self.env.ref_context.get('py:module'))
fullname = (modname and modname + '.' or '') + name_cls[0]
# note target
if fullname not in self.state.document.ids:
@@ -236,7 +263,7 @@ class PyObject(ObjectDescription):
def after_content(self):
if self.clsname_set:
- self.env.temp_data['py:class'] = None
+ self.env.ref_context.pop('py:class', None)
class PyModulelevel(PyObject):
@@ -281,7 +308,7 @@ class PyClasslike(PyObject):
def before_content(self):
PyObject.before_content(self)
if self.names:
- self.env.temp_data['py:class'] = self.names[0][0]
+ self.env.ref_context['py:class'] = self.names[0][0]
self.clsname_set = True
@@ -359,8 +386,8 @@ class PyClassmember(PyObject):
def before_content(self):
PyObject.before_content(self)
lastname = self.names and self.names[-1][1]
- if lastname and not self.env.temp_data.get('py:class'):
- self.env.temp_data['py:class'] = lastname.strip('.')
+ if lastname and not self.env.ref_context.get('py:class'):
+ self.env.ref_context['py:class'] = lastname.strip('.')
self.clsname_set = True
@@ -416,7 +443,7 @@ class PyModule(Directive):
env = self.state.document.settings.env
modname = self.arguments[0].strip()
noindex = 'noindex' in self.options
- env.temp_data['py:module'] = modname
+ env.ref_context['py:module'] = modname
ret = []
if not noindex:
env.domaindata['py']['modules'][modname] = \
@@ -454,16 +481,16 @@ class PyCurrentModule(Directive):
env = self.state.document.settings.env
modname = self.arguments[0].strip()
if modname == 'None':
- env.temp_data['py:module'] = None
+ env.ref_context.pop('py:module', None)
else:
- env.temp_data['py:module'] = modname
+ env.ref_context['py:module'] = modname
return []
class PyXRefRole(XRefRole):
def process_link(self, env, refnode, has_explicit_title, title, target):
- refnode['py:module'] = env.temp_data.get('py:module')
- refnode['py:class'] = env.temp_data.get('py:class')
+ refnode['py:module'] = env.ref_context.get('py:module')
+ refnode['py:class'] = env.ref_context.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
@@ -497,7 +524,7 @@ class PythonModuleIndex(Index):
ignores = self.domain.env.config['modindex_common_prefix']
ignores = sorted(ignores, key=len, reverse=True)
# list of all modules, sorted by module name
- modules = sorted(self.domain.data['modules'].iteritems(),
+ modules = sorted(iteritems(self.domain.data['modules']),
key=lambda x: x[0].lower())
# sort out collapsable modules
prev_modname = ''
@@ -547,7 +574,7 @@ class PythonModuleIndex(Index):
collapse = len(modules) - num_toplevels < num_toplevels
# sort by first letter
- content = sorted(content.iteritems())
+ content = sorted(iteritems(content))
return content, collapse
@@ -602,13 +629,22 @@ class PythonDomain(Domain):
]
def clear_doc(self, docname):
- for fullname, (fn, _) in self.data['objects'].items():
+ for fullname, (fn, _) in list(self.data['objects'].items()):
if fn == docname:
del self.data['objects'][fullname]
- for modname, (fn, _, _, _) in self.data['modules'].items():
+ for modname, (fn, _, _, _) in list(self.data['modules'].items()):
if fn == docname:
del self.data['modules'][modname]
+ def merge_domaindata(self, docnames, otherdata):
+ # XXX check duplicates?
+ for fullname, (fn, objtype) in otherdata['objects'].items():
+ if fn in docnames:
+ self.data['objects'][fullname] = (fn, objtype)
+ for modname, data in otherdata['modules'].items():
+ if data[0] in docnames:
+ self.data['modules'][modname] = data
+
def find_obj(self, env, modname, classname, name, type, searchmode=0):
"""Find a Python object for "name", perhaps using the given module
and/or classname. Returns a list of (name, object entry) tuples.
@@ -625,7 +661,10 @@ class PythonDomain(Domain):
newname = None
if searchmode == 1:
- objtypes = self.objtypes_for_role(type)
+ if type is None:
+ objtypes = list(self.object_types)
+ else:
+ objtypes = self.objtypes_for_role(type)
if objtypes is not None:
if modname and classname:
fullname = modname + '.' + classname + '.' + name
@@ -686,25 +725,47 @@ class PythonDomain(Domain):
name, obj = matches[0]
if obj[1] == 'module':
- # get additional info for modules
- docname, synopsis, platform, deprecated = self.data['modules'][name]
- assert docname == obj[0]
- title = name
- if synopsis:
- title += ': ' + synopsis
- if deprecated:
- title += _(' (deprecated)')
- if platform:
- title += ' (' + platform + ')'
- return make_refnode(builder, fromdocname, docname,
- 'module-' + name, contnode, title)
+ return self._make_module_refnode(builder, fromdocname, name,
+ contnode)
else:
return make_refnode(builder, fromdocname, obj[0], name,
contnode, name)
+ def resolve_any_xref(self, env, fromdocname, builder, target,
+ node, contnode):
+ modname = node.get('py:module')
+ clsname = node.get('py:class')
+ results = []
+
+ # always search in "refspecific" mode with the :any: role
+ matches = self.find_obj(env, modname, clsname, target, None, 1)
+ for name, obj in matches:
+ if obj[1] == 'module':
+ results.append(('py:mod',
+ self._make_module_refnode(builder, fromdocname,
+ name, contnode)))
+ else:
+ results.append(('py:' + self.role_for_objtype(obj[1]),
+ make_refnode(builder, fromdocname, obj[0], name,
+ contnode, name)))
+ return results
+
+ def _make_module_refnode(self, builder, fromdocname, name, contnode):
+ # get additional info for modules
+ docname, synopsis, platform, deprecated = self.data['modules'][name]
+ title = name
+ if synopsis:
+ title += ': ' + synopsis
+ if deprecated:
+ title += _(' (deprecated)')
+ if platform:
+ title += ' (' + platform + ')'
+ return make_refnode(builder, fromdocname, docname,
+ 'module-' + name, contnode, title)
+
def get_objects(self):
- for modname, info in self.data['modules'].iteritems():
+ for modname, info in iteritems(self.data['modules']):
yield (modname, modname, 'module', info[0], 'module-' + modname, 0)
- for refname, (docname, type) in self.data['objects'].iteritems():
+ for refname, (docname, type) in iteritems(self.data['objects']):
if type != 'module': # modules are already handled
yield (refname, refname, type, docname, refname, 1)