Skip to content

Commit

Permalink
refactor: Return multiple identifiers from fallback method
Browse files Browse the repository at this point in the history
Since an object can have aliases (different identifiers
leading to it), and since users sometimes want to
render an object using one of its aliases instead of its
canonical identifier, we make sure to register every
identifier associated to an object so that autorefs
can find it when fixing cross-references.

Issue mkdocstrings/autorefs#11: mkdocstrings/autorefs#11
  • Loading branch information
pawamoy committed Nov 28, 2021
1 parent 1353e4d commit d7eca90
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 20 deletions.
13 changes: 8 additions & 5 deletions src/mkdocstrings/extension.py
Expand Up @@ -117,16 +117,19 @@ def run(self, parent: Element, blocks: MutableSequence[str]) -> None:
heading_level = match["heading"].count("#")
log.debug(f"Matched '::: {identifier}'")

html, handler = self._process_block(identifier, block, heading_level)
html, handler, data = self._process_block(identifier, block, heading_level)
el = Element("div", {"class": "mkdocstrings"})
# The final HTML is inserted as opaque to subsequent processing, and only revealed at the end.
el.text = self.md.htmlStash.store(html)
# So we need to duplicate the headings directly (and delete later), just so 'toc' can pick them up.
headings = handler.renderer.get_headings()
el.extend(headings)

page = self._autorefs.current_page
for anchor in handler.renderer.get_anchors(data):
self._autorefs.register_anchor(page, anchor)

for heading in headings:
page = self._autorefs.current_page
anchor = heading.attrib["id"]
self._autorefs.register_anchor(page, anchor)

Expand All @@ -146,7 +149,7 @@ def run(self, parent: Element, blocks: MutableSequence[str]) -> None:
# list for future processing.
blocks.insert(0, the_rest)

def _process_block(self, identifier: str, yaml_block: str, heading_level: int = 0) -> Tuple[str, BaseHandler]:
def _process_block(self, identifier: str, yaml_block: str, heading_level: int = 0) -> Tuple[str, BaseHandler, CollectorItem]:
"""Process an autodoc block.
Arguments:
Expand All @@ -159,7 +162,7 @@ def _process_block(self, identifier: str, yaml_block: str, heading_level: int =
TemplateNotFound: When a template used for rendering could not be found.
Returns:
Rendered HTML and the handler that was used.
Rendered HTML, the handler that was used, and the collected item.
"""
config = yaml.safe_load(yaml_block) or {}
handler_name = self._handlers.get_handler_name(config)
Expand Down Expand Up @@ -196,7 +199,7 @@ def _process_block(self, identifier: str, yaml_block: str, heading_level: int =
)
raise

return (rendered, handler)
return rendered, handler, data


def get_item_configs(handler_config: dict, config: dict) -> Tuple[Mapping, Mapping]:
Expand Down
21 changes: 9 additions & 12 deletions src/mkdocstrings/handlers/base.py
Expand Up @@ -125,17 +125,14 @@ def render(self, data: CollectorItem, config: dict) -> str:
The rendered template as HTML.
""" # noqa: DAR202 (excess return section)

def get_anchor(self, data: CollectorItem) -> Optional[str]:
"""Return the canonical identifier (HTML anchor) for a collected item.
This must match what the renderer would've actually rendered,
e.g. if rendering the item contains `<h2 id="foo">...` then the return value should be "foo".
def get_anchors(self, data: CollectorItem) -> List[str]:
"""Return the possible identifiers (HTML anchors) for a collected item.
Arguments:
data: The collected data.
Returns:
The HTML anchor (without '#') as a string, or None if this item doesn't have an anchor.
The HTML anchors (without '#'), or an empty list if this item doesn't have an anchor.
""" # noqa: DAR202 (excess return section)

def do_convert_markdown(self, text: str, heading_level: int, html_id: str = "") -> Markup:
Expand Down Expand Up @@ -329,23 +326,23 @@ def __init__(self, config: dict) -> None:
self._handlers: Dict[str, BaseHandler] = {}
self.inventory: Inventory = Inventory(project=self._config["site_name"])

def get_anchor(self, identifier: str) -> Optional[str]:
def get_anchors(self, identifier: str) -> List[str]:
"""Return the canonical HTML anchor for the identifier, if any of the seen handlers can collect it.
Arguments:
identifier: The identifier (one that [collect][mkdocstrings.handlers.base.BaseCollector.collect] can accept).
Returns:
A string - anchor without '#', or None if there isn't any identifier familiar with it.
A list of strings - anchors without '#', or an empty list if there isn't any identifier familiar with it.
"""
for handler in self._handlers.values():
try:
anchor = handler.renderer.get_anchor(handler.collector.collect(identifier, {}))
anchors = handler.renderer.get_anchors(handler.collector.collect(identifier, {}))
except CollectionError:
continue
if anchor is not None:
return anchor
return None
if anchors:
return anchors
return []

def get_handler_name(self, config: dict) -> str:
"""Return the handler name defined in an "autodoc" instruction YAML configuration, or the global default handler.
Expand Down
7 changes: 5 additions & 2 deletions src/mkdocstrings/handlers/python.py
Expand Up @@ -95,8 +95,11 @@ def render(self, data: CollectorItem, config: dict) -> str: # noqa: D102 (ignor
**{"config": final_config, data["category"]: data, "heading_level": heading_level, "root": True},
)

def get_anchor(self, data: CollectorItem) -> str: # noqa: D102 (ignore missing docstring)
return data.get("path")
def get_anchors(self, data: CollectorItem) -> List[str]: # noqa: D102 (ignore missing docstring)
try:
return [data["path"]]
except KeyError:
return []

def update_env(self, md: Markdown, config: dict) -> None: # noqa: D102 (ignore missing docstring)
super().update_env(md, config)
Expand Down
2 changes: 1 addition & 1 deletion src/mkdocstrings/plugin.py
Expand Up @@ -183,7 +183,7 @@ def on_config(self, config: Config, **kwargs) -> Config: # noqa: W0613 (unused
config["plugins"]["autorefs"] = autorefs
log.debug(f"Added a subdued autorefs instance {autorefs!r}")
# Add collector-based fallback in either case.
autorefs.get_fallback_anchor = self.handlers.get_anchor
autorefs.get_fallback_anchor = self.handlers.get_anchors

mkdocstrings_extension = MkdocstringsExtension(extension_config, self.handlers, autorefs)
config["markdown_extensions"].append(mkdocstrings_extension)
Expand Down

0 comments on commit d7eca90

Please sign in to comment.