Allow extensions to define the keys returned by linkcode (#11824)

Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
This commit is contained in:
Nicolas Peugnet 2025-01-03 22:28:34 +01:00 committed by GitHub
parent 20c1c7fc1c
commit 0fbf88a59f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 54 additions and 8 deletions

View File

@ -21,6 +21,9 @@ Features added
* Add a new ``duplicate_declaration`` warning type,
with ``duplicate_declaration.c`` and ``duplicate_declaration.cpp`` subtypes.
Patch by Julien Lecomte and Adam Turner.
* #11824: linkcode: Allow extensions to add support for a domain by defining
the keys that should be present.
Patch by Nicolas Peugnet.
Bugs fixed
----------

View File

@ -49,3 +49,21 @@ Configuration
return None
filename = info['module'].replace('.', '/')
return "https://somesite/sourcerepo/%s.py" % filename
Third-party domains
-------------------
Support for other domains can be added by extensions with
:py:func:`.add_linkcode_domain()`.
For example, a Sphinx extension that provides a ``php`` domain
could use the following code to support :mod:`~sphinx.ext.linkcode`:
.. code-block:: python
from sphinx.ext.linkcode import add_linkcode_domain
def setup(app):
add_linkcode_domain('php', ['namespace', 'class', 'fullname'])
.. autofunction:: add_linkcode_domain

View File

@ -18,6 +18,23 @@ if TYPE_CHECKING:
from sphinx.util.typing import ExtensionMetadata
_DOMAIN_KEYS = {
'py': ['module', 'fullname'],
'c': ['names'],
'cpp': ['names'],
'js': ['object', 'fullname'],
}
def add_linkcode_domain(domain: str, keys: list[str], override: bool = False) -> None:
"""Register a new list of keys to use for a domain.
.. versionadded:: 8.2
"""
if override or domain not in _DOMAIN_KEYS:
_DOMAIN_KEYS[domain] = list(keys)
class LinkcodeError(SphinxError):
category = 'linkcode error'
@ -37,13 +54,6 @@ def doctree_read(app: Sphinx, doctree: Node) -> None:
# ``supported_linkcode`` attribute.
node_only_expr = getattr(app.builder, 'supported_linkcode', 'html')
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()
@ -53,7 +63,7 @@ def doctree_read(app: Sphinx, doctree: Node) -> None:
# Convert signode to a specified format
info = {}
for key in domain_keys.get(domain, []):
for key in _DOMAIN_KEYS.get(domain, []):
value = signode.get(key)
if not value:
value = ''

View File

@ -1,6 +1,8 @@
import sys
from pathlib import Path
from sphinx.ext.linkcode import add_linkcode_domain
sys.path.insert(0, str(Path.cwd().resolve()))
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
@ -19,5 +21,10 @@ if 'test_linkcode' in tags: # NoQA: F821 (tags is injected into conf.py)
return 'https://foobar/js/' + info['fullname']
elif domain in {'c', 'cpp'}:
return f'https://foobar/{domain}/{"".join(info["names"])}'
elif domain == 'rst':
return 'http://foobar/rst/{fullname}'.format(**info)
else:
raise AssertionError
def setup(app):
add_linkcode_domain('rst', ['fullname'])

View File

@ -167,3 +167,10 @@ CPP domain
.. cpp:function:: T& operator[]( unsigned j )
const T& operator[]( unsigned j ) const
rST domain
==========
.. rst:role:: foo
Foo description.

View File

@ -116,6 +116,7 @@ def test_linkcode(app):
assert 'https://foobar/js/' in stuff
assert 'https://foobar/c/' in stuff
assert 'https://foobar/cpp/' in stuff
assert 'http://foobar/rst/' in stuff
@pytest.mark.sphinx('html', testroot='ext-viewcode-find', freshenv=True)