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.py123
1 files changed, 61 insertions, 62 deletions
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
index d167d39e1..24be3c6e9 100644
--- a/sphinx/domains/python.py
+++ b/sphinx/domains/python.py
@@ -80,42 +80,53 @@ class ModuleEntry(NamedTuple):
deprecated: bool
-def type_to_xref(target: str, env: BuildEnvironment = None, suppress_prefix: bool = False
- ) -> addnodes.pending_xref:
- """Convert a type string to a cross reference node."""
- if target == 'None':
+def parse_reftarget(reftarget: str, suppress_prefix: bool = False
+ ) -> Tuple[str, str, str, bool]:
+ """Parse a type string and return (reftype, reftarget, title, refspecific flag)"""
+ refspecific = False
+ if reftarget.startswith('.'):
+ reftarget = reftarget[1:]
+ title = reftarget
+ refspecific = True
+ elif reftarget.startswith('~'):
+ reftarget = reftarget[1:]
+ title = reftarget.split('.')[-1]
+ elif suppress_prefix:
+ title = reftarget.split('.')[-1]
+ elif reftarget.startswith('typing.'):
+ title = reftarget[7:]
+ else:
+ title = reftarget
+
+ if reftarget == 'None' or reftarget.startswith('typing.'):
+ # typing module provides non-class types. Obj reference is good to refer them.
reftype = 'obj'
else:
reftype = 'class'
+ return reftype, reftarget, title, refspecific
+
+
+def type_to_xref(target: str, env: BuildEnvironment = None, suppress_prefix: bool = False
+ ) -> addnodes.pending_xref:
+ """Convert a type string to a cross reference node."""
if env:
kwargs = {'py:module': env.ref_context.get('py:module'),
'py:class': env.ref_context.get('py:class')}
else:
kwargs = {}
- refspecific = False
- if target.startswith('.'):
- target = target[1:]
- text = target
- refspecific = True
- elif target.startswith('~'):
- target = target[1:]
- text = target.split('.')[-1]
- elif suppress_prefix:
- text = target.split('.')[-1]
- else:
- text = target
+ reftype, target, title, refspecific = parse_reftarget(target, suppress_prefix)
if env.config.python_use_unqualified_type_names:
# Note: It would be better to use qualname to describe the object to support support
# nested classes. But python domain can't access the real python object because this
# module should work not-dynamically.
- shortname = text.split('.')[-1]
+ shortname = title.split('.')[-1]
contnodes: List[Node] = [pending_xref_condition('', shortname, condition='resolved'),
- pending_xref_condition('', text, condition='*')]
+ pending_xref_condition('', title, condition='*')]
else:
- contnodes = [nodes.Text(text)]
+ contnodes = [nodes.Text(title)]
return pending_xref('', *contnodes,
refdomain='py', reftype=reftype, reftarget=target,
@@ -203,10 +214,16 @@ def _parse_annotation(annotation: str, env: BuildEnvironment) -> List[Node]:
return result
else:
if sys.version_info < (3, 8):
- if isinstance(node, ast.Ellipsis):
+ if isinstance(node, ast.Bytes):
+ return [addnodes.desc_sig_literal_string('', repr(node.s))]
+ elif isinstance(node, ast.Ellipsis):
return [addnodes.desc_sig_punctuation('', "...")]
elif isinstance(node, ast.NameConstant):
return [nodes.Text(node.value)]
+ elif isinstance(node, ast.Num):
+ return [addnodes.desc_sig_literal_string('', repr(node.n))]
+ elif isinstance(node, ast.Str):
+ return [addnodes.desc_sig_literal_string('', repr(node.s))]
raise SyntaxError # unsupported syntax
@@ -341,27 +358,27 @@ class PyXrefMixin:
result = super().make_xref(rolename, domain, target, # type: ignore
innernode, contnode,
env, inliner=None, location=None)
- result['refspecific'] = True
- result['py:module'] = env.ref_context.get('py:module')
- result['py:class'] = env.ref_context.get('py:class')
- if target.startswith(('.', '~')):
- prefix, result['reftarget'] = target[0], target[1:]
- if prefix == '.':
- text = target[1:]
- elif prefix == '~':
- text = target.split('.')[-1]
- for node in list(result.traverse(nodes.Text)):
- node.parent[node.parent.index(node)] = nodes.Text(text)
- break
- elif isinstance(result, pending_xref) and env.config.python_use_unqualified_type_names:
- children = result.children
- result.clear()
-
- shortname = target.split('.')[-1]
- textnode = innernode('', shortname)
- contnodes = [pending_xref_condition('', '', textnode, condition='resolved'),
- pending_xref_condition('', '', *children, condition='*')]
- result.extend(contnodes)
+ if isinstance(result, pending_xref):
+ result['refspecific'] = True
+ result['py:module'] = env.ref_context.get('py:module')
+ result['py:class'] = env.ref_context.get('py:class')
+
+ reftype, reftarget, reftitle, _ = parse_reftarget(target)
+ if reftarget != reftitle:
+ result['reftype'] = reftype
+ result['reftarget'] = reftarget
+
+ result.clear()
+ result += innernode(reftitle, reftitle)
+ elif env.config.python_use_unqualified_type_names:
+ children = result.children
+ result.clear()
+
+ shortname = target.split('.')[-1]
+ textnode = innernode('', shortname)
+ contnodes = [pending_xref_condition('', '', textnode, condition='resolved'),
+ pending_xref_condition('', '', *children, condition='*')]
+ result.extend(contnodes)
return result
@@ -394,16 +411,7 @@ class PyXrefMixin:
class PyField(PyXrefMixin, Field):
- def make_xref(self, rolename: str, domain: str, target: str,
- innernode: Type[TextlikeNode] = nodes.emphasis,
- contnode: Node = None, env: BuildEnvironment = None,
- inliner: Inliner = None, location: Node = None) -> Node:
- if rolename == 'class' and target == 'None':
- # None is not a type, so use obj role instead.
- rolename = 'obj'
-
- return super().make_xref(rolename, domain, target, innernode, contnode,
- env, inliner, location)
+ pass
class PyGroupedField(PyXrefMixin, GroupedField):
@@ -411,16 +419,7 @@ class PyGroupedField(PyXrefMixin, GroupedField):
class PyTypedField(PyXrefMixin, TypedField):
- def make_xref(self, rolename: str, domain: str, target: str,
- innernode: Type[TextlikeNode] = nodes.emphasis,
- contnode: Node = None, env: BuildEnvironment = None,
- inliner: Inliner = None, location: Node = None) -> Node:
- if rolename == 'class' and target == 'None':
- # None is not a type, so use obj role instead.
- rolename = 'obj'
-
- return super().make_xref(rolename, domain, target, innernode, contnode,
- env, inliner, location)
+ pass
class PyObject(ObjectDescription[Tuple[str, str]]):
@@ -1454,7 +1453,7 @@ def builtin_resolver(app: Sphinx, env: BuildEnvironment,
return None
elif node.get('reftype') in ('class', 'obj') and node.get('reftarget') == 'None':
return contnode
- elif node.get('reftype') in ('class', 'exc'):
+ elif node.get('reftype') in ('class', 'obj', 'exc'):
reftarget = node.get('reftarget')
if inspect.isclass(getattr(builtins, reftarget, None)):
# built-in class