Coverage for src/mkdocs_manpage/plugin.py: 84.06%
49 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-05 21:04 +0200
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-05 21:04 +0200
1"""MkDocs plugin that generates a manpage at the end of the build."""
3from __future__ import annotations
5import os
6import subprocess
7import tempfile
8from shutil import which
9from typing import TYPE_CHECKING
11from mkdocs.plugins import BasePlugin
13from mkdocs_manpage.config import PluginConfig
14from mkdocs_manpage.logger import get_logger
16if TYPE_CHECKING:
17 from typing import Any
19 from mkdocs.config.defaults import MkDocsConfig
20 from mkdocs.structure.pages import Page
23logger = get_logger(__name__)
26def _log_pandoc_output(output: str) -> None:
27 for line in output.split("\n"):
28 if line.startswith("[INFO]"):
29 logger.debug(f"pandoc: {line[7:]}")
30 elif line.startswith("[WARNING]"):
31 logger.warning(f"pandoc: {line[10:]}")
32 else:
33 logger.debug(f"pandoc: {line[8:]}")
36class MkdocsManpagePlugin(BasePlugin[PluginConfig]):
37 """The MkDocs plugin to generate manpages.
39 This plugin defines the following event hooks:
41 - `on_page_content`
42 - `on_post_build`
44 Check the [Developing Plugins](https://www.mkdocs.org/user-guide/plugins/#developing-plugins) page of `mkdocs`
45 for more information about its plugin system.
46 """
48 def __init__(self) -> None: # noqa: D107
49 self.pages: dict[str, str] = {}
51 def on_page_content(self, html: str, *, page: Page, **kwargs: Any) -> str | None: # noqa: ARG002
52 """Record pages contents.
54 Hook for the [`on_page_content` event](https://www.mkdocs.org/user-guide/plugins/#on_page_content).
55 In this hook we simply record the HTML of the pages into a dictionary whose keys are the pages' URIs.
57 Parameters:
58 html: The page HTML.
59 page: The page object.
60 """
61 if not self.config.enabled: 61 ↛ 62line 61 didn't jump to line 62, because the condition on line 61 was never true
62 return None
63 if page.file.src_uri in self.config.pages or not self.config.pages:
64 logger.debug(f"Adding page {page.file.src_uri} to manpage")
65 self.pages[page.file.src_uri] = html
66 return html
68 def on_post_build(self, config: MkDocsConfig, **kwargs: Any) -> None: # noqa: ARG002
69 """Combine all recorded pages contents and convert it to a manual page with Pandoc.
71 Hook for the [`on_post_build` event](https://www.mkdocs.org/user-guide/plugins/#on_post_build).
72 In this hook we concatenate all previously recorded HTML, and convert it to a manual page with Pandoc.
74 Parameters:
75 config: MkDocs configuration.
76 """
77 if not self.config.enabled: 77 ↛ 78line 77 didn't jump to line 78, because the condition on line 77 was never true
78 return
79 pandoc = which("pandoc")
80 if pandoc is None: 80 ↛ 81line 80 didn't jump to line 81, because the condition on line 80 was never true
81 logger.debug("Could not find pandoc executable, trying to call 'pandoc' directly")
82 pandoc = "pandoc"
83 pages = []
84 if self.config.pages: 84 ↛ 91line 84 didn't jump to line 91, because the condition on line 84 was never false
85 for page in self.config.pages:
86 try:
87 pages.append(self.pages[page])
88 except KeyError:
89 logger.error(f"No page with path {page}") # noqa: TRY400
90 else:
91 pages = list(self.pages.values())
92 combined = "\n\n".join(pages)
93 output_file = os.path.join(config.site_dir, "manpage.1")
94 with tempfile.NamedTemporaryFile("w", prefix="mkdocs_manpage_", suffix=".html") as temp_file:
95 temp_file.write(combined)
96 pandoc_process = subprocess.run(
97 [pandoc, "--verbose", "--standalone", "--to", "man", temp_file.name, "-o", output_file], # noqa: S603
98 stdout=subprocess.PIPE,
99 stderr=subprocess.STDOUT,
100 text=True,
101 )
102 _log_pandoc_output(pandoc_process.stdout)
103 logger.info(f"Generated manpage at {output_file}")