GitHub

@@ -8,11 +8,12 @@

88

import subprocess

99

import sys

1010

import warnings

11+

from collections import defaultdict

1112

from dataclasses import replace

1213

from functools import lru_cache

1314

from pathlib import Path

1415

from re import Match, Pattern

15-

from typing import TYPE_CHECKING, Any, Callable, ClassVar, Literal

16+

from typing import TYPE_CHECKING, Any, Callable, ClassVar, Literal, TypeVar

16171718

from griffe import (

1819

Alias,

@@ -28,11 +29,11 @@

2829

)

2930

from jinja2 import TemplateNotFound, pass_context, pass_environment

3031

from markupsafe import Markup

31-

from mkdocs_autorefs import AutorefsHookInterface

32+

from mkdocs_autorefs import AutorefsHookInterface, Backlink, BacklinkCrumb

3233

from mkdocstrings import get_logger

33343435

if TYPE_CHECKING:

35-

from collections.abc import Iterator, Sequence

36+

from collections.abc import Iterable, Iterator, Sequence

36373738

from griffe import Attribute, Class, Function, Module

3839

from jinja2 import Environment, Template

@@ -210,10 +211,15 @@ def do_format_attribute(

210211211212

signature = str(attribute_path).strip()

212213

if annotations and attribute.annotation:

213-

annotation = template.render(context.parent, expression=attribute.annotation, signature=True)

214+

annotation = template.render(

215+

context.parent,

216+

expression=attribute.annotation,

217+

signature=True,

218+

backlink_type="returned-by",

219+

)

214220

signature += f": {annotation}"

215221

if attribute.value:

216-

value = template.render(context.parent, expression=attribute.value, signature=True)

222+

value = template.render(context.parent, expression=attribute.value, signature=True, backlink_type="used-by")

217223

signature += f" = {value}"

218224219225

signature = do_format_code(signature, line_length)

@@ -725,3 +731,45 @@ def get_context(self) -> AutorefsHookInterface.Context:

725731

filepath=str(filepath),

726732

lineno=lineno,

727733

)

734+735+736+

T = TypeVar("T")

737+

Tree = dict[T, "Tree"]

738+

CompactTree = dict[tuple[T, ...], "CompactTree"]

739+

_rtree = lambda: defaultdict(_rtree) # type: ignore[has-type,var-annotated] # noqa: E731

740+741+742+

def _tree(data: Iterable[tuple[T, ...]]) -> Tree:

743+

new_tree = _rtree()

744+

for nav in data:

745+

*path, leaf = nav

746+

node = new_tree

747+

for key in path:

748+

node = node[key]

749+

node[leaf] = _rtree()

750+

return new_tree

751+752+753+

def _compact_tree(tree: Tree) -> CompactTree:

754+

new_tree = _rtree()

755+

for key, value in tree.items():

756+

child = _compact_tree(value)

757+

if len(child) == 1:

758+

child_key, child_value = next(iter(child.items()))

759+

new_key = (key, *child_key)

760+

new_tree[new_key] = child_value

761+

else:

762+

new_tree[(key,)] = child

763+

return new_tree

764+765+766+

def do_backlink_tree(backlinks: list[Backlink]) -> CompactTree[BacklinkCrumb]:

767+

"""Build a tree of backlinks.

768+769+

Parameters:

770+

backlinks: The list of backlinks.

771+772+

Returns:

773+

A tree of backlinks.

774+

"""

775+

return _compact_tree(_tree(backlink.crumbs for backlink in backlinks))

Read the original on github.com ↗