mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #7463 from tk0miya/4944_canonical_name
Support canonical name of python objects
This commit is contained in:
commit
4ad466c7a6
6
CHANGES
6
CHANGES
@ -10,6 +10,9 @@ Dependencies
|
||||
Incompatible changes
|
||||
--------------------
|
||||
|
||||
* #4826: py domain: The structure of python objects is changed. A boolean value
|
||||
is added to indicate that the python object is canonical one
|
||||
|
||||
Deprecated
|
||||
----------
|
||||
|
||||
@ -23,6 +26,9 @@ Deprecated
|
||||
Features added
|
||||
--------------
|
||||
|
||||
* #4826: py domain: Add ``:canonical:`` option to python directives to describe
|
||||
the location where the object is defined
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
|
@ -189,6 +189,14 @@ The following directives are provided for module and class contents:
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
.. rst:directive:option:: canonical
|
||||
:type: full qualified name including module name
|
||||
|
||||
Describe the location where the object is defined if the object is
|
||||
imported from other modules
|
||||
|
||||
.. versionadded:: 4.0
|
||||
|
||||
.. rst:directive:: .. py:data:: name
|
||||
|
||||
Describes global data in a module, including both variables and values used
|
||||
@ -207,6 +215,14 @@ The following directives are provided for module and class contents:
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
.. rst:directive:option:: canonical
|
||||
:type: full qualified name including module name
|
||||
|
||||
Describe the location where the object is defined if the object is
|
||||
imported from other modules
|
||||
|
||||
.. versionadded:: 4.0
|
||||
|
||||
.. rst:directive:: .. py:exception:: name
|
||||
|
||||
Describes an exception class. The signature can, but need not include
|
||||
@ -246,6 +262,14 @@ The following directives are provided for module and class contents:
|
||||
|
||||
.. rubric:: options
|
||||
|
||||
.. rst:directive:option:: canonical
|
||||
:type: full qualified name including module name
|
||||
|
||||
Describe the location where the object is defined if the object is
|
||||
imported from other modules
|
||||
|
||||
.. versionadded:: 4.0
|
||||
|
||||
.. rst:directive:option:: final
|
||||
:type: no value
|
||||
|
||||
@ -271,6 +295,14 @@ The following directives are provided for module and class contents:
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
.. rst:directive:option:: canonical
|
||||
:type: full qualified name including module name
|
||||
|
||||
Describe the location where the object is defined if the object is
|
||||
imported from other modules
|
||||
|
||||
.. versionadded:: 4.0
|
||||
|
||||
.. rst:directive:: .. py:method:: name(parameters)
|
||||
|
||||
Describes an object method. The parameters should not include the ``self``
|
||||
@ -294,6 +326,14 @@ The following directives are provided for module and class contents:
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
.. rst:directive:option:: canonical
|
||||
:type: full qualified name including module name
|
||||
|
||||
Describe the location where the object is defined if the object is
|
||||
imported from other modules
|
||||
|
||||
.. versionadded:: 4.0
|
||||
|
||||
.. rst:directive:option:: classmethod
|
||||
:type: no value
|
||||
|
||||
|
@ -65,7 +65,8 @@ pairindextypes = {
|
||||
|
||||
ObjectEntry = NamedTuple('ObjectEntry', [('docname', str),
|
||||
('node_id', str),
|
||||
('objtype', str)])
|
||||
('objtype', str),
|
||||
('canonical', bool)])
|
||||
ModuleEntry = NamedTuple('ModuleEntry', [('docname', str),
|
||||
('node_id', str),
|
||||
('synopsis', str),
|
||||
@ -312,6 +313,7 @@ class PyObject(ObjectDescription):
|
||||
option_spec = {
|
||||
'noindex': directives.flag,
|
||||
'module': directives.unchanged,
|
||||
'canonical': directives.unchanged,
|
||||
'annotation': directives.unchanged,
|
||||
}
|
||||
|
||||
@ -453,6 +455,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)
|
||||
|
||||
indextext = self.get_index_text(modname, name_cls)
|
||||
if indextext:
|
||||
self.indexnode['entries'].append(('single', indextext, node_id, '', None))
|
||||
@ -1037,7 +1044,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
|
||||
@ -1047,7 +1055,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]:
|
||||
@ -1200,7 +1208,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')
|
||||
@ -1246,7 +1258,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,
|
||||
}
|
||||
|
@ -192,20 +192,22 @@ def test_domain_py_find_obj(app, status, warning):
|
||||
|
||||
assert (find_obj(None, None, 'NONEXISTANT', 'class') == [])
|
||||
assert (find_obj(None, None, 'NestedParentA', 'class') ==
|
||||
[('NestedParentA', ('roles', 'NestedParentA', 'class'))])
|
||||
[('NestedParentA', ('roles', 'NestedParentA', 'class', False))])
|
||||
assert (find_obj(None, None, 'NestedParentA.NestedChildA', 'class') ==
|
||||
[('NestedParentA.NestedChildA', ('roles', 'NestedParentA.NestedChildA', 'class'))])
|
||||
[('NestedParentA.NestedChildA',
|
||||
('roles', 'NestedParentA.NestedChildA', 'class', False))])
|
||||
assert (find_obj(None, 'NestedParentA', 'NestedChildA', 'class') ==
|
||||
[('NestedParentA.NestedChildA', ('roles', 'NestedParentA.NestedChildA', 'class'))])
|
||||
[('NestedParentA.NestedChildA',
|
||||
('roles', 'NestedParentA.NestedChildA', 'class', False))])
|
||||
assert (find_obj(None, None, 'NestedParentA.NestedChildA.subchild_1', 'meth') ==
|
||||
[('NestedParentA.NestedChildA.subchild_1',
|
||||
('roles', 'NestedParentA.NestedChildA.subchild_1', 'method'))])
|
||||
('roles', 'NestedParentA.NestedChildA.subchild_1', 'method', False))])
|
||||
assert (find_obj(None, 'NestedParentA', 'NestedChildA.subchild_1', 'meth') ==
|
||||
[('NestedParentA.NestedChildA.subchild_1',
|
||||
('roles', 'NestedParentA.NestedChildA.subchild_1', 'method'))])
|
||||
('roles', 'NestedParentA.NestedChildA.subchild_1', 'method', False))])
|
||||
assert (find_obj(None, 'NestedParentA.NestedChildA', 'subchild_1', 'meth') ==
|
||||
[('NestedParentA.NestedChildA.subchild_1',
|
||||
('roles', 'NestedParentA.NestedChildA.subchild_1', 'method'))])
|
||||
('roles', 'NestedParentA.NestedChildA.subchild_1', 'method', False))])
|
||||
|
||||
|
||||
def test_get_full_qualified_name():
|
||||
@ -464,7 +466,7 @@ def test_pydata(app):
|
||||
[desc, ([desc_signature, desc_name, "var"],
|
||||
[desc_content, ()])]))
|
||||
assert 'var' in domain.objects
|
||||
assert domain.objects['var'] == ('index', 'var', 'data')
|
||||
assert domain.objects['var'] == ('index', 'var', 'data', False)
|
||||
|
||||
|
||||
def test_pyfunction(app):
|
||||
@ -494,9 +496,9 @@ def test_pyfunction(app):
|
||||
entries=[('single', 'func2() (in module example)', 'example.func2', '', None)])
|
||||
|
||||
assert 'func1' in domain.objects
|
||||
assert domain.objects['func1'] == ('index', 'func1', 'function')
|
||||
assert domain.objects['func1'] == ('index', 'func1', 'function', False)
|
||||
assert 'example.func2' in domain.objects
|
||||
assert domain.objects['example.func2'] == ('index', 'example.func2', 'function')
|
||||
assert domain.objects['example.func2'] == ('index', 'example.func2', 'function', False)
|
||||
|
||||
|
||||
def test_pyclass_options(app):
|
||||
@ -518,13 +520,13 @@ def test_pyclass_options(app):
|
||||
assert_node(doctree[0], addnodes.index,
|
||||
entries=[('single', 'Class1 (built-in class)', 'Class1', '', None)])
|
||||
assert 'Class1' in domain.objects
|
||||
assert domain.objects['Class1'] == ('index', 'Class1', 'class')
|
||||
assert domain.objects['Class1'] == ('index', 'Class1', 'class', False)
|
||||
|
||||
# :final:
|
||||
assert_node(doctree[2], addnodes.index,
|
||||
entries=[('single', 'Class2 (built-in class)', 'Class2', '', None)])
|
||||
assert 'Class2' in domain.objects
|
||||
assert domain.objects['Class2'] == ('index', 'Class2', 'class')
|
||||
assert domain.objects['Class2'] == ('index', 'Class2', 'class', False)
|
||||
|
||||
|
||||
def test_pymethod_options(app):
|
||||
@ -570,7 +572,7 @@ def test_pymethod_options(app):
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth1' in domain.objects
|
||||
assert domain.objects['Class.meth1'] == ('index', 'Class.meth1', 'method')
|
||||
assert domain.objects['Class.meth1'] == ('index', 'Class.meth1', 'method', False)
|
||||
|
||||
# :classmethod:
|
||||
assert_node(doctree[1][1][2], addnodes.index,
|
||||
@ -580,7 +582,7 @@ def test_pymethod_options(app):
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth2' in domain.objects
|
||||
assert domain.objects['Class.meth2'] == ('index', 'Class.meth2', 'method')
|
||||
assert domain.objects['Class.meth2'] == ('index', 'Class.meth2', 'method', False)
|
||||
|
||||
# :staticmethod:
|
||||
assert_node(doctree[1][1][4], addnodes.index,
|
||||
@ -590,7 +592,7 @@ def test_pymethod_options(app):
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth3' in domain.objects
|
||||
assert domain.objects['Class.meth3'] == ('index', 'Class.meth3', 'method')
|
||||
assert domain.objects['Class.meth3'] == ('index', 'Class.meth3', 'method', False)
|
||||
|
||||
# :async:
|
||||
assert_node(doctree[1][1][6], addnodes.index,
|
||||
@ -600,7 +602,7 @@ def test_pymethod_options(app):
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth4' in domain.objects
|
||||
assert domain.objects['Class.meth4'] == ('index', 'Class.meth4', 'method')
|
||||
assert domain.objects['Class.meth4'] == ('index', 'Class.meth4', 'method', False)
|
||||
|
||||
# :property:
|
||||
assert_node(doctree[1][1][8], addnodes.index,
|
||||
@ -609,7 +611,7 @@ def test_pymethod_options(app):
|
||||
[desc_name, "meth5"])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth5' in domain.objects
|
||||
assert domain.objects['Class.meth5'] == ('index', 'Class.meth5', 'method')
|
||||
assert domain.objects['Class.meth5'] == ('index', 'Class.meth5', 'method', False)
|
||||
|
||||
# :abstractmethod:
|
||||
assert_node(doctree[1][1][10], addnodes.index,
|
||||
@ -619,7 +621,7 @@ def test_pymethod_options(app):
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth6' in domain.objects
|
||||
assert domain.objects['Class.meth6'] == ('index', 'Class.meth6', 'method')
|
||||
assert domain.objects['Class.meth6'] == ('index', 'Class.meth6', 'method', False)
|
||||
|
||||
# :final:
|
||||
assert_node(doctree[1][1][12], addnodes.index,
|
||||
@ -629,7 +631,7 @@ def test_pymethod_options(app):
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth7' in domain.objects
|
||||
assert domain.objects['Class.meth7'] == ('index', 'Class.meth7', 'method')
|
||||
assert domain.objects['Class.meth7'] == ('index', 'Class.meth7', 'method', False)
|
||||
|
||||
|
||||
def test_pyclassmethod(app):
|
||||
@ -650,7 +652,7 @@ def test_pyclassmethod(app):
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth' in domain.objects
|
||||
assert domain.objects['Class.meth'] == ('index', 'Class.meth', 'method')
|
||||
assert domain.objects['Class.meth'] == ('index', 'Class.meth', 'method', False)
|
||||
|
||||
|
||||
def test_pystaticmethod(app):
|
||||
@ -671,7 +673,7 @@ def test_pystaticmethod(app):
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth' in domain.objects
|
||||
assert domain.objects['Class.meth'] == ('index', 'Class.meth', 'method')
|
||||
assert domain.objects['Class.meth'] == ('index', 'Class.meth', 'method', False)
|
||||
|
||||
|
||||
def test_pyattribute(app):
|
||||
@ -694,7 +696,7 @@ def test_pyattribute(app):
|
||||
[desc_annotation, " = ''"])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.attr' in domain.objects
|
||||
assert domain.objects['Class.attr'] == ('index', 'Class.attr', 'attribute')
|
||||
assert domain.objects['Class.attr'] == ('index', 'Class.attr', 'attribute', False)
|
||||
|
||||
|
||||
def test_pydecorator_signature(app):
|
||||
@ -709,7 +711,7 @@ def test_pydecorator_signature(app):
|
||||
domain="py", objtype="function", noindex=False)
|
||||
|
||||
assert 'deco' in domain.objects
|
||||
assert domain.objects['deco'] == ('index', 'deco', 'function')
|
||||
assert domain.objects['deco'] == ('index', 'deco', 'function', False)
|
||||
|
||||
|
||||
def test_pydecoratormethod_signature(app):
|
||||
@ -724,7 +726,22 @@ def test_pydecoratormethod_signature(app):
|
||||
domain="py", objtype="method", noindex=False)
|
||||
|
||||
assert 'deco' in domain.objects
|
||||
assert domain.objects['deco'] == ('index', 'deco', 'method')
|
||||
assert domain.objects['deco'] == ('index', 'deco', 'method', False)
|
||||
|
||||
|
||||
def test_canonical(app):
|
||||
text = (".. py:class:: io.StringIO\n"
|
||||
" :canonical: _io.StringIO")
|
||||
domain = app.env.get_domain('py')
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_annotation, "class "],
|
||||
[desc_addname, "io."],
|
||||
[desc_name, "StringIO"])],
|
||||
desc_content)]))
|
||||
assert 'io.StringIO' in domain.objects
|
||||
assert domain.objects['io.StringIO'] == ('index', 'io.StringIO', 'class', False)
|
||||
assert domain.objects['_io.StringIO'] == ('index', 'io.StringIO', 'class', True)
|
||||
|
||||
|
||||
@pytest.mark.sphinx(freshenv=True)
|
||||
|
@ -84,7 +84,7 @@ def test_object_inventory(app):
|
||||
refs = app.env.domaindata['py']['objects']
|
||||
|
||||
assert 'func_without_module' in refs
|
||||
assert refs['func_without_module'] == ('objects', 'func_without_module', 'function')
|
||||
assert refs['func_without_module'] == ('objects', 'func_without_module', 'function', False)
|
||||
assert 'func_without_module2' in refs
|
||||
assert 'mod.func_in_module' in refs
|
||||
assert 'mod.Cls' in refs
|
||||
|
Loading…
Reference in New Issue
Block a user