Fix #9121: py domain: duplicated warning for canonical and alias

A duplicated warning is emitted when both canonical and its alias
objects are defined on the same document. But it should not be emitted
because they're the same object, not conflicted.
This commit is contained in:
Takeshi KOMIYA 2021-04-23 01:19:18 +09:00
parent dfdc7626b5
commit d02a466cb5
3 changed files with 52 additions and 9 deletions

View File

@ -23,6 +23,8 @@ Features added
* #9098: html: copy-range protection for doctests doesn't work in Safari
* #9103: LaTeX: imgconverter: conversion runs even if not needed
* #8127: py domain: Ellipsis in info-field-list causes nit-picky warning
* #9121: py domain: duplicated warning is emitted when both canonical and its
alias objects are defined on the document
* #9023: More CSS classes on domain descriptions, see :ref:`nodes` for details.
Bugs fixed

View File

@ -68,7 +68,7 @@ class ObjectEntry(NamedTuple):
docname: str
node_id: str
objtype: str
canonical: bool
aliased: bool
class ModuleEntry(NamedTuple):
@ -505,7 +505,7 @@ class PyObject(ObjectDescription[Tuple[str, str]]):
canonical_name = self.options.get('canonical')
if canonical_name:
domain.note_object(canonical_name, self.objtype, node_id, canonical=True,
domain.note_object(canonical_name, self.objtype, node_id, aliased=True,
location=signode)
if 'noindexentry' not in self.options:
@ -1138,17 +1138,25 @@ class PythonDomain(Domain):
return self.data.setdefault('objects', {}) # fullname -> ObjectEntry
def note_object(self, name: str, objtype: str, node_id: str,
canonical: bool = False, location: Any = None) -> None:
aliased: bool = False, location: Any = None) -> None:
"""Note a python object for cross reference.
.. versionadded:: 2.1
"""
if name in self.objects:
other = self.objects[name]
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, canonical)
if other.aliased and aliased is False:
# The original definition found. Override it!
pass
elif other.aliased is False and aliased:
# The original definition is already registered.
return
else:
# duplicated
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, aliased)
@property
def modules(self) -> Dict[str, ModuleEntry]:
@ -1326,8 +1334,8 @@ 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
if obj.canonical:
# canonical names are not full-text searchable.
if obj.aliased:
# aliased 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)

View File

@ -870,6 +870,39 @@ def test_canonical(app):
assert domain.objects['_io.StringIO'] == ('index', 'io.StringIO', 'class', True)
def test_canonical_definition_overrides(app, warning):
text = (".. py:class:: io.StringIO\n"
" :canonical: _io.StringIO\n"
".. py:class:: _io.StringIO\n")
restructuredtext.parse(app, text)
assert warning.getvalue() == ""
domain = app.env.get_domain('py')
assert domain.objects['_io.StringIO'] == ('index', 'id0', 'class', False)
def test_canonical_definition_skip(app, warning):
text = (".. py:class:: _io.StringIO\n"
".. py:class:: io.StringIO\n"
" :canonical: _io.StringIO\n")
restructuredtext.parse(app, text)
assert warning.getvalue() == ""
domain = app.env.get_domain('py')
assert domain.objects['_io.StringIO'] == ('index', 'io.StringIO', 'class', False)
def test_canonical_duplicated(app, warning):
text = (".. py:class:: mypackage.StringIO\n"
" :canonical: _io.StringIO\n"
".. py:class:: io.StringIO\n"
" :canonical: _io.StringIO\n")
restructuredtext.parse(app, text)
assert warning.getvalue() != ""
def test_info_field_list(app):
text = (".. py:module:: example\n"
".. py:class:: Class\n"