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

feat: Let handlers add CSS to the pages, do so for Python handler #218

Merged
merged 4 commits into from
Feb 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
16 changes: 0 additions & 16 deletions docs/css/mkdocstrings.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,3 @@ div.doc-contents:not(.first) {
border-left: 4px solid rgba(230, 230, 230);
margin-bottom: 80px;
}

/* Don't capitalize names. */
h5.doc-heading {
text-transform: none !important;
}

/* Avoid breaking parameters name, etc. in table cells. */
td code {
word-break: normal !important;
}

/* For pieces of Markdown rendered in table cells. */
td p {
margin-top: 0 !important;
margin-bottom: 0 !important;
}
26 changes: 26 additions & 0 deletions docs/handlers/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,32 @@ arguments, and you can add any other keyword argument you'd like.
The global configuration items (other than `selection` and `rendering`)
will be passed to this function when getting your handler.

### Templates

You renderer's implementation should normally be backed by templates, which go
to the directory `mkdocstrings/handlers/custom_handler/some_theme`.
(`custom_handler` here should be replaced with the actual name of your handler,
and `some_theme` should be the name of an actual MkDocs theme that you support,
e.g. `material`).

With that structure, you can use `self.env.get_template("foo.html")` inside
your `render` implementation. This already chooses the subdirectory based on
the current MkDocs theme.

If you wish to support *any* MkDocs theme, rather than a few specifically
selected ones, you can pick one theme's subdirectory to be the fallback for
when an unknown theme is encountered. Then you just need to set the
`fallback_theme` variable on your renderer subclass. The fallback directory can
be used even for themes you explicitly support: you can omit some template from
one of the other theme directories in case they're exactly the same as in the
fallback theme.

If your theme's HTML requires CSS to go along with it, put it into a file named
`mkdocstrings/handlers/custom_handler/some_theme/style.css`, then this will be
included into the final site automatically if this handler is ever used.
Alternatively, you can put the CSS as a string into the `extra_css` variable of
your renderer.

### Usage

When a custom handler is installed,
Expand Down
44 changes: 0 additions & 44 deletions docs/handlers/python.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,22 +329,6 @@ div.doc-contents:not(.first) {
border-left: 4px solid rgba(230, 230, 230);
margin-bottom: 80px;
}

/* Don't capitalize names. */
h5.doc-heading {
text-transform: none !important;
}

/* Avoid breaking parameters name, etc. in table cells. */
td code {
word-break: normal !important;
}

/* For pieces of Markdown rendered in table cells. */
td p {
margin-top: 0 !important;
margin-bottom: 0 !important;
}
```

## Recommended style (ReadTheDocs)
Expand All @@ -358,32 +342,4 @@ div.doc-contents:not(.first) {
border-left: 4px solid rgba(230, 230, 230);
margin-bottom: 60px;
}

/* Avoid breaking parameters name, etc. in table cells. */
td code {
word-break: normal !important;
}

/* For pieces of Markdown rendered in table cells. */
td p {
margin-top: 0 !important;
margin-bottom: 0 !important;
}

/* Avoid breaking code headings. */
.doc-heading code {
white-space: normal;
}

/* Improve rendering of parameters, returns and exceptions. */
.field-name {
min-width: 100px;
}
.field-name, .field-body {
border: none !important;
padding: 0 !important;
}
.field-list {
margin: 0 !important;
}
```
35 changes: 27 additions & 8 deletions src/mkdocstrings/handlers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import textwrap
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Any, Dict, List, Optional, Sequence
from typing import Any, Dict, Iterable, List, Optional, Sequence
from xml.etree.ElementTree import Element, tostring

from jinja2 import Environment, FileSystemLoader
Expand Down Expand Up @@ -139,10 +139,12 @@ class BaseRenderer(ABC):
You can also override the `update_env` method, to add more filters to the Jinja environment,
making them available in your Jinja templates.

