From 67b56eb4726f05501e8554722945f67758c9d0cd Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 31 Mar 2019 19:55:10 +0900 Subject: [PATCH 1/4] Add "objects" property to PythonDomain --- sphinx/domains/python.py | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index ea71aa976..628141f58 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -777,11 +777,16 @@ class PythonDomain(Domain): PythonModuleIndex, ] + @property + def objects(self): + # type: () -> Dict[str, Tuple[str, str]] + return self.data.setdefault('objects', {}) # fullname -> docname, objtype + def clear_doc(self, docname): # type: (str) -> None - for fullname, (fn, _l) in list(self.data['objects'].items()): + for fullname, (fn, _l) in list(self.objects.items()): if fn == docname: - del self.data['objects'][fullname] + del self.objects[fullname] for modname, (fn, _x, _x, _x) in list(self.data['modules'].items()): if fn == docname: del self.data['modules'][modname] @@ -791,7 +796,7 @@ class PythonDomain(Domain): # XXX check duplicates? for fullname, (fn, objtype) in otherdata['objects'].items(): if fn in docnames: - self.data['objects'][fullname] = (fn, objtype) + self.objects[fullname] = (fn, objtype) for modname, data in otherdata['modules'].items(): if data[0] in docnames: self.data['modules'][modname] = data @@ -808,7 +813,6 @@ class PythonDomain(Domain): if not name: return [] - objects = self.data['objects'] matches = [] # type: List[Tuple[str, Any]] newname = None @@ -820,44 +824,44 @@ class PythonDomain(Domain): if objtypes is not None: if modname and classname: fullname = modname + '.' + classname + '.' + name - if fullname in objects and objects[fullname][1] in objtypes: + if fullname in self.objects and self.objects[fullname][1] in objtypes: newname = fullname if not newname: - if modname and modname + '.' + name in objects and \ - objects[modname + '.' + name][1] in objtypes: + if modname and modname + '.' + name in self.objects and \ + self.objects[modname + '.' + name][1] in objtypes: newname = modname + '.' + name - elif name in objects and objects[name][1] in objtypes: + elif name in self.objects and self.objects[name][1] in objtypes: newname = name else: # "fuzzy" searching mode searchname = '.' + name - matches = [(oname, objects[oname]) for oname in objects + matches = [(oname, self.objects[oname]) for oname in self.objects if oname.endswith(searchname) and - objects[oname][1] in objtypes] + self.objects[oname][1] in objtypes] else: # NOTE: searching for exact match, object type is not considered - if name in objects: + if name in self.objects: newname = name elif type == 'mod': # only exact matches allowed for modules return [] - elif classname and classname + '.' + name in objects: + elif classname and classname + '.' + name in self.objects: newname = classname + '.' + name - elif modname and modname + '.' + name in objects: + elif modname and modname + '.' + name in self.objects: newname = modname + '.' + name elif modname and classname and \ - modname + '.' + classname + '.' + name in objects: + modname + '.' + classname + '.' + name in self.objects: newname = modname + '.' + classname + '.' + name # special case: builtin exceptions have module "exceptions" set elif type == 'exc' and '.' not in name and \ - 'exceptions.' + name in objects: + 'exceptions.' + name in self.objects: newname = 'exceptions.' + name # special case: object methods elif type in ('func', 'meth') and '.' not in name and \ - 'object.' + name in objects: + 'object.' + name in self.objects: newname = 'object.' + name if newname is not None: - matches.append((newname, objects[newname])) + matches.append((newname, self.objects[newname])) return matches def resolve_xref(self, env, fromdocname, builder, @@ -919,7 +923,7 @@ class PythonDomain(Domain): # type: () -> Iterator[Tuple[str, str, str, str, str, int]] for modname, info in self.data['modules'].items(): yield (modname, modname, 'module', info[0], 'module-' + modname, 0) - for refname, (docname, type) in self.data['objects'].items(): + for refname, (docname, type) in self.objects.items(): if type != 'module': # modules are already handled yield (refname, refname, type, docname, refname, 1) From f7a32746d3a6fec18a899f27f8fd9c272fdd723c Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 31 Mar 2019 19:56:15 +0900 Subject: [PATCH 2/4] Add "modules" property to PythonDomain --- sphinx/domains/python.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 628141f58..842a49b08 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -782,14 +782,19 @@ class PythonDomain(Domain): # type: () -> Dict[str, Tuple[str, str]] return self.data.setdefault('objects', {}) # fullname -> docname, objtype + @property + def modules(self): + # type: () -> Dict[str, Tuple[str, str, str, bool]] + return self.data.setdefault('modules', {}) # modname -> docname, synopsis, platform, deprecated # NOQA + def clear_doc(self, docname): # type: (str) -> None for fullname, (fn, _l) in list(self.objects.items()): if fn == docname: del self.objects[fullname] - for modname, (fn, _x, _x, _x) in list(self.data['modules'].items()): + for modname, (fn, _x, _x, _y) in list(self.modules.items()): if fn == docname: - del self.data['modules'][modname] + del self.modules[modname] def merge_domaindata(self, docnames, otherdata): # type: (List[str], Dict) -> None @@ -799,7 +804,7 @@ class PythonDomain(Domain): self.objects[fullname] = (fn, objtype) for modname, data in otherdata['modules'].items(): if data[0] in docnames: - self.data['modules'][modname] = data + self.modules[modname] = data def find_obj(self, env, modname, classname, name, type, searchmode=0): # type: (BuildEnvironment, str, str, str, str, int) -> List[Tuple[str, Any]] @@ -908,7 +913,7 @@ class PythonDomain(Domain): def _make_module_refnode(self, builder, fromdocname, name, contnode): # type: (Builder, str, str, nodes.Node) -> nodes.Element # get additional info for modules - docname, synopsis, platform, deprecated = self.data['modules'][name] + docname, synopsis, platform, deprecated = self.modules[name] title = name if synopsis: title += ': ' + synopsis @@ -921,7 +926,7 @@ class PythonDomain(Domain): def get_objects(self): # type: () -> Iterator[Tuple[str, str, str, str, str, int]] - for modname, info in self.data['modules'].items(): + for modname, info in self.modules.items(): yield (modname, modname, 'module', info[0], 'module-' + modname, 0) for refname, (docname, type) in self.objects.items(): if type != 'module': # modules are already handled From 259be8716ad4b2332aa4d7693d73400eb06fa7d7 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 24 Mar 2019 15:43:16 +0900 Subject: [PATCH 3/4] Add PythonDomain.note_module() to register a python object to the domain --- CHANGES | 6 +++++- sphinx/domains/python.py | 31 +++++++++++++++++++++---------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/CHANGES b/CHANGES index 1acf3ba1c..deb418bef 100644 --- a/CHANGES +++ b/CHANGES @@ -48,7 +48,11 @@ Features added -------------- * Add a helper class ``sphinx.transforms.post_transforms.SphinxPostTransform`` -* Add a helper method ``SphinxDirective.set_source_info()`` +* Add helper methods + + - ``PythonDomain.note_object()`` + - ``SphinxDirective.set_source_info()`` + * #6180: Support ``--keep-going`` with BuildDoc setup command * ``math`` directive now supports ``:class:`` option * todo: ``todo`` directive now supports ``:name:`` option diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 842a49b08..6768c697a 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -9,6 +9,7 @@ """ import re +from typing import cast from docutils import nodes from docutils.parsers.rst import directives @@ -332,15 +333,9 @@ class PyObject(ObjectDescription): signode['ids'].append(fullname) signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) - objects = self.env.domaindata['py']['objects'] - if fullname in objects: - self.state_machine.reporter.warning( - 'duplicate object description of %s, ' % fullname + - 'other instance in ' + - self.env.doc2path(objects[fullname][0]) + - ', use :noindex: for one of them', - line=self.lineno) - objects[fullname] = (self.env.docname, self.objtype) + + domain = cast(PythonDomain, self.env.get_domain('py')) + domain.note_object(fullname, self.objtype) indextext = self.get_index_text(modname, name_cls) if indextext: @@ -583,6 +578,8 @@ class PyModule(SphinxDirective): def run(self): # type: () -> List[nodes.Node] + domain = cast(PythonDomain, self.env.get_domain('py')) + modname = self.arguments[0].strip() noindex = 'noindex' in self.options self.env.ref_context['py:module'] = modname @@ -594,7 +591,8 @@ class PyModule(SphinxDirective): 'deprecated' in self.options) # make a duplicate entry in 'objects' to facilitate searching for # the module in PythonDomain.find_obj() - self.env.domaindata['py']['objects'][modname] = (self.env.docname, 'module') + domain.note_object(modname, 'module') + targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True) self.state.document.note_explicit_target(targetnode) @@ -782,6 +780,19 @@ class PythonDomain(Domain): # type: () -> Dict[str, Tuple[str, str]] return self.data.setdefault('objects', {}) # fullname -> docname, objtype + def note_object(self, name, objtype, location=None): + # type: (str, str, Any) -> None + """Note a python object for cross reference. + + .. versionadded:: 2.1 + """ + if name in self.objects: + docname = self.objects[name][0] + logger.warning(__('duplicate object description of %s, ' + 'other instance in %s, use :noindex: for one of them'), + name, docname, location=location) + self.objects[name] = (self.env.docname, objtype) + @property def modules(self): # type: () -> Dict[str, Tuple[str, str, str, bool]] From 0e837370f5993c42ee4e4655c7d048d466ec1c02 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 31 Mar 2019 20:22:38 +0900 Subject: [PATCH 4/4] Add PythonDomain.note_module() to register a python module to the domain --- CHANGES | 1 + sphinx/domains/python.py | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index deb418bef..ab842b49c 100644 --- a/CHANGES +++ b/CHANGES @@ -50,6 +50,7 @@ Features added * Add a helper class ``sphinx.transforms.post_transforms.SphinxPostTransform`` * Add helper methods + - ``PythonDomain.note_module()`` - ``PythonDomain.note_object()`` - ``SphinxDirective.set_source_info()`` diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 6768c697a..013f9d195 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -585,12 +585,11 @@ class PyModule(SphinxDirective): self.env.ref_context['py:module'] = modname ret = [] # type: List[nodes.Node] if not noindex: - self.env.domaindata['py']['modules'][modname] = (self.env.docname, - self.options.get('synopsis', ''), - self.options.get('platform', ''), - 'deprecated' in self.options) - # make a duplicate entry in 'objects' to facilitate searching for - # the module in PythonDomain.find_obj() + # note module to the domain + domain.note_module(modname, + self.options.get('synopsis', ''), + self.options.get('platform', ''), + 'deprecated' in self.options) domain.note_object(modname, 'module') targetnode = nodes.target('', '', ids=['module-' + modname], @@ -798,6 +797,14 @@ class PythonDomain(Domain): # type: () -> Dict[str, Tuple[str, str, str, bool]] return self.data.setdefault('modules', {}) # modname -> docname, synopsis, platform, deprecated # NOQA + def note_module(self, name, synopsis, platform, deprecated): + # type: (str, str, str, bool) -> None + """Note a python module for cross reference. + + .. versionadded:: 2.1 + """ + self.modules[name] = (self.env.docname, synopsis, platform, deprecated) + def clear_doc(self, docname): # type: (str) -> None for fullname, (fn, _l) in list(self.objects.items()):