Skip to content

Update handler for mkdocstrings 0.28 #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 41 additions & 28 deletions mkdocstrings_handlers/vba/_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

from __future__ import annotations

import copy
from collections import ChainMap
from pathlib import Path
from typing import (
Any,
Expand All @@ -16,7 +14,7 @@
)

from griffe import patch_loggers
from markdown import Markdown
from mkdocs.config.defaults import MkDocsConfig
from mkdocs.exceptions import PluginError
from mkdocstrings.handlers.base import BaseHandler, CollectionError
from mkdocstrings.loggers import get_logger
Expand Down Expand Up @@ -107,6 +105,17 @@ def __init__(self, *, base_dir: Path, encoding: str, **kwargs: Any) -> None:
**`docstring_section_style`** | `str` | The style used to render docstring sections. Options: `table`, `list`, `spacy`. | `table`
"""

def get_options(self, local_options: Mapping[str, Any]) -> Dict[str, Any]:
"""Combine the default options with the local options.

Arguments:
local_options: The options provided in Markdown pages.

Returns:
The combined options.
"""
return {**self.default_config, **local_options}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to deepcopy here, but I've been bitten by this previously. This creates a new dict, but with references to mutable structures from both default config and local options. The only time config is mutated is below in render with config["members_order"] = Order(config["members_order"]), which I believe doesn't cause any issue.

We can still deepcopy here just to be safe. Let me know.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the real solution is to not mutate the config at all. Otherwise yes, let's use deepcopy.


def collect(
self,
identifier: str,
Expand Down Expand Up @@ -141,75 +150,79 @@ def collect(
def render(
self,
data: VbaModuleInfo,
config: Mapping[str, Any],
config: MutableMapping[str, Any],
) -> str:
final_config = ChainMap(dict(copy.deepcopy(config)), self.default_config)
template = self.env.get_template(f"module.html")

# Heading level is a "state" variable, that will change at each step
# of the rendering recursion. Therefore, it's easier to use it as a plain value
# than as an item in a dictionary.
heading_level = final_config["heading_level"]
heading_level = config["heading_level"]
try:
final_config["members_order"] = Order(final_config["members_order"])
config["members_order"] = Order(config["members_order"])
except ValueError:
choices = "', '".join(item.value for item in Order)
raise PluginError(
f"Unknown members_order '{final_config['members_order']}', choose between '{choices}'."
f"Unknown members_order '{config['members_order']}', choose between '{choices}'."
)

return template.render(
config=final_config,
config=config,
module=data,
heading_level=heading_level,
root=True,
)

def update_env(self, md: Markdown, config: Dict[Any, Any]) -> None:
super().update_env(md, config)
def update_env(self, config: Dict[Any, Any]) -> None:
self.env.trim_blocks = True
self.env.lstrip_blocks = True
self.env.keep_trailing_newline = False
self.env.filters["crossref"] = do_crossref
self.env.filters["multi_crossref"] = do_multi_crossref
self.env.filters["order_members"] = do_order_members

def get_anchors(self, data: VbaModuleInfo) -> Tuple[str, ...]:
def get_aliases(self, identifier: str) -> Tuple[str, ...]:
"""Get the aliases of the given identifier.

Aliases are used to register secondary URLs in mkdocs-autorefs,
to make sure cross-references work with any location of the same object.

Arguments:
identifier: The identifier to get aliases for.

Returns:
A tuple of aliases for the given identifier.
"""
try:
data = self.collect(identifier, {})
except CollectionError:
return ()
return data.path.as_posix(), *(p.signature.name for p in data.procedures)
Comment on lines +196 to 200
Copy link
Member Author

@pawamoy pawamoy Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have replicated previous behavior here, but I have to note that this seems incorrect to return both the path of the file, as well as all the procedure names within it. It's possible that the previous method (get_anchors) was confusing, and the "anchors" concept not well explained. The renaming to get_aliases is to make it more clear: it's not really about HTML anchors, it's about obtaining all the different "aliases" ("locations") for a given object, to correctly populate mkdocs-autorefs data. I know nothing about VBA, but surely Procedure1 is not an alias of Procedure2?

Aliases (and the identifier) are supposed to be strings that you can pass to collect.

Here I'd either just return (data.path.as_posix(),) (or even an empty tuple ()), or add a way to support collecting Procedure1 without a file path (or these procedure objects should expose fully qualified names that can be collected).



def get_handler(
*,
theme: str = "material",
custom_templates: str | None = None,
config_file_path: str | None = None,
encoding: str = "latin1",
tool_config: MkDocsConfig | None = None,
**kwargs: Any,
) -> VbaHandler:
"""
Get a new `VbaHandler`.

Arguments:
theme: The theme to use when rendering contents.
custom_templates: Directory containing custom templates.
config_file_path: The MkDocs configuration file path.
encoding:
The encoding to use when reading VBA files.
Excel exports .bas and .cls files as `latin1`.
See https://en.wikipedia.org/wiki/ISO/IEC_8859-1 .
tool_config: SSG configuration.
kwargs: Extra keyword arguments that we don't use.

Returns:
An instance of `VbaHandler`.
"""
return VbaHandler(
base_dir=(
Path(config_file_path).resolve().parent
if config_file_path
else Path(".").resolve()
),
encoding=encoding,
handler="vba",
theme=theme,
custom_templates=custom_templates,
base_dir = (
Path(getattr(tool_config, "config_file_path", None) or "./mkdocs.yml")
.resolve()
.parent
)
return VbaHandler(base_dir=base_dir, encoding=encoding, **kwargs)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"setuptools_scm",
],
install_requires=[
"mkdocstrings>=0.26.1,<1",
"mkdocstrings>=0.28,<1",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0.28 is not published yet so it will probably fail in CI.

"griffe>=1.3.1,<2",
"mkdocs-material>=9.2,<10",
],
Expand Down
Loading