Skip to content

mkdocs_manpage ¤

MkDocs Manpage package.

MkDocs plugin to generate a manpage from the documentation site.

Classes:

Functions:

  • get_logger –

    Return a logger for plugins.

  • preprocess –

    Pre-process HTML with user-defined functions.

MkdocsManpagePlugin ¤

MkdocsManpagePlugin()

Bases: BasePlugin[PluginConfig]

The MkDocs plugin to generate manpages.

This plugin defines the following event hooks:

  • on_page_content
  • on_post_build

Check the Developing Plugins page of mkdocs for more information about its plugin system.

Methods:

  • on_config –

    Save the global MkDocs configuration.

  • on_files –

    Expand inputs for manual pages.

  • on_page_content –

    Record pages contents.

  • on_post_build –

    Combine all recorded pages contents and convert it to a manual page with Pandoc.

Source code in src/mkdocs_manpage/_internal/plugin.py
83
84
def __init__(self) -> None:
    self._html_pages: dict[str, dict[str, str]] = defaultdict(dict)

on_config ¤

on_config(config: MkDocsConfig) -> MkDocsConfig | None

Save the global MkDocs configuration.

Hook for the on_config event. In this hook, we save the global MkDocs configuration into an instance variable, to re-use it later.

Parameters:

  • config ¤

    (MkDocsConfig) –

    The MkDocs config object.

Returns:

  • MkDocsConfig | None –

    The same, untouched config.

Source code in src/mkdocs_manpage/_internal/plugin.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None:
    """Save the global MkDocs configuration.

    Hook for the [`on_config` event](https://www.mkdocs.org/user-guide/plugins/#on_config).
    In this hook, we save the global MkDocs configuration into an instance variable,
    to re-use it later.

    Arguments:
        config: The MkDocs config object.

    Returns:
        The same, untouched config.
    """
    self._mkdocs_config = config
    return config

on_files ¤

on_files(
    files: Files, *, config: MkDocsConfig
) -> Files | None

Expand inputs for manual pages.

Hook for the on_files event. In this hook we expand inputs for each manual pages (glob patterns using *).

Parameters:

  • files ¤

    (Files) –

    The collection of MkDocs files.

  • config ¤

    (MkDocsConfig) –

    The MkDocs configuration.

Returns:

  • Files | None –

    Modified collection or none.

Source code in src/mkdocs_manpage/_internal/plugin.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def on_files(self, files: Files, *, config: MkDocsConfig) -> Files | None:  # noqa: ARG002
    """Expand inputs for manual pages.

    Hook for the [`on_files` event](https://www.mkdocs.org/user-guide/plugins/#on_files).
    In this hook we expand inputs for each manual pages (glob patterns using `*`).

    Parameters:
        files: The collection of MkDocs files.
        config: The MkDocs configuration.

    Returns:
        Modified collection or none.
    """
    for manpage in self.config.pages:
        manpage["inputs"] = self._expand_inputs(manpage["inputs"], page_uris=list(files.src_uris.keys()))
    return files

on_page_content ¤

on_page_content(
    html: str, *, page: Page, **kwargs: Any
) -> str | None

Record pages contents.

Hook for the on_page_content event. In this hook we simply record the HTML of the pages into a dictionary whose keys are the pages' URIs.

Parameters:

  • html ¤

    (str) –

    The page HTML.

  • page ¤

    (Page) –

    The page object.

Source code in src/mkdocs_manpage/_internal/plugin.py
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
def on_page_content(self, html: str, *, page: Page, **kwargs: Any) -> str | None:  # noqa: ARG002
    """Record pages contents.

    Hook for the [`on_page_content` event](https://www.mkdocs.org/user-guide/plugins/#on_page_content).
    In this hook we simply record the HTML of the pages into a dictionary whose keys are the pages' URIs.

    Parameters:
        html: The page HTML.
        page: The page object.
    """
    if not self.config.enabled:
        return None
    for manpage in self.config.pages:
        if page.file.src_uri in manpage["inputs"]:
            _logger.debug(f"Adding page {page.file.src_uri} to manpage {manpage['output']}")
            self._html_pages[manpage["output"]][page.file.src_uri] = html
    return html

