Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/src/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ Configuration
Type: ``bool``. Issue warning when failing to resolve the canonical location
of an object that a code element references. Defaults to :code:`False`.

.. confval:: codeautolink_warn_on_no_backreference

Type: ``bool``. Issue warning when no backreference could be found
from reference documentation using the :rst:dir:`autolink-examples` table.
This highlights objects for which no tutorial, example or how-to exists.
Defaults to :code:`False`.

Directives
----------
.. rst:directive:: .. autolink-examples:: object
Expand Down
5 changes: 4 additions & 1 deletion docs/src/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ These release notes are based on
sphinx-codeautolink adheres to
`Semantic Versioning <https://semver.org>`_.

0.16.3 (unreleased)
0.17.0 (unreleased)
-------------------
- Introduce :confval:`codeautolink_warn_on_no_backreference` to highlight
where reference documentation does not appear to have
a corresponding tutorial or how-to (:issue:`161`)
- Add more Pygments lexer aliases in code blocks (:issue:`160`)

0.16.2 (2025-01-16)
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ extend-ignore = [
"COM812", # recommended by ruff format
"ISC001", # recommended by ruff format
"PLR0913", # many arguments is fine
"PLR0915", # many statements is fine
]
extend-unsafe-fixes = ["F401"]
isort.split-on-trailing-comma = false
Expand Down
6 changes: 6 additions & 0 deletions src/sphinx_codeautolink/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ def setup(app: Sphinx):
rebuild="html",
types=[bool],
)
app.add_config_value(
"codeautolink_warn_on_no_backreference",
default=False,
rebuild="html",
types=[bool],
)

app.add_directive("autolink-concat", directive.Concat)
app.add_directive("autolink-examples", directive.Examples)
Expand Down
8 changes: 7 additions & 1 deletion src/sphinx_codeautolink/extension/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ def __init__(self) -> None:
self.inventory_map: dict[str, str] = {}
self.warn_missing_inventory = None
self.warn_failed_resolve = None
self.warn_no_backreference = None

# Populated once
self.outdated_docs: set[str] = set()
Expand Down Expand Up @@ -103,6 +104,7 @@ def build_inited(self, app) -> None:
self.inventory_map = app.config.codeautolink_inventory_map
self.warn_missing_inventory = app.config.codeautolink_warn_on_missing_inventory
self.warn_failed_resolve = app.config.codeautolink_warn_on_failed_resolve
self.warn_no_backreference = app.config.codeautolink_warn_on_no_backreference

# Append static resources path so references in setup() are valid
app.config.html_static_path.append(
Expand Down Expand Up @@ -255,7 +257,11 @@ def generate_backref_tables(self, app, doctree, docname):
rm_vis = RemoveExtensionVisitor(doctree)
return doctree.walkabout(rm_vis)

visitor = CodeRefsVisitor(doctree, code_refs=self.code_refs)
visitor = CodeRefsVisitor(
doctree,
code_refs=self.code_refs,
warn_no_backreference=self.warn_no_backreference,
)
doctree.walk(visitor)
return None

Expand Down
14 changes: 13 additions & 1 deletion src/sphinx_codeautolink/extension/backref.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

from docutils import nodes

from sphinx_codeautolink.warn import logger, warn_type

from .directive import DeferredExamples


Expand Down Expand Up @@ -56,10 +58,15 @@ class CodeRefsVisitor(nodes.SparseNodeVisitor):
"""Replace :class:`DeferredCodeReferences` with table of concrete references."""

def __init__(
self, *args, code_refs: dict[str, list[CodeExample]], **kwargs
self,
*args,
code_refs: dict[str, list[CodeExample]],
warn_no_backreference: bool = False,
**kwargs,
) -> None:
super().__init__(*args, **kwargs)
self.code_refs = code_refs
self.warn_no_backreference = warn_no_backreference

def unknown_departure(self, node) -> None:
"""Ignore unknown nodes."""
Expand All @@ -79,6 +86,11 @@ def unknown_visit(self, node) -> None:
items = sorted(set(items))

if not items:
if self.warn_no_backreference:
msg = f"No backreference for: '{node.ref}'"
logger.warning(
msg, type=warn_type, subtype="no_backreference", location=node
)
# Remove surrounding paragraph too
node.parent.parent.remove(node.parent)
return
Expand Down
1 change: 1 addition & 0 deletions tests/extension/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
}
codeautolink_warn_on_missing_inventory = True
codeautolink_warn_on_failed_resolve = True
codeautolink_warn_on_no_backreference = False
"""

any_whitespace = re.compile(r"\s*")
Expand Down
9 changes: 9 additions & 0 deletions tests/extension/fail/no_backreference.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
codeautolink_warn_on_no_backreference = True
# split
Test project
============

.. code:: python

import test_project
test_project.bar()