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

1"""MkDocs plugin that generates a manpage at the end of the build.""" 

2 

3from __future__ import annotations 

4 

5import os 

6import subprocess 

7import tempfile 

8from shutil import which 

9from typing import TYPE_CHECKING 

10 

11from mkdocs.plugins import BasePlugin 

12 

13from mkdocs_manpage.config import PluginConfig 

14from mkdocs_manpage.logger import get_logger 

15 

16if TYPE_CHECKING: 

17 from typing import Any 

18 

19 from mkdocs.config.defaults import MkDocsConfig 

20 from mkdocs.structure.pages import Page 

21 

22 

23logger = get_logger(__name__) 

24 

25 

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:]}") 

34 

35 

36class MkdocsManpagePlugin(BasePlugin[PluginConfig]): 

37 """The MkDocs plugin to generate manpages. 

38 

39 This plugin defines the following event hooks: 

40 

41 - `on_page_content` 

42 - `on_post_build` 

43 

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 """ 

47 

48 def __init__(self) -> None: # noqa: D107 

49 self.pages: dict[str, str] = {} 

50 

51 def on_page_content(self, html: str, *, page: Page, **kwargs: Any) -> str | None: # noqa: ARG002 

52 """Record pages contents. 

53 

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. 

56 

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 

67 

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. 

70 

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. 

73 

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}")