Skip to content

Commit f07bf58

Browse files
committedFeb 11, 2025
feat: Support cross-referencing constructor parameters in instance attribute values
1 parent 24afd5d commit f07bf58

File tree

3 files changed

+47
-18
lines changed

3 files changed

+47
-18
lines changed
 

‎src/mkdocstrings_handlers/python/handler.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,11 @@ def update_env(self, config: Any) -> None: # noqa: ARG002
305305
self.env.tests["existing_template"] = lambda template_name: template_name in self.env.list_templates()
306306

307307
def get_aliases(self, identifier: str) -> tuple[str, ...]: # noqa: D102 (ignore missing docstring)
308+
if "(" in identifier:
309+
identifier, parameter = identifier.split("(", 1)
310+
parameter.removesuffix(")")
311+
else:
312+
parameter = ""
308313
try:
309314
data = self._modules_collection[identifier]
310315
except (KeyError, AliasResolutionError):
@@ -315,7 +320,9 @@ def get_aliases(self, identifier: str) -> tuple[str, ...]: # noqa: D102 (ignore
315320
if alias not in aliases:
316321
aliases.append(alias)
317322
except AliasResolutionError:
318-
return tuple(aliases)
323+
pass
324+
if parameter:
325+
return tuple(f"{alias}({parameter})" for alias in aliases)
319326
return tuple(aliases)
320327

321328
def normalize_extension_paths(self, extensions: Sequence) -> Sequence:

‎src/mkdocstrings_handlers/python/rendering.py

+36-15
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
from mkdocstrings.loggers import get_logger
3333

3434
if TYPE_CHECKING:
35-
from collections.abc import Sequence
35+
from collections.abc import Iterator, Sequence
3636

3737
from griffe import Attribute, Class, Function, Module
3838
from jinja2 import Environment, Template
@@ -326,26 +326,47 @@ def repl(match: Match) -> str:
326326
return Markup(text).format(**variables)
327327

328328

329-
def do_split_path(path: str, full_path: str) -> list[tuple[str, str]]:
329+
_split_path_re = re.compile(r"([.(]?)([\w]+)(\))?")
330+
_splitable_re = re.compile(r"[().]")
331+
332+
333+
def do_split_path(path: str, full_path: str) -> Iterator[tuple[str, str, str, str]]:
330334
"""Split object paths for building cross-references.
331335
332336
Parameters:
333337
path: The path to split.
338+
full_path: The full path, used to compute correct paths for each part of the path.
334339
335-
Returns:
336-
A list of pairs (title, full path).
340+
Yields:
341+
4-tuples: prefix, word, full path, suffix.
337342
"""
338-
if "." not in path:
339-
return [(path, full_path)]
340-
pairs = []
341-
full_path = ""
342-
for part in path.split("."):
343-
if full_path:
344-
full_path += f".{part}"
345-
else:
346-
full_path = part
347-
pairs.append((part, full_path))
348-
return pairs
343+
# Path is a single word, yield full path directly.
344+
if not _splitable_re.search(path):
345+
yield ("", path, full_path, "")
346+
return
347+
348+
current_path = ""
349+
if path == full_path:
350+
# Split full path and yield directly without storing data in a dict.
351+
for match in _split_path_re.finditer(full_path):
352+
prefix, word, suffix = match.groups()
353+
current_path = f"{current_path}{prefix}{word}{suffix or ''}" if current_path else word
354+
yield prefix or "", word, current_path, suffix or ""
355+
return
356+
357+
# Split full path first to store tuples in a dict.
358+
elements = {}
359+
for match in _split_path_re.finditer(full_path):
360+
prefix, word, suffix = match.groups()
361+
current_path = f"{current_path}{prefix}{word}{suffix or ''}" if current_path else word
362+
elements[word] = (prefix or "", word, current_path, suffix or "")
363+
364+
# Then split path and pick tuples from the dict.
365+
first = True
366+
for match in _split_path_re.finditer(path):
367+
prefix, word, current_path, suffix = elements[match.group(2)]
368+
yield "" if first else prefix, word, current_path, suffix
369+
first = False
349370

350371

351372
def _keep_object(name: str, filters: Sequence[tuple[Pattern, bool]]) -> bool:

‎src/mkdocstrings_handlers/python/templates/material/_base/expression.html.jinja

+3-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ which is a tree-like structure representing a Python expression.
3131
{%- elif annotation_path == "full" -%}
3232
{%- set annotation = full -%}
3333
{%- endif -%}
34-
{%- for title, path in annotation|split_path(full) -%}
34+
{%- for prefix, title, path, suffix in annotation|split_path(full) -%}
35+
{{ prefix }}
3536
{%- if not signature -%}
3637
{#- Always render cross-references outside of signatures. We don't need to stash them. -#}
3738
<autoref identifier="{{ path }}" optional{% if title != path %} hover{% endif %}>{{ title }}</autoref>
@@ -44,7 +45,7 @@ which is a tree-like structure representing a Python expression.
4445
{#- We're in a signature but cross-references are disabled, we just render the title. -#}
4546
{{ title }}
4647
{%- endif -%}
47-
{%- if not loop.last -%}.{%- endif -%}
48+
{{ suffix }}
4849
{%- endfor -%}
4950
{%- endwith -%}
5051
{%- endmacro -%}

0 commit comments

Comments
 (0)
Please sign in to comment.