Skip to content

Commit

Permalink
feat: Support cross-referencing constructor parameters in instance at…
Browse files Browse the repository at this point in the history
…tribute values
  • Loading branch information
pawamoy committed Feb 11, 2025
1 parent 24afd5d commit f07bf58
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 18 deletions.
9 changes: 8 additions & 1 deletion src/mkdocstrings_handlers/python/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,11 @@ def update_env(self, config: Any) -> None: # noqa: ARG002
self.env.tests["existing_template"] = lambda template_name: template_name in self.env.list_templates()

def get_aliases(self, identifier: str) -> tuple[str, ...]: # noqa: D102 (ignore missing docstring)
if "(" in identifier:
identifier, parameter = identifier.split("(", 1)
parameter.removesuffix(")")
else:
parameter = ""
try:
data = self._modules_collection[identifier]
except (KeyError, AliasResolutionError):
Expand All @@ -315,7 +320,9 @@ def get_aliases(self, identifier: str) -> tuple[str, ...]: # noqa: D102 (ignore
if alias not in aliases:
aliases.append(alias)
except AliasResolutionError:
return tuple(aliases)
pass
if parameter:
return tuple(f"{alias}({parameter})" for alias in aliases)
return tuple(aliases)

def normalize_extension_paths(self, extensions: Sequence) -> Sequence:
Expand Down
51 changes: 36 additions & 15 deletions src/mkdocstrings_handlers/python/rendering.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from mkdocstrings.loggers import get_logger

if TYPE_CHECKING:
from collections.abc import Sequence
from collections.abc import Iterator, Sequence

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


def do_split_path(path: str, full_path: str) -> list[tuple[str, str]]:
_split_path_re = re.compile(r"([.(]?)([\w]+)(\))?")
_splitable_re = re.compile(r"[().]")


def do_split_path(path: str, full_path: str) -> Iterator[tuple[str, str, str, str]]:
"""Split object paths for building cross-references.
Parameters:
path: The path to split.
full_path: The full path, used to compute correct paths for each part of the path.
Returns:
A list of pairs (title, full path).
Yields:
4-tuples: prefix, word, full path, suffix.
"""
if "." not in path:
return [(path, full_path)]
pairs = []
full_path = ""
for part in path.split("."):
if full_path:
full_path += f".{part}"
else:
full_path = part
pairs.append((part, full_path))
return pairs
# Path is a single word, yield full path directly.
if not _splitable_re.search(path):
yield ("", path, full_path, "")
return

current_path = ""
if path == full_path:
# Split full path and yield directly without storing data in a dict.
for match in _split_path_re.finditer(full_path):
prefix, word, suffix = match.groups()
current_path = f"{current_path}{prefix}{word}{suffix or ''}" if current_path else word
yield prefix or "", word, current_path, suffix or ""
return

# Split full path first to store tuples in a dict.
elements = {}
for match in _split_path_re.finditer(full_path):
prefix, word, suffix = match.groups()
current_path = f"{current_path}{prefix}{word}{suffix or ''}" if current_path else word
elements[word] = (prefix or "", word, current_path, suffix or "")

# Then split path and pick tuples from the dict.
first = True
for match in _split_path_re.finditer(path):
prefix, word, current_path, suffix = elements[match.group(2)]
yield "" if first else prefix, word, current_path, suffix
first = False


def _keep_object(name: str, filters: Sequence[tuple[Pattern, bool]]) -> bool:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ which is a tree-like structure representing a Python expression.
{%- elif annotation_path == "full" -%}
{%- set annotation = full -%}
{%- endif -%}
{%- for title, path in annotation|split_path(full) -%}
{%- for prefix, title, path, suffix in annotation|split_path(full) -%}
{{ prefix }}
{%- if not signature -%}
{#- Always render cross-references outside of signatures. We don't need to stash them. -#}
<autoref identifier="{{ path }}" optional{% if title != path %} hover{% endif %}>{{ title }}</autoref>
Expand All @@ -44,7 +45,7 @@ which is a tree-like structure representing a Python expression.
{#- We're in a signature but cross-references are disabled, we just render the title. -#}
{{ title }}
{%- endif -%}
{%- if not loop.last -%}.{%- endif -%}
{{ suffix }}
{%- endfor -%}
{%- endwith -%}
{%- endmacro -%}
Expand Down

0 comments on commit f07bf58

Please sign in to comment.