Skip to content

Commit

Permalink
Close sphinx-doc#8603: autodoc: Allow to refer to a python object usi…
Browse files Browse the repository at this point in the history
…ng canonical name

This generates `:canonical:` option for `:py:class:` directive if the
target class is imported from other module.  It allows users to refer it
using both the new name (imported name) and the original name (canonical
name).

It helps a library that implements some class in private module (like
`_io.StringIO`), and publish it as public module (like `io.StringIO`).
  • Loading branch information
tk0miya committed Mar 21, 2021
1 parent 5e8f814 commit 0e3c4f7
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Expand Up @@ -64,6 +64,8 @@ Features added

* #8924: autodoc: Support ``bound`` argument for TypeVar
* #7383: autodoc: Support typehints for properties
* #8603: autodoc: Allow to refer to a python object using its canonical name
when the object has two different names; a canonical name and an alias name
* #7549: autosummary: Enable :confval:`autosummary_generate` by default
* #4826: py domain: Add ``:canonical:`` option to python directives to describe
the location where the object is defined
Expand Down
15 changes: 15 additions & 0 deletions sphinx/ext/autodoc/__init__.py
Expand Up @@ -1590,6 +1590,17 @@ def get_overloaded_signatures(self) -> List[Signature]:

return []

def get_canonical_fullname(self) -> Optional[str]:
__modname__ = safe_getattr(self.object, '__module__', self.modname)
__qualname__ = safe_getattr(self.object, '__qualname__', None)
if __qualname__ is None:
__qualname__ = safe_getattr(self.object, '__name__', None)

if __modname__ and __qualname__:
return '.'.join([__modname__, __qualname__])
else:
return None

def add_directive_header(self, sig: str) -> None:
sourcename = self.get_sourcename()

Expand All @@ -1600,6 +1611,10 @@ def add_directive_header(self, sig: str) -> None:
if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals:
self.add_line(' :final:', sourcename)

canonical_fullname = self.get_canonical_fullname()
if not self.doc_as_attr and canonical_fullname and self.fullname != canonical_fullname:
self.add_line(' :canonical: %s' % canonical_fullname, sourcename)

# add inheritance info, if wanted
if not self.doc_as_attr and self.options.show_inheritance:
sourcename = self.get_sourcename()
Expand Down
25 changes: 25 additions & 0 deletions tests/test_ext_autodoc.py
Expand Up @@ -2474,3 +2474,28 @@ def test_hide_value(app):
' :meta hide-value:',
'',
]


@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_canonical(app):
options = {'members': None,
'imported-members': None}
actual = do_autodoc(app, 'module', 'target.canonical', options)
assert list(actual) == [
'',
'.. py:module:: target.canonical',
'',
'',
'.. py:class:: Foo()',
' :module: target.canonical',
' :canonical: target.canonical.original.Foo',
'',
' docstring',
'',
'',
' .. py:method:: Foo.meth()',
' :module: target.canonical',
'',
' docstring',
'',
]

0 comments on commit 0e3c4f7

Please sign in to comment.