Merge pull request #7363 from tk0miya/refactor_pydomain

refactor: py domain: Use namedtuple for python objects and modules
This commit is contained in:
Takeshi KOMIYA 2020-03-30 01:34:40 +09:00 committed by GitHub
commit 3a95fa7336
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -14,7 +14,7 @@ import re
import typing import typing
import warnings import warnings
from inspect import Parameter from inspect import Parameter
from typing import Any, Dict, Iterable, Iterator, List, Tuple from typing import Any, Dict, Iterable, Iterator, List, NamedTuple, Tuple
from typing import cast from typing import cast
from docutils import nodes from docutils import nodes
@ -67,6 +67,15 @@ pairindextypes = {
'builtin': _('built-in function'), '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)])
def _parse_annotation(annotation: str) -> List[Node]: def _parse_annotation(annotation: str) -> List[Node]:
"""Parse type annotation.""" """Parse type annotation."""
@ -1113,8 +1122,8 @@ class PythonDomain(Domain):
] ]
@property @property
def objects(self) -> Dict[str, Tuple[str, str, str]]: def objects(self) -> Dict[str, ObjectEntry]:
return self.data.setdefault('objects', {}) # fullname -> docname, node_id, objtype 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, location: Any = None) -> None:
"""Note a python object for cross reference. """Note a python object for cross reference.
@ -1122,15 +1131,15 @@ class PythonDomain(Domain):
.. versionadded:: 2.1 .. versionadded:: 2.1
""" """
if name in self.objects: if name in self.objects:
docname = self.objects[name][0] other = self.objects[name]
logger.warning(__('duplicate object description of %s, ' logger.warning(__('duplicate object description of %s, '
'other instance in %s, use :noindex: for one of them'), 'other instance in %s, use :noindex: for one of them'),
name, docname, location=location) name, other.docname, location=location)
self.objects[name] = (self.env.docname, node_id, objtype) self.objects[name] = ObjectEntry(self.env.docname, node_id, objtype)
@property @property
def modules(self) -> Dict[str, Tuple[str, str, str, str, bool]]: def modules(self) -> Dict[str, ModuleEntry]:
return self.data.setdefault('modules', {}) # modname -> docname, node_id, synopsis, platform, deprecated # NOQA return self.data.setdefault('modules', {}) # modname -> ModuleEntry
def note_module(self, name: str, node_id: str, synopsis: str, def note_module(self, name: str, node_id: str, synopsis: str,
platform: str, deprecated: bool) -> None: platform: str, deprecated: bool) -> None:
@ -1138,28 +1147,29 @@ class PythonDomain(Domain):
.. versionadded:: 2.1 .. versionadded:: 2.1
""" """
self.modules[name] = (self.env.docname, node_id, synopsis, platform, deprecated) self.modules[name] = ModuleEntry(self.env.docname, node_id,
synopsis, platform, deprecated)
def clear_doc(self, docname: str) -> None: def clear_doc(self, docname: str) -> None:
for fullname, (fn, _x, _x) in list(self.objects.items()): for fullname, obj in list(self.objects.items()):
if fn == docname: if obj.docname == docname:
del self.objects[fullname] del self.objects[fullname]
for modname, (fn, _x, _x, _x, _y) in list(self.modules.items()): for modname, mod in list(self.modules.items()):
if fn == docname: if mod.docname == docname:
del self.modules[modname] del self.modules[modname]
def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None: def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# XXX check duplicates? # XXX check duplicates?
for fullname, (fn, node_id, objtype) in otherdata['objects'].items(): for fullname, obj in otherdata['objects'].items():
if fn in docnames: if obj.docname in docnames:
self.objects[fullname] = (fn, node_id, objtype) self.objects[fullname] = obj
for modname, data in otherdata['modules'].items(): for modname, mod in otherdata['modules'].items():
if data[0] in docnames: if mod.docname in docnames:
self.modules[modname] = data self.modules[modname] = mod
def find_obj(self, env: BuildEnvironment, modname: str, classname: str, def find_obj(self, env: BuildEnvironment, modname: str, classname: str,
name: str, type: str, searchmode: int = 0 name: str, type: str, searchmode: int = 0
) -> List[Tuple[str, Tuple[str, str, str]]]: ) -> List[Tuple[str, ObjectEntry]]:
"""Find a Python object for "name", perhaps using the given module """Find a Python object for "name", perhaps using the given module
and/or classname. Returns a list of (name, object entry) tuples. and/or classname. Returns a list of (name, object entry) tuples.
""" """
@ -1170,7 +1180,7 @@ class PythonDomain(Domain):
if not name: if not name:
return [] return []
matches = [] # type: List[Tuple[str, Tuple[str, str, str]]] matches = [] # type: List[Tuple[str, ObjectEntry]]
newname = None newname = None
if searchmode == 1: if searchmode == 1:
@ -1181,20 +1191,20 @@ class PythonDomain(Domain):
if objtypes is not None: if objtypes is not None:
if modname and classname: if modname and classname:
fullname = modname + '.' + classname + '.' + name fullname = modname + '.' + classname + '.' + name
if fullname in self.objects and self.objects[fullname][2] in objtypes: if fullname in self.objects and self.objects[fullname].objtype in objtypes:
newname = fullname newname = fullname
if not newname: if not newname:
if modname and modname + '.' + name in self.objects and \ if modname and modname + '.' + name in self.objects and \
self.objects[modname + '.' + name][2] in objtypes: self.objects[modname + '.' + name].objtype in objtypes:
newname = modname + '.' + name newname = modname + '.' + name
elif name in self.objects and self.objects[name][2] in objtypes: elif name in self.objects and self.objects[name].objtype in objtypes:
newname = name newname = name
else: else:
# "fuzzy" searching mode # "fuzzy" searching mode
searchname = '.' + name searchname = '.' + name
matches = [(oname, self.objects[oname]) for oname in self.objects matches = [(oname, self.objects[oname]) for oname in self.objects
if oname.endswith(searchname) and if oname.endswith(searchname) and
self.objects[oname][2] in objtypes] self.objects[oname].objtype in objtypes]
else: else:
# NOTE: searching for exact match, object type is not considered # NOTE: searching for exact match, object type is not considered
if name in self.objects: if name in self.objects:
@ -1262,22 +1272,23 @@ class PythonDomain(Domain):
def _make_module_refnode(self, builder: Builder, fromdocname: str, name: str, def _make_module_refnode(self, builder: Builder, fromdocname: str, name: str,
contnode: Node) -> Element: contnode: Node) -> Element:
# get additional info for modules # get additional info for modules
docname, node_id, synopsis, platform, deprecated = self.modules[name] module = self.modules[name]
title = name title = name
if synopsis: if module.synopsis:
title += ': ' + synopsis title += ': ' + module.synopsis
if deprecated: if module.deprecated:
title += _(' (deprecated)') title += _(' (deprecated)')
if platform: if module.platform:
title += ' (' + platform + ')' title += ' (' + module.platform + ')'
return make_refnode(builder, fromdocname, docname, node_id, contnode, title) return make_refnode(builder, fromdocname, module.docname, module.node_id,
contnode, title)
def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]: def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
for modname, info in self.modules.items(): for modname, mod in self.modules.items():
yield (modname, modname, 'module', info[0], info[1], 0) yield (modname, modname, 'module', mod.docname, mod.node_id, 0)
for refname, (docname, node_id, type) in self.objects.items(): for refname, obj in self.objects.items():
if type != 'module': # modules are already handled if obj.objtype != 'module': # modules are already handled
yield (refname, refname, type, docname, node_id, 1) yield (refname, refname, obj.objtype, obj.docname, obj.node_id, 1)
def get_full_qualified_name(self, node: Element) -> str: def get_full_qualified_name(self, node: Element) -> str:
modname = node.get('py:module') modname = node.get('py:module')