on_post_build ¤

on_post_build(config: MkDocsConfig, **kwargs: Any) -> None

Combine all recorded pages contents and convert it to a manual page with Pandoc.

Hook for the on_post_build event. In this hook we concatenate all previously recorded HTML, and convert it to a manual page with Pandoc.

Parameters:

  • config ¤

    (MkDocsConfig) –

    MkDocs configuration.

Source code in src/mkdocs_manpage/_internal/plugin.py
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
def on_post_build(self, config: MkDocsConfig, **kwargs: Any) -> None:  # noqa: ARG002
    """Combine all recorded pages contents and convert it to a manual page with Pandoc.

    Hook for the [`on_post_build` event](https://www.mkdocs.org/user-guide/plugins/#on_post_build).
    In this hook we concatenate all previously recorded HTML, and convert it to a manual page with Pandoc.

    Parameters:
        config: MkDocs configuration.
    """
    if not self.config.enabled:
        return
    pandoc = which("pandoc")
    if pandoc is None:
        _logger.debug("Could not find pandoc executable, trying to call 'pandoc' directly")
        pandoc = "pandoc"

    for page in self.config.pages:
        try:
            html = "\n\n".join(self._html_pages[page["output"]][input_page] for input_page in page["inputs"])
        except KeyError as error:
            raise PluginError(str(error)) from error

        if self.config.get("preprocess"):
            html = preprocess(html, self.config["preprocess"], page["output"])

        output_file = Path(config.config_file_path).parent.joinpath(page["output"])
        output_file.parent.mkdir(parents=True, exist_ok=True)
        section = output_file.suffix[1:]
        section_header = page.get("header") or _section_headers.get(section, _section_headers["1"])
        title = page.get("title") or self._mkdocs_config.site_name

        with tempfile.NamedTemporaryFile(
            "w",
            prefix="mkdocs_manpage_",
            suffix=".1.html",
            encoding="utf8",
        ) as temp_file:
            temp_file.write(html)
            temp_file.flush()
            pandoc_variables = [
                f"title:{title}",
                f"section:{section}",
                f"date:{date.today().strftime('%Y-%m-%d')}",  # noqa: DTZ011
                f"footer:mkdocs-manpage v{metadata.version('mkdocs-manpage')}",
                f"header:{section_header}",
            ]
            pandoc_options = [
                "--verbose",
                "--standalone",
                "--wrap=none",
            ]
            pandoc_command = [
                pandoc,
                *pandoc_options,
                *[f"-V{var}" for var in pandoc_variables],
                "--to",
                "man",
                temp_file.name,
                "-o",
                str(output_file),
            ]
            pandoc_process = subprocess.run(  # noqa: S603
                pandoc_command,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                encoding="utf-8",
                check=False,
            )
        _log_pandoc_output(pandoc_process.stdout)
        _logger.info(f"Generated manpage {output_file}")

PageConfig ¤

Bases: Config

Sub-config for each manual page.

Attributes:

  • header –

    Header to show in the manual page; defaults to the standard section header.

  • inputs –

    Source page URIs to include, with optional * patterns.

  • output –

    Path of the generated manual page, relative to the MkDocs config file.

  • title –

    Title to show in the manual page; defaults to the site name.

header class-attribute instance-attribute ¤

header = mkconf.Optional(mkconf.Type(str))

Header to show in the manual page; defaults to the standard section header.

inputs class-attribute instance-attribute ¤

inputs = mkconf.ListOfItems(mkconf.Type(str))

Source page URIs to include, with optional * patterns.

output class-attribute instance-attribute ¤

output = mkconf.File(exists=False)

Path of the generated manual page, relative to the MkDocs config file.

title class-attribute instance-attribute ¤

title = mkconf.Optional(mkconf.Type(str))

Title to show in the manual page; defaults to the site name.

PluginConfig ¤

Bases: Config

Configuration options for the plugin.

Attributes:

  • enabled –

    Whether to generate manual pages.

  • pages –

    Configuration for the manual pages to generate.

  • preprocess –

    Path to a Python file that preprocesses each manual page's HTML.

enabled class-attribute instance-attribute ¤

