diff options
Diffstat (limited to 'sphinx/ext/extlinks.py')
| -rw-r--r-- | sphinx/ext/extlinks.py | 41 |
1 files changed, 41 insertions, 0 deletions
diff --git a/sphinx/ext/extlinks.py b/sphinx/ext/extlinks.py index 0af335686..71591febb 100644 --- a/sphinx/ext/extlinks.py +++ b/sphinx/ext/extlinks.py @@ -25,6 +25,7 @@ :license: BSD, see LICENSE for details. """ +import re import warnings from typing import Any, Dict, List, Tuple @@ -35,9 +36,48 @@ from docutils.parsers.rst.states import Inliner import sphinx from sphinx.application import Sphinx from sphinx.deprecation import RemovedInSphinx60Warning +from sphinx.locale import __ +from sphinx.transforms.post_transforms import SphinxPostTransform +from sphinx.util import logging from sphinx.util.nodes import split_explicit_title from sphinx.util.typing import RoleFunction +logger = logging.getLogger(__name__) + + +class ExternalLinksChecker(SphinxPostTransform): + """ + For each external link, check if it can be replaced by an extlink. + + We treat each ``reference`` node without ``internal`` attribute as an external link. + """ + + default_priority = 500 + + def run(self, **kwargs: Any) -> None: + for refnode in self.document.traverse(nodes.reference): + self.check_uri(refnode) + + def check_uri(self, refnode: nodes.reference) -> None: + """ + If the URI in ``refnode`` has a replacement in ``extlinks``, + emit a warning with a replacement suggestion. + """ + if 'internal' in refnode or 'refuri' not in refnode: + return + + uri = refnode['refuri'] + + for alias, (base_uri, caption) in self.app.config.extlinks.items(): + uri_pattern = re.compile(base_uri.replace('%s', '(?P<value>.+)')) + match = uri_pattern.match(uri) + if match and match.groupdict().get('value'): + # build a replacement suggestion + msg = __('hardcoded link %r could be replaced by an extlink ' + '(try using %r instead)') + replacement = f":{alias}:`{match.groupdict().get('value')}`" + logger.warning(msg, uri, replacement, location=refnode) + def make_link_role(name: str, base_url: str, caption: str) -> RoleFunction: # Check whether we have base_url and caption strings have an '%s' for @@ -85,4 +125,5 @@ def setup_link_roles(app: Sphinx) -> None: def setup(app: Sphinx) -> Dict[str, Any]: app.add_config_value('extlinks', {}, 'env') app.connect('builder-inited', setup_link_roles) + app.add_post_transform(ExternalLinksChecker) return {'version': sphinx.__display_version__, 'parallel_read_safe': True} |