To define a fallback theme, add a `FALLBACK_THEME` class-variable.
To define a fallback theme, add a `fallback_theme` class-variable.
To add custom CSS, add an `extra_css` variable or create an 'style.css' file beside the templates.
"""

fallback_theme: str = ""
extra_css = ""

def __init__(self, directory: str, theme: str, custom_templates: Optional[str] = None) -> None:
"""
Expand All @@ -158,16 +160,22 @@ def __init__(self, directory: str, theme: str, custom_templates: Optional[str] =
"""
paths = []

if custom_templates is not None:
paths.append(Path(custom_templates) / directory / theme)

themes_dir = TEMPLATES_DIR / directory

paths.append(themes_dir / theme)

if self.fallback_theme != "":
if self.fallback_theme:
paths.append(themes_dir / self.fallback_theme)

for path in paths:
css_path = path / "style.css"
if css_path.is_file():
self.extra_css += "\n" + css_path.read_text(encoding="utf-8")
break

if custom_templates is not None:
paths.insert(0, Path(custom_templates) / directory / theme)

self.env = Environment(
autoescape=True,
loader=FileSystemLoader(paths),
Expand Down Expand Up @@ -493,9 +501,20 @@ def get_handler(self, name: str, handler_config: Optional[dict] = None) -> BaseH
) # type: ignore
return self._handlers[name]

def teardown(self):
@property
def seen_handlers(self) -> Iterable[BaseHandler]:
"""
Get the handlers that were encountered so far throughout the build.

Returns:
An iterable of instances of [`BaseHandler`][mkdocstrings.handlers.base.BaseHandler]
(usable only to loop through it).
"""
return self._handlers.values()

def teardown(self) -> None:
"""Teardown all cached handlers and clear the cache."""
for handler in self._handlers.values():
for handler in self.seen_handlers:
handler.collector.teardown()
self._handlers.clear()

Expand Down
12 changes: 11 additions & 1 deletion src/mkdocstrings/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from mkdocs.plugins import BasePlugin
from mkdocs.structure.pages import Page
from mkdocs.structure.toc import AnchorLink
from mkdocs.utils import write_file

from mkdocstrings.extension import MkdocstringsExtension
from mkdocstrings.handlers.base import BaseHandler, Handlers
Expand Down Expand Up @@ -99,6 +100,8 @@ class MkdocstringsPlugin(BasePlugin):
```
"""

css_filename = "assets/_mkdocstrings.css"

def __init__(self) -> None:
"""Initialize the object."""
super().__init__()
Expand Down Expand Up @@ -181,6 +184,9 @@ def on_config(self, config: Config, **kwargs) -> Config: # noqa: W0613 (unused
self._handlers = Handlers(extension_config)
mkdocstrings_extension = MkdocstringsExtension(extension_config, self._handlers)
config["markdown_extensions"].append(mkdocstrings_extension)

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

return config

def on_page_content(self, html: str, page: Page, **kwargs) -> str: # noqa: W0613 (unused arguments)
Expand Down Expand Up @@ -253,7 +259,7 @@ def on_post_page(self, output: str, page: Page, **kwargs) -> str: # noqa: W0613

return fixed_output

def on_post_build(self, **kwargs) -> None: # noqa: W0613,R0201 (unused arguments, cannot be static)
def on_post_build(self, config: Config, **kwargs) -> None: # noqa: W0613,R0201 (unused arguments, cannot be static)
"""
Teardown the handlers.

Expand All @@ -267,9 +273,13 @@ def on_post_build(self, **kwargs) -> None: # noqa: W0613,R0201 (unused argument
this hook.

Arguments:
config: The MkDocs config object.
kwargs: Additional arguments passed by MkDocs.
"""
if self._handlers:
css_content = "\n".join(handler.renderer.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))
pawamoy marked this conversation as resolved.
Show resolved Hide resolved

log.debug("Tearing handlers down")
self._handlers.teardown()

Expand Down
15 changes: 15 additions & 0 deletions src/mkdocstrings/templates/python/material/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* Don't capitalize names. */
h5.doc-heading {
text-transform: none !important;
}

/* Avoid breaking parameters name, etc. in table cells. */
.doc-contents td code {
word-break: normal !important;
}

/* For pieces of Markdown rendered in table cells. */
.doc-contents td p {
margin-top: 0 !important;
margin-bottom: 0 !important;
}
27 changes: 27 additions & 0 deletions src/mkdocstrings/templates/python/readthedocs/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* Avoid breaking parameters name, etc. in table cells. */
.doc-contents td code {
word-break: normal !important;
}

/* For pieces of Markdown rendered in table cells. */
.doc-contents td p {
margin-top: 0 !important;
margin-bottom: 0 !important;
}

/* Avoid breaking code headings. */
.doc-heading code {
white-space: normal;
}

/* Improve rendering of parameters, returns and exceptions. */
.doc-contents .field-name {
min-width: 100px;
}
.doc-contents .field-name, .field-body {
border: none !important;
padding: 0 !important;
}
.doc-contents .field-list {
margin: 0 !important;
}