78 lines
2.2 KiB
Python
78 lines
2.2 KiB
Python
|
"""Add external links to module code in Python object descriptions."""
|
||
|
|
||
|
from __future__ import annotations
|
||
|
|
||
|
from typing import TYPE_CHECKING, Any
|
||
|
|
||
|
from docutils import nodes
|
||
|
|
||
|
import sphinx
|
||
|
from sphinx import addnodes
|
||
|
from sphinx.errors import SphinxError
|
||
|
from sphinx.locale import _
|
||
|
|
||
|
if TYPE_CHECKING:
|
||
|
from docutils.nodes import Node
|
||
|
|
||
|
from sphinx.application import Sphinx
|
||
|
|
||
|
|
||
|
class LinkcodeError(SphinxError):
|
||
|
category = "linkcode error"
|
||
|
|
||
|
|
||
|
def doctree_read(app: Sphinx, doctree: Node) -> None:
|
||
|
env = app.builder.env
|
||
|
|
||
|
resolve_target = getattr(env.config, 'linkcode_resolve', None)
|
||
|
if not callable(env.config.linkcode_resolve):
|
||
|
msg = 'Function `linkcode_resolve` is not given in conf.py'
|
||
|
raise LinkcodeError(msg)
|
||
|
assert resolve_target is not None # for mypy
|
||
|
|
||
|
domain_keys = {
|
||
|
'py': ['module', 'fullname'],
|
||
|
'c': ['names'],
|
||
|
'cpp': ['names'],
|
||
|
'js': ['object', 'fullname'],
|
||
|
}
|
||
|
|
||
|
for objnode in list(doctree.findall(addnodes.desc)):
|
||
|
domain = objnode.get('domain')
|
||
|
uris: set[str] = set()
|
||
|
for signode in objnode:
|
||
|
if not isinstance(signode, addnodes.desc_signature):
|
||
|
continue
|
||
|
|
||
|
# Convert signode to a specified format
|
||
|
info = {}
|
||
|
for key in domain_keys.get(domain, []):
|
||
|
value = signode.get(key)
|
||
|
if not value:
|
||
|
value = ''
|
||
|
info[key] = value
|
||
|
if not info:
|
||
|
continue
|
||
|
|
||
|
# Call user code to resolve the link
|
||
|
uri = resolve_target(domain, info)
|
||
|
if not uri:
|
||
|
# no source
|
||
|
continue
|
||
|
|
||
|
if uri in uris or not uri:
|
||
|
# only one link per name, please
|
||
|
continue
|
||
|
uris.add(uri)
|
||
|
|
||
|
inline = nodes.inline('', _('[source]'), classes=['viewcode-link'])
|
||
|
onlynode = addnodes.only(expr='html')
|
||
|
onlynode += nodes.reference('', '', inline, internal=False, refuri=uri)
|
||
|
signode += onlynode
|
||
|
|
||
|
|
||
|
def setup(app: Sphinx) -> dict[str, Any]:
|
||
|
app.connect('doctree-read', doctree_read)
|
||
|
app.add_config_value('linkcode_resolve', None, '')
|
||
|
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
|