Skip to content

Commit bb4be5b

Browse files
committed
feat: Hook into autorefs to provide context around cross-ref errors
1 parent 2d59cdb commit bb4be5b

25 files changed

+138
-77
lines changed

pyproject.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ classifiers = [
2929
"Typing :: Typed",
3030
]
3131
dependencies = [
32-
"mkdocstrings>=0.25",
33-
"mkdocs-autorefs>=1.0",
32+
"mkdocstrings>=0.26",
33+
"mkdocs-autorefs>=1.2",
3434
"griffe>=0.49",
3535
]
3636

src/mkdocstrings_handlers/python/handler.py

+1
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ def update_env(self, md: Markdown, config: dict) -> None:
426426
self.env.filters["as_functions_section"] = rendering.do_as_functions_section
427427
self.env.filters["as_classes_section"] = rendering.do_as_classes_section
428428
self.env.filters["as_modules_section"] = rendering.do_as_modules_section
429+
self.env.globals["AutorefsHook"] = rendering.AutorefsHook
429430
self.env.tests["existing_template"] = lambda template_name: template_name in self.env.list_templates()
430431

431432
def get_anchors(self, data: CollectorItem) -> tuple[str, ...]: # noqa: D102 (ignore missing docstring)

src/mkdocstrings_handlers/python/rendering.py

+58
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
)
2323
from jinja2 import TemplateNotFound, pass_context, pass_environment
2424
from markupsafe import Markup
25+
from mkdocs_autorefs.references import AutorefsHookInterface
2526
from mkdocstrings.loggers import get_logger
2627

