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.py188
1 files changed, 40 insertions, 148 deletions
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
index 79d7e4f46..0fc5e9961 100644
--- a/sphinx/domains/python.py
+++ b/sphinx/domains/python.py
@@ -15,7 +15,7 @@ import sys
import typing
import warnings
from inspect import Parameter
-from typing import Any, Dict, Iterable, Iterator, List, NamedTuple, Tuple, cast
+from typing import Any, Dict, Iterable, Iterator, List, NamedTuple, Tuple, Type, cast
from docutils import nodes
from docutils.nodes import Element, Node
@@ -25,7 +25,7 @@ from sphinx import addnodes
from sphinx.addnodes import desc_signature, pending_xref
from sphinx.application import Sphinx
from sphinx.builders import Builder
-from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
+from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, Index, IndexEntry, ObjType
from sphinx.environment import BuildEnvironment
@@ -40,10 +40,6 @@ from sphinx.util.inspect import signature_from_str
from sphinx.util.nodes import make_id, make_refnode
from sphinx.util.typing import TextlikeNode
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
-
logger = logging.getLogger(__name__)
@@ -68,14 +64,20 @@ pairindextypes = {
'builtin': _('built-in function'),
}
-ObjectEntry = NamedTuple('ObjectEntry', [('docname', str),
- ('node_id', str),
- ('objtype', str)])
-ModuleEntry = NamedTuple('ModuleEntry', [('docname', str),
- ('node_id', str),
- ('synopsis', str),
- ('platform', str),
- ('deprecated', bool)])
+
+class ObjectEntry(NamedTuple):
+ docname: str
+ node_id: str
+ objtype: str
+ canonical: bool
+
+
+class ModuleEntry(NamedTuple):
+ docname: str
+ node_id: str
+ synopsis: str
+ platform: str
+ deprecated: bool
def type_to_xref(text: str, env: BuildEnvironment = None) -> addnodes.pending_xref:
@@ -100,6 +102,11 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
def unparse(node: ast.AST) -> List[Node]:
if isinstance(node, ast.Attribute):
return [nodes.Text("%s.%s" % (unparse(node.value)[0], node.attr))]
+ elif isinstance(node, ast.Constant): # type: ignore
+ if node.value is Ellipsis:
+ return [addnodes.desc_sig_punctuation('', "...")]
+ else:
+ return [nodes.Text(node.value)]
elif isinstance(node, ast.Expr):
return unparse(node.value)
elif isinstance(node, ast.Index):
@@ -135,13 +142,6 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
return result
else:
- if sys.version_info >= (3, 6):
- if isinstance(node, ast.Constant):
- if node.value is Ellipsis:
- return [addnodes.desc_sig_punctuation('', "...")]
- else:
- return [nodes.Text(node.value)]
-
if sys.version_info < (3, 8):
if isinstance(node, ast.Ellipsis):
return [addnodes.desc_sig_punctuation('', "...")]
@@ -267,7 +267,7 @@ def _pseudo_parse_arglist(signode: desc_signature, arglist: str) -> None:
# when it comes to handling "." and "~" prefixes.
class PyXrefMixin:
def make_xref(self, rolename: str, domain: str, target: str,
- innernode: "Type[TextlikeNode]" = nodes.emphasis,
+ innernode: Type[TextlikeNode] = nodes.emphasis,
contnode: Node = None, env: BuildEnvironment = None) -> Node:
result = super().make_xref(rolename, domain, target, # type: ignore
innernode, contnode, env)
@@ -284,7 +284,7 @@ class PyXrefMixin:
return result
def make_xrefs(self, rolename: str, domain: str, target: str,
- innernode: "Type[TextlikeNode]" = nodes.emphasis,
+ innernode: Type[TextlikeNode] = nodes.emphasis,
contnode: Node = None, env: BuildEnvironment = None) -> List[Node]:
delims = r'(\s*[\[\]\(\),](?:\s*or\s)?\s*|\s+or\s+)'
delims_re = re.compile(delims)
@@ -308,7 +308,7 @@ class PyXrefMixin:
class PyField(PyXrefMixin, Field):
def make_xref(self, rolename: str, domain: str, target: str,
- innernode: "Type[TextlikeNode]" = nodes.emphasis,
+ innernode: Type[TextlikeNode] = nodes.emphasis,
contnode: Node = None, env: BuildEnvironment = None) -> Node:
if rolename == 'class' and target == 'None':
# None is not a type, so use obj role instead.
@@ -323,7 +323,7 @@ class PyGroupedField(PyXrefMixin, GroupedField):
class PyTypedField(PyXrefMixin, TypedField):
def make_xref(self, rolename: str, domain: str, target: str,
- innernode: "Type[TextlikeNode]" = nodes.emphasis,
+ innernode: Type[TextlikeNode] = nodes.emphasis,
contnode: Node = None, env: BuildEnvironment = None) -> Node:
if rolename == 'class' and target == 'None':
# None is not a type, so use obj role instead.
@@ -343,6 +343,7 @@ class PyObject(ObjectDescription):
'noindex': directives.flag,
'noindexentry': directives.flag,
'module': directives.unchanged,
+ 'canonical': directives.unchanged,
'annotation': directives.unchanged,
}
@@ -484,6 +485,11 @@ class PyObject(ObjectDescription):
domain = cast(PythonDomain, self.env.get_domain('py'))
domain.note_object(fullname, self.objtype, node_id, location=signode)
+ canonical_name = self.options.get('canonical')
+ if canonical_name:
+ domain.note_object(canonical_name, self.objtype, node_id, canonical=True,
+ location=signode)
+
if 'noindexentry' not in self.options:
indextext = self.get_index_text(modname, name_cls)
if indextext:
@@ -548,40 +554,6 @@ class PyObject(ObjectDescription):
self.env.ref_context.pop('py:module')
-class PyModulelevel(PyObject):
- """
- Description of an object on module level (functions, data).
- """
-
- def run(self) -> List[Node]:
- for cls in self.__class__.__mro__:
- if cls.__name__ != 'DirectiveAdapter':
- warnings.warn('PyModulelevel is deprecated. '
- 'Please check the implementation of %s' % cls,
- RemovedInSphinx40Warning, stacklevel=2)
- break
- else:
- warnings.warn('PyModulelevel is deprecated',
- RemovedInSphinx40Warning, stacklevel=2)
-
- return super().run()
-
- def needs_arglist(self) -> bool:
- return self.objtype == 'function'
-
- def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
- if self.objtype == 'function':
- if not modname:
- return _('%s() (built-in function)') % name_cls[0]
- return _('%s() (in module %s)') % (name_cls[0], modname)
- elif self.objtype == 'data':
- if not modname:
- return _('%s (built-in variable)') % name_cls[0]
- return _('%s (in module %s)') % (name_cls[0], modname)
- else:
- return ''
-
-
class PyFunction(PyObject):
"""Description of a function."""
@@ -696,91 +668,6 @@ class PyClasslike(PyObject):
return ''
-class PyClassmember(PyObject):
- """
- Description of a class member (methods, attributes).
- """
-
- def run(self) -> List[Node]:
- for cls in self.__class__.__mro__:
- if cls.__name__ != 'DirectiveAdapter':
- warnings.warn('PyClassmember is deprecated. '
- 'Please check the implementation of %s' % cls,
- RemovedInSphinx40Warning, stacklevel=2)
- break
- else:
- warnings.warn('PyClassmember is deprecated',
- RemovedInSphinx40Warning, stacklevel=2)
-
- return super().run()
-
- def needs_arglist(self) -> bool:
- return self.objtype.endswith('method')
-
- def get_signature_prefix(self, sig: str) -> str:
- if self.objtype == 'staticmethod':
- return 'static '
- elif self.objtype == 'classmethod':
- return 'classmethod '
- return ''
-
- def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
- name, cls = name_cls
- add_modules = self.env.config.add_module_names
- if self.objtype == 'method':
- try:
- clsname, methname = name.rsplit('.', 1)
- except ValueError:
- if modname:
- return _('%s() (in module %s)') % (name, modname)
- else:
- return '%s()' % name
- if modname and add_modules:
- return _('%s() (%s.%s method)') % (methname, modname, clsname)
- else:
- return _('%s() (%s method)') % (methname, clsname)
- elif self.objtype == 'staticmethod':
- try:
- clsname, methname = name.rsplit('.', 1)
- except ValueError:
- if modname:
- return _('%s() (in module %s)') % (name, modname)
- else:
- return '%s()' % name
- if modname and add_modules:
- return _('%s() (%s.%s static method)') % (methname, modname,
- clsname)
- else:
- return _('%s() (%s static method)') % (methname, clsname)
- elif self.objtype == 'classmethod':
- try:
- clsname, methname = name.rsplit('.', 1)
- except ValueError:
- if modname:
- return _('%s() (in module %s)') % (name, modname)
- else:
- return '%s()' % name
- if modname:
- return _('%s() (%s.%s class method)') % (methname, modname,
- clsname)
- else:
- return _('%s() (%s class method)') % (methname, clsname)
- elif self.objtype == 'attribute':
- try:
- clsname, attrname = name.rsplit('.', 1)
- except ValueError:
- if modname:
- return _('%s (in module %s)') % (name, modname)
- else:
- return name
- if modname and add_modules:
- return _('%s (%s.%s attribute)') % (attrname, modname, clsname)
- else:
- return _('%s (%s attribute)') % (attrname, clsname)
- else:
- return ''
-
-
class PyMethod(PyObject):
"""Description of a method."""
@@ -1191,7 +1078,8 @@ class PythonDomain(Domain):
def objects(self) -> Dict[str, ObjectEntry]:
return self.data.setdefault('objects', {}) # fullname -> ObjectEntry
- def note_object(self, name: str, objtype: str, node_id: str, location: Any = None) -> None:
+ def note_object(self, name: str, objtype: str, node_id: str,
+ canonical: bool = False, location: Any = None) -> None:
"""Note a python object for cross reference.
.. versionadded:: 2.1
@@ -1201,7 +1089,7 @@ class PythonDomain(Domain):
logger.warning(__('duplicate object description of %s, '
'other instance in %s, use :noindex: for one of them'),
name, other.docname, location=location)
- self.objects[name] = ObjectEntry(self.env.docname, node_id, objtype)
+ self.objects[name] = ObjectEntry(self.env.docname, node_id, objtype, canonical)
@property
def modules(self) -> Dict[str, ModuleEntry]:
@@ -1354,7 +1242,11 @@ class PythonDomain(Domain):
yield (modname, modname, 'module', mod.docname, mod.node_id, 0)
for refname, obj in self.objects.items():
if obj.objtype != 'module': # modules are already handled
- yield (refname, refname, obj.objtype, obj.docname, obj.node_id, 1)
+ if obj.canonical:
+ # canonical names are not full-text searchable.
+ yield (refname, refname, obj.objtype, obj.docname, obj.node_id, -1)
+ else:
+ yield (refname, refname, obj.objtype, obj.docname, obj.node_id, 1)
def get_full_qualified_name(self, node: Element) -> str:
modname = node.get('py:module')
@@ -1400,7 +1292,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
return {
'version': 'builtin',
- 'env_version': 2,
+ 'env_version': 3,
'parallel_read_safe': True,
'parallel_write_safe': True,
}