Skip to content
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

Drop support for MkDocs < 1.4, modernize usages #629

Merged
merged 2 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ dependencies = [
"Jinja2>=2.11.1",
"Markdown>=3.3",
"MarkupSafe>=1.1",
"mkdocs>=1.2",
"mkdocs>=1.4",
"mkdocs-autorefs>=0.3.1",
"pymdown-extensions>=6.3",
"importlib-metadata>=4.6; python_version < '3.10'",
Expand Down
3 changes: 1 addition & 2 deletions src/mkdocstrings/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ def __init__(
Arguments:
parser: A `markdown.blockparser.BlockParser` instance.
md: A `markdown.Markdown` instance.
config: The [configuration][mkdocstrings.plugin.MkdocstringsPlugin.config_scheme]
of the `mkdocstrings` plugin.
config: The [configuration][mkdocstrings.plugin.PluginConfig] of the `mkdocstrings` plugin.
handlers: The handlers container.
autorefs: The autorefs plugin instance.
"""
Expand Down
94 changes: 46 additions & 48 deletions src/mkdocstrings/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
from typing import TYPE_CHECKING, Any, BinaryIO, Callable, Iterable, List, Mapping, Tuple, TypeVar
from urllib import request

from mkdocs.config.config_options import Type as MkType
from mkdocs.config.config_options import Dir, Optional
from mkdocs.config import Config
from mkdocs.config import config_options as opt
from mkdocs.plugins import BasePlugin
from mkdocs.utils import write_file
from mkdocs_autorefs.plugin import AutorefsPlugin
Expand All @@ -34,7 +34,6 @@

if TYPE_CHECKING:
from jinja2.environment import Environment
from mkdocs.config import Config
from mkdocs.config.defaults import MkDocsConfig

if sys.version_info < (3, 10):
Expand Down Expand Up @@ -63,38 +62,13 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
return wrapper


class MkdocstringsPlugin(BasePlugin):
"""An `mkdocs` plugin.

This plugin defines the following event hooks:

- `on_config`
- `on_env`
- `on_post_build`
class PluginConfig(Config):
"""The configuration options of `mkdocstrings`, written in `mkdocs.yml`."""

Check the [Developing Plugins](https://www.mkdocs.org/user-guide/plugins/#developing-plugins) page of `mkdocs`
for more information about its plugin system.
"""

config_scheme: tuple[tuple[str, MkType]] = ( # type: ignore[assignment]
("handlers", MkType(dict, default={})),
("default_handler", MkType(str, default="python")),
("custom_templates", Optional(Dir(exists=True))),
("enable_inventory", MkType(bool, default=None)),
("enabled", MkType(bool, default=True)),
)
handlers = opt.Type(dict, default={})
"""
The configuration options of `mkdocstrings`, written in `mkdocs.yml`.

Available options are:

- **`handlers`**: Global configuration of handlers. You can set global configuration per handler, applied everywhere,
but overridable in each "autodoc" instruction. Example:
- **`default_handler`**: The default handler to use. The value is the name of the handler module. Default is "python".
- **`custom_templates`**: Location of custom templates to use when rendering API objects. Value should be the path of
a directory relative to the MkDocs configuration file.
- **`enable_inventory`**: Whether to enable object inventory creation.
- **`enabled`**: Whether to enable the plugin. Default is true. If false, *mkdocstrings* will not collect or render anything.
Global configuration of handlers. You can set global configuration per handler, applied everywhere,
but overridable in each "autodoc" instruction. Example:
oprypin marked this conversation as resolved.
Show resolved Hide resolved

```yaml
plugins:
Expand All @@ -110,6 +84,30 @@ class MkdocstringsPlugin(BasePlugin):
```
"""

default_handler = opt.Type(str, default="python")
"""The default handler to use. The value is the name of the handler module. Default is "python"."""
custom_templates = opt.Optional(opt.Dir(exists=True)),
"""Location of custom templates to use when rendering API objects. Value should be the path of
a directory relative to the MkDocs configuration file."""
oprypin marked this conversation as resolved.
Show resolved Hide resolved
enable_inventory = opt.Optional(opt.Type(bool))
"""Whether to enable object inventory creation."""
enabled = opt.Type(bool, default=True)
"""Whether to enable the plugin. Default is true. If false, *mkdocstrings* will not collect or render anything."""


class MkdocstringsPlugin(BasePlugin[PluginConfig]):
"""An `mkdocs` plugin.

This plugin defines the following event hooks:

- `on_config`
- `on_env`
- `on_post_build`

Check the [Developing Plugins](https://www.mkdocs.org/user-guide/plugins/#developing-plugins) page of `mkdocs`
for more information about its plugin system.
"""

css_filename = "assets/_mkdocstrings.css"

def __init__(self) -> None:
Expand Down Expand Up @@ -152,41 +150,41 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None:
return config
log.debug("Adding extension to the list")

theme_name = config["theme"].name or os.path.dirname(config["theme"].dirs[0])
theme_name = config.theme.name or os.path.dirname(config.theme.dirs[0])

to_import: InventoryImportType = []
for handler_name, conf in self.config["handlers"].items():
for handler_name, conf in self.config.handlers.items():
for import_item in conf.pop("import", ()):
if isinstance(import_item, str):
import_item = {"url": import_item} # noqa: PLW2901
to_import.append((handler_name, import_item))

extension_config = {
"theme_name": theme_name,
"mdx": config["markdown_extensions"],
"mdx_configs": config["mdx_configs"],
"mdx": config.markdown_extensions,
"mdx_configs": config.mdx_configs,
"mkdocstrings": self.config,
"mkdocs": config,
}
self._handlers = Handlers(extension_config)

try:
# If autorefs plugin is explicitly enabled, just use it.
autorefs = config["plugins"]["autorefs"]
autorefs = config.plugins["autorefs"]
log.debug(f"Picked up existing autorefs instance {autorefs!r}")
except KeyError:
# Otherwise, add a limited instance of it that acts only on what's added through `register_anchor`.
autorefs = AutorefsPlugin()
autorefs.scan_toc = False
config["plugins"]["autorefs"] = autorefs
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_anchors

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

config["extra_css"].insert(0, self.css_filename) # So that it has lower priority than user files.
config.extra_css.insert(0, self.css_filename) # So that it has lower priority than user files.

self._inv_futures = {}
if to_import:
Expand All @@ -210,7 +208,7 @@ def inventory_enabled(self) -> bool:
Returns:
Whether the inventory is enabled.
"""
inventory_enabled = self.config["enable_inventory"]
inventory_enabled = self.config.enable_inventory
if inventory_enabled is None:
inventory_enabled = any(handler.enable_inventory for handler in self.handlers.seen_handlers)
return inventory_enabled
Expand All @@ -222,9 +220,9 @@ def plugin_enabled(self) -> bool:
Returns:
Whether the plugin is enabled.
"""
return self.config["enabled"]
return self.config.enabled

def on_env(self, env: Environment, config: Config, *args: Any, **kwargs: Any) -> None: # noqa: ARG002
def on_env(self, env: Environment, config: MkDocsConfig, *args: Any, **kwargs: Any) -> None: # noqa: ARG002
"""Extra actions that need to happen after all Markdown rendering and before HTML rendering.

Hook for the [`on_env` event](https://www.mkdocs.org/user-guide/plugins/#on_env).
Expand All @@ -236,12 +234,12 @@ def on_env(self, env: Environment, config: Config, *args: Any, **kwargs: Any) ->
return
if self._handlers:
css_content = "\n".join(handler.extra_css for handler in self.handlers.seen_handlers)
write_file(css_content.encode("utf-8"), os.path.join(config["site_dir"], self.css_filename))
write_file(css_content.encode("utf-8"), os.path.join(config.site_dir, self.css_filename))

if self.inventory_enabled:
log.debug("Creating inventory file objects.inv")
inv_contents = self.handlers.inventory.format_sphinx()
write_file(inv_contents, os.path.join(config["site_dir"], "objects.inv"))
write_file(inv_contents, os.path.join(config.site_dir, "objects.inv"))

if self._inv_futures:
log.debug(f"Waiting for {len(self._inv_futures)} inventory download(s)")
Expand All @@ -256,12 +254,12 @@ def on_env(self, env: Environment, config: Config, *args: Any, **kwargs: Any) ->
loader_name = loader.__func__.__qualname__
log.error(f"Couldn't load inventory {import_item} through {loader_name}: {error}") # noqa: TRY400
for page, identifier in results.items():
config["plugins"]["autorefs"].register_url(page, identifier)
config.plugins["autorefs"].register_url(page, identifier)
self._inv_futures = {}

def on_post_build(
self,
config: Config, # noqa: ARG002
config: MkDocsConfig, # noqa: ARG002
**kwargs: Any, # noqa: ARG002
) -> None:
"""Teardown the handlers.
Expand Down
6 changes: 3 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import pytest
from markdown.core import Markdown
from mkdocs import config
from mkdocs.config.defaults import get_schema
from mkdocs.config.defaults import MkDocsConfig

if TYPE_CHECKING:
from pathlib import Path
Expand All @@ -19,7 +19,7 @@
@pytest.fixture(name="mkdocs_conf")
def fixture_mkdocs_conf(request: pytest.FixtureRequest, tmp_path: Path) -> Iterator[config.Config]:
"""Yield a MkDocs configuration object."""
conf = config.Config(schema=get_schema()) # type: ignore[call-arg]
conf = MkDocsConfig()
while hasattr(request, "_parent_request") and hasattr(request._parent_request, "_parent_request"):
request = request._parent_request

Expand Down Expand Up @@ -53,6 +53,6 @@ def fixture_plugin(mkdocs_conf: config.Config) -> MkdocstringsPlugin:


@pytest.fixture(name="ext_markdown")
def fixture_ext_markdown(mkdocs_conf: config.Config) -> Markdown:
def fixture_ext_markdown(mkdocs_conf: MkDocsConfig) -> Markdown:
"""Return a Markdown instance with MkdocstringsExtension."""
return Markdown(extensions=mkdocs_conf["markdown_extensions"], extension_configs=mkdocs_conf["mdx_configs"])