Skip to content

Commit

Permalink
Close sphinx-doc#5603: 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 c4eb528
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ Features added

* #8924: autodoc: Support ``bound`` argument for TypeVar
* #7383: autodoc: Support typehints for properties
* #5603: 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
Original file line number Diff line number Diff line change
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
1 change: 1 addition & 0 deletions tests/roots/test-ext-autodoc/target/canonical/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from target.canonical.original import Foo
5 changes: 5 additions & 0 deletions tests/roots/test-ext-autodoc/target/canonical/original.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Foo:
"""docstring"""

def meth(self):
"""docstring"""
25 changes: 25 additions & 0 deletions tests/test_ext_autodoc.py
Original file line number Diff line number Diff line change
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 c4eb528

Please sign in to comment.