enabled = mkconf.Type(bool, default=True)

Whether to generate manual pages.

pages class-attribute instance-attribute ¤

pages = mkconf.ListOfItems(mkconf.SubConfig(PageConfig))

Configuration for the manual pages to generate.

preprocess class-attribute instance-attribute ¤

preprocess = mkconf.Optional(mkconf.File(exists=True))

Path to a Python file that preprocesses each manual page's HTML.

PluginLogger ¤

PluginLogger(prefix: str, logger: Logger)

Bases: LoggerAdapter

A logger adapter to prefix messages with the originating package name.

Parameters:

  • prefix ¤

    (str) –

    The string to insert in front of every message.

  • logger ¤

    (Logger) –

    The logger instance.

Methods:

Source code in src/mkdocs_manpage/_internal/logger.py
31
32
33
34
35
36
37
38
39
def __init__(self, prefix: str, logger: logging.Logger):
    """Initialize the object.

    Arguments:
        prefix: The string to insert in front of every message.
        logger: The logger instance.
    """
    super().__init__(logger, {})
    self._prefix = prefix

process ¤

process(
    msg: str, kwargs: MutableMapping[str, Any]
) -> tuple[str, Any]

Process the message.

Parameters:

Returns:

Source code in src/mkdocs_manpage/_internal/logger.py
41
42
43
44
45
46
47
48
49
50
51
def process(self, msg: str, kwargs: MutableMapping[str, Any]) -> tuple[str, Any]:
    """Process the message.

    Arguments:
        msg: The message:
        kwargs: Remaining arguments.

    Returns:
        The processed message.
    """
    return f"{self._prefix}: {msg}", kwargs

get_logger ¤

get_logger(name: str) -> PluginLogger

Return a logger for plugins.

Parameters:

  • name ¤

    (str) –

    The name to use with logging.getLogger.

Returns:

  • PluginLogger –

    A logger configured to work well in MkDocs, prefixing each message with the plugin package name.

Source code in src/mkdocs_manpage/_internal/logger.py
54
55
56
57
58
59
60
61
62
63
64
65
def get_logger(name: str) -> PluginLogger:
    """Return a logger for plugins.

    Arguments:
        name: The name to use with `logging.getLogger`.

    Returns:
        A logger configured to work well in MkDocs,
            prefixing each message with the plugin package name.
    """
    logger = logging.getLogger(f"mkdocs.plugins.{name}")
    return PluginLogger(name.split(".", 1)[0], logger)

preprocess ¤

preprocess(html: str, module_path: str, output: str) -> str

Pre-process HTML with user-defined functions.

Parameters:

  • html ¤

    (str) –

    The HTML to process before conversion to a manpage.

  • module_path ¤

    (str) –

    The path of a Python module containing a preprocess function. The function must accept one and only one argument called soup. The soup argument is an instance of bs4.BeautifulSoup.

  • output ¤

    (str) –

    The output path of the relevant manual page.

Returns:

  • str –

    The processed HTML.

Source code in src/mkdocs_manpage/_internal/preprocess.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
def preprocess(html: str, module_path: str, output: str) -> str:
    """Pre-process HTML with user-defined functions.

    Parameters:
        html: The HTML to process before conversion to a manpage.
        module_path: The path of a Python module containing a `preprocess` function.
            The function must accept one and only one argument called `soup`.
            The `soup` argument is an instance of [`bs4.BeautifulSoup`][].
        output: The output path of the relevant manual page.

    Returns:
        The processed HTML.
    """
    try:
        from bs4 import BeautifulSoup  # noqa: PLC0415
    except ImportError as error:
        raise PluginError(
            "mkdocs-manpage must be installed with the `preprocess` extra to use HTML pre-processing: "
            "`pip install mkdocs-manpage[preprocess]",
        ) from error
    try:
        module = _load_module(module_path)
    except Exception as error:
        raise PluginError(f"Could not load module: {error}") from error
    soup = BeautifulSoup(html, "lxml")
    try:
        module.preprocess(soup, output)
    except Exception as error:
        raise PluginError(f"Could not pre-process HTML: {error}") from error
    return str(soup)