2728
if TYPE_CHECKING:
@@ -571,3 +572,60 @@ def do_as_modules_section(
571572
A modules docstring section.
572573
"""
573574
return DocstringSectionModules([])
575+
576+
577+
class AutorefsHook(AutorefsHookInterface):
578+
"""Autorefs hook.
579+
580+
With this hook, we're able to add context to autorefs (cross-references),
581+
such as originating file path and line number, to improve error reporting.
582+
"""
583+
584+
def __init__(self, current_object: Object | Alias, config: dict[str, Any]) -> None:
585+
"""Initialize the hook.
586+
587+
Parameters:
588+
current_object: The object being rendered.
589+
config: The configuration dictionary.
590+
"""
591+
self.current_object = current_object
592+
self.config = config
593+
594+
def expand_identifier(self, identifier: str) -> str:
595+
"""Expand an identifier.
596+
597+
Parameters:
598+
identifier: The identifier to expand.
599+
600+
Returns:
601+
The expanded identifier.
602+
"""
603+
return identifier
604+
605+
def get_context(self) -> AutorefsHookInterface.Context:
606+
"""Get the context for the current object.
607+
608+
Returns:
609+
The context.
610+
"""
611+
role = {
612+
"attribute": "data" if self.current_object.parent and self.current_object.parent.is_module else "attr",
613+
"class": "class",
614+
"function": "meth" if self.current_object.parent and self.current_object.parent.is_class else "func",
615+
"module": "mod",
616+
}.get(self.current_object.kind.value.lower(), "obj")
617+
origin = self.current_object.path
618+
try:
619+
filepath = self.current_object.docstring.parent.filepath # type: ignore[union-attr]
620+
lineno = self.current_object.docstring.lineno or 0 # type: ignore[union-attr]
621+
except AttributeError:
622+
filepath = self.current_object.filepath
623+
lineno = 0
624+
625+
return AutorefsHookInterface.Context(
626+
domain="py",
627+
role=role,
628+
origin=origin,
629+
filepath=str(filepath),
630+
lineno=lineno,
631+
)

src/mkdocstrings_handlers/python/templates/material/_base/docstring.html.jinja

+33-31
Original file line numberDiff line numberDiff line change
@@ -19,35 +19,37 @@ Context:
1919
-#}
2020
{{ log.debug("Rendering docstring") }}
2121
{% endblock logs %}
22-
{% for section in docstring_sections %}
23-
{% if config.show_docstring_description and section.kind.value == "text" %}
24-
{{ section.value|convert_markdown(heading_level, html_id) }}
25-
{% elif config.show_docstring_attributes and section.kind.value == "attributes" %}
26-
{% include "docstring/attributes"|get_template with context %}
27-
{% elif config.show_docstring_functions and section.kind.value == "functions" %}
28-
{% include "docstring/functions"|get_template with context %}
29-
{% elif config.show_docstring_classes and section.kind.value == "classes" %}
30-
{% include "docstring/classes"|get_template with context %}
31-
{% elif config.show_docstring_modules and section.kind.value == "modules" %}
32-
{% include "docstring/modules"|get_template with context %}
33-
{% elif config.show_docstring_parameters and section.kind.value == "parameters" %}
34-
{% include "docstring/parameters"|get_template with context %}
35-
{% elif config.show_docstring_other_parameters and section.kind.value == "other parameters" %}
36-
{% include "docstring/other_parameters"|get_template with context %}
37-
{% elif config.show_docstring_raises and section.kind.value == "raises" %}
38-
{% include "docstring/raises"|get_template with context %}
39-
{% elif config.show_docstring_warns and section.kind.value == "warns" %}
40-
{% include "docstring/warns"|get_template with context %}
41-
{% elif config.show_docstring_yields and section.kind.value == "yields" %}
42-
{% include "docstring/yields"|get_template with context %}
43-
{% elif config.show_docstring_receives and section.kind.value == "receives" %}
44-
{% include "docstring/receives"|get_template with context %}
45-
{% elif config.show_docstring_returns and section.kind.value == "returns" %}
46-
{% include "docstring/returns"|get_template with context %}
47-
{% elif config.show_docstring_examples and section.kind.value == "examples" %}
48-
{% include "docstring/examples"|get_template with context %}
49-
{% elif config.show_docstring_description and section.kind.value == "admonition" %}
50-
{% include "docstring/admonition"|get_template with context %}
51-
{% endif %}
52-
{% endfor %}
22+
{% with autoref_hook = AutorefsHook(obj, config) %}
23+
{% for section in docstring_sections %}
24+
{% if config.show_docstring_description and section.kind.value == "text" %}
25+
{{ section.value|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
26+
{% elif config.show_docstring_attributes and section.kind.value == "attributes" %}
27+
{% include "docstring/attributes"|get_template with context %}
28+
{% elif config.show_docstring_functions and section.kind.value == "functions" %}
29+
{% include "docstring/functions"|get_template with context %}
30+
{% elif config.show_docstring_classes and section.kind.value == "classes" %}
31+
{% include "docstring/classes"|get_template with context %}
32+
{% elif config.show_docstring_modules and section.kind.value == "modules" %}
33+
{% include "docstring/modules"|get_template with context %}
34+
{% elif config.show_docstring_parameters and section.kind.value == "parameters" %}
35+
{% include "docstring/parameters"|get_template with context %}
36+
{% elif config.show_docstring_other_parameters and section.kind.value == "other parameters" %}
37+
{% include "docstring/other_parameters"|get_template with context %}
38+
{% elif config.show_docstring_raises and section.kind.value == "raises" %}
39+
{% include "docstring/raises"|get_template with context %}
40+
{% elif config.show_docstring_warns and section.kind.value == "warns" %}
41+
{% include "docstring/warns"|get_template with context %}
42+
{% elif config.show_docstring_yields and section.kind.value == "yields" %}
43+
{% include "docstring/yields"|get_template with context %}
44+
{% elif config.show_docstring_receives and section.kind.value == "receives" %}
45+
{% include "docstring/receives"|get_template with context %}
46+
{% elif config.show_docstring_returns and section.kind.value == "returns" %}
47+
{% include "docstring/returns"|get_template with context %}
48+
{% elif config.show_docstring_examples and section.kind.value == "examples" %}
49+
{% include "docstring/examples"|get_template with context %}
50+
{% elif config.show_docstring_description and section.kind.value == "admonition" %}
51+
{% include "docstring/admonition"|get_template with context %}
52+
{% endif %}
53+
{% endfor %}
54+
{% endwith %}
5355
{% endif %}

src/mkdocstrings_handlers/python/templates/material/_base/docstring/admonition.html.jinja

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ Context:
1515
{% endblock logs %}
1616

1717
<details class="{{ section.value.kind }}" open>
18-
<summary>{{ section.title|convert_markdown(heading_level, html_id, strip_paragraph=True) }}</summary>
19-
{{ section.value.contents|convert_markdown(heading_level, html_id) }}
18+
<summary>{{ section.title|convert_markdown(heading_level, html_id, strip_paragraph=True, autoref_hook=autoref_hook) }}</summary>
19+
{{ section.value.contents|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
2020
</details>

src/mkdocstrings_handlers/python/templates/material/_base/docstring/attributes.html.jinja

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Context:
4343
</td>
4444
<td>
4545
<div class="doc-md-description">
46-
{{ attribute.description|convert_markdown(heading_level, html_id) }}
46+
{{ attribute.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
4747
</div>
4848
</td>
4949
</tr>
@@ -66,7 +66,7 @@ Context:
6666
{% endif %}
6767
6868
<div class="doc-md-description">
69-
{{ attribute.description|convert_markdown(heading_level, html_id) }}
69+
{{ attribute.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
7070
</div>
7171
</li>
7272
{% endfor %}
@@ -88,7 +88,7 @@ Context:
8888
<td><code><autoref identifier="{{ obj.path }}.{{ attribute.name }}" optional hover>{{ attribute.name }}</autoref></code></td>
8989
<td class="doc-attribute-details">
9090
<div class="doc-md-description">
91-
{{ attribute.description|convert_markdown(heading_level, html_id) }}
91+
{{ attribute.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
9292
</div>
9393
<p>
9494
{% if attribute.annotation %}

src/mkdocstrings_handlers/python/templates/material/_base/docstring/classes.html.jinja

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Context:
3535
<td><code><autoref identifier="{{ obj.path }}.{{ class.name }}" optional hover>{{ class.name }}</autoref></code></td>
3636
<td>
3737
<div class="doc-md-description">
38-
{{ class.description|convert_markdown(heading_level, html_id) }}
38+
{{ class.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
3939
</div>
4040
</td>
4141
</tr>
@@ -53,7 +53,7 @@ Context:
5353
<b><code><autoref identifier="{{ obj.path }}.{{ class.name }}" optional hover>{{ class.name }}</autoref></code></b>
5454
5555
<div class="doc-md-description">
56-
{{ class.description|convert_markdown(heading_level, html_id) }}
56+
{{ class.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
5757
</div>
5858
</li>
5959
{% endfor %}
@@ -75,7 +75,7 @@ Context:
7575
<td><code><autoref identifier="{{ obj.path }}.{{ class.name }}" optional hover>{{ class.name }}</autoref></code></td>
7676
<td class="doc-class-details">
7777
<div class="doc-md-description">
78-
{{ class.description|convert_markdown(heading_level, html_id) }}
78+
{{ class.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
7979
</div>
8080
</td>
8181
</tr>

src/mkdocstrings_handlers/python/templates/material/_base/docstring/examples.html.jinja

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Context:
2121
<p><span class="doc-section-title">{{ section.title or lang.t("Examples:") }}</span></p>
2222
{% for section_type, sub_section in section.value %}
2323
{% if section_type.value == "text" %}
24-
{{ sub_section|convert_markdown(heading_level, html_id) }}
24+
{{ sub_section|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
2525
{% elif section_type.value == "examples" %}
2626
{{ sub_section|highlight(language="pycon", linenums=False) }}
2727
{% endif %}

src/mkdocstrings_handlers/python/templates/material/_base/docstring/functions.html.jinja

+3-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Context:
3636
<td><code><autoref identifier="{{ obj.path }}.{{ function.name }}" optional hover>{{ function.name }}</autoref></code></td>
3737
<td>
3838
<div class="doc-md-description">
39-
{{ function.description|convert_markdown(heading_level, html_id) }}
39+
{{ function.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
4040
</div>
4141
</td>
4242
</tr>
@@ -56,7 +56,7 @@ Context:
5656
<b><code><autoref identifier="{{ obj.path }}.{{ function.name }}" optional hover>{{ function.name }}</autoref></code></b>
5757
5858
<div class="doc-md-description">
59-
{{ function.description|convert_markdown(heading_level, html_id) }}
59+
{{ function.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
6060
</div>
6161
</li>
6262
{% endif %}
@@ -80,7 +80,7 @@ Context:
8080
<td><code><autoref identifier="{{ obj.path }}.{{ function.name }}" optional hover>{{ function.name }}</autoref></code></td>
8181
<td class="doc-function-details">
8282
<div class="doc-md-description">
83-
{{ function.description|convert_markdown(heading_level, html_id) }}
83+
{{ function.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
8484
</div>
8585
</td>
8686
</tr>

src/mkdocstrings_handlers/python/templates/material/_base/docstring/modules.html.jinja

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Context:
3535
<td><code><autoref identifier="{{ obj.path }}.{{ module.name }}" optional hover>{{ module.name }}</autoref></code></td>
3636
<td>
3737
<div class="doc-md-description">
38-
{{ module.description|convert_markdown(heading_level, html_id) }}
38+
{{ module.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
3939
</div>
4040
</td>
4141
</tr>
@@ -53,7 +53,7 @@ Context:
5353
<b><code><autoref identifier="{{ obj.path }}.{{ module.name }}" optional hover>{{ module.name }}</autoref></code></b>
5454
5555
<div class="doc-md-description">
56-
{{ module.description|convert_markdown(heading_level, html_id) }}
56+
{{ module.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
5757
</div>
5858
</li>
5959
{% endfor %}
@@ -75,7 +75,7 @@ Context:
7575
<td><code><autoref identifier="{{ obj.path }}.{{ module.name }}" optional hover>{{ module.name }}</autoref></code></td>
7676
<td class="doc-module-details">
7777
<div class="doc-md-description">
78-
{{ module.description|convert_markdown(heading_level, html_id) }}
78+
{{ module.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
7979
</div>
8080
</td>
8181
</tr>

src/mkdocstrings_handlers/python/templates/material/_base/docstring/other_parameters.html.jinja

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Context:
4343
</td>
4444
<td>
4545
<div class="doc-md-description">
46-
{{ parameter.description|convert_markdown(heading_level, html_id) }}
46+
{{ parameter.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
4747
</div>
4848
</td>
4949
</tr>
@@ -66,7 +66,7 @@ Context:
6666
{% endif %}
6767
6868
<div class="doc-md-description">
69-
{{ parameter.description|convert_markdown(heading_level, html_id) }}
69+
{{ parameter.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
7070
</div>
7171
</li>
7272
{% endfor %}
@@ -88,7 +88,7 @@ Context:
8888
<td><code>{{ parameter.name }}</code></td>
8989
<td class="doc-param-details">
9090
<div class="doc-md-description">
91-
{{ parameter.description|convert_markdown(heading_level, html_id) }}
91+
{{ parameter.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
9292
</div>
9393
<p>
9494
{% if parameter.annotation %}

src/mkdocstrings_handlers/python/templates/material/_base/docstring/parameters.html.jinja

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Context:
4444
</td>
4545
<td>
4646
<div class="doc-md-description">
47-
{{ parameter.description|convert_markdown(heading_level, html_id) }}
47+
{{ parameter.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
4848
</div>
4949
</td>
5050
<td>
@@ -81,7 +81,7 @@ Context:
8181
{% endif %}
8282
8383
<div class="doc-md-description">
84-
{{ parameter.description|convert_markdown(heading_level, html_id) }}
84+
{{ parameter.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
8585
</div>
8686
</li>
8787
{% endfor %}
@@ -103,7 +103,7 @@ Context:
103103
<td><code>{{ parameter.name }}</code></td>
104104
<td class="doc-param-details">
105105
<div class="doc-md-description">
106-
{{ parameter.description|convert_markdown(heading_level, html_id) }}
106+
{{ parameter.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
107107
</div>
108108
<p>
109109
{% if parameter.annotation %}

src/mkdocstrings_handlers/python/templates/material/_base/docstring/raises.html.jinja

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Context:
4141
</td>
4242
<td>
4343
<div class="doc-md-description">
44-
{{ raises.description|convert_markdown(heading_level, html_id) }}
44+
{{ raises.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
4545
</div>
4646
</td>
4747
</tr>
@@ -63,7 +63,7 @@ Context:
6363
6464
{% endif %}
6565
<div class="doc-md-description">
66-
{{ raises.description|convert_markdown(heading_level, html_id) }}
66+
{{ raises.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
6767
</div>
6868
</li>
6969
{% endfor %}
@@ -91,7 +91,7 @@ Context:
9191
</td>
9292
<td class="doc-raises-details">
9393
<div class="doc-md-description">
94-
{{ raises.description|convert_markdown(heading_level, html_id) }}
94+
{{ raises.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
9595
</div>
9696
</td>
9797
</tr>

src/mkdocstrings_handlers/python/templates/material/_base/docstring/receives.html.jinja

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Context:
4444
</td>
4545
<td>
4646
<div class="doc-md-description">
47-
{{ receives.description|convert_markdown(heading_level, html_id) }}
47+
{{ receives.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
4848
</div>
4949
</td>
5050
</tr>
@@ -69,7 +69,7 @@ Context:
6969
{% endif %}
7070
7171
<div class="doc-md-description">
72-
{{ receives.description|convert_markdown(heading_level, html_id) }}
72+
{{ receives.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
7373
</div>
7474
</li>
7575
{% endfor %}
@@ -101,7 +101,7 @@ Context:
101101
</td>
102102
<td class="doc-receives-details">
103103
<div class="doc-md-description">
104-
{{ receives.description|convert_markdown(heading_level, html_id) }}
104+
{{ receives.description|convert_markdown(heading_level, html_id, autoref_hook=autoref_hook) }}
105105
</div>
106106
{% if receives.name and receives.annotation %}
107107
<p>

0 commit comments

Comments
 (0)