From 6fe7a081bd6b486afd24513ffd11cbfa9a705066 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Wed, 17 May 2023 17:01:05 +0200 Subject: [PATCH] Add a script to build man page from a package entry_points --- MANIFEST.in | 2 +- tools/build_man_page.py | 114 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 tools/build_man_page.py diff --git a/MANIFEST.in b/MANIFEST.in index 5ee9a8d02e..c7decdf8b6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -17,4 +17,4 @@ recursive-include qtdesigner_plugins *.py *.rst recursive-include src/silx/resources * recursive-include examples * recursive-include package * - +recursive-include tools * diff --git a/tools/build_man_page.py b/tools/build_man_page.py new file mode 100644 index 0000000000..8cf050b025 --- /dev/null +++ b/tools/build_man_page.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2016-2022 European Synchrotron Radiation Facility +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ###########################################################################*/ +"""Build man pages of the project's entry points + +The project's package MUST be installed in the current Python environment. +""" + +import logging +from pathlib import Path +import re +import subprocess +import sys +from typing import Iterator, Tuple + +import pkg_resources + + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +PROJECT = "silx" + + +def get_synopsis(module_name: str) -> str: + """Execute Python commands to retrieve the synopsis for help2man""" + commands = ( + "import logging", + "logging.basicConfig(level=logging.ERROR)", + f"import {module_name}", + f"print({module_name}.__doc__)", + ) + result = subprocess.run( + [sys.executable, "-c", "; ".join(commands)], + capture_output=True, + ) + if result.returncode: + logger.warning("Error while getting synopsis for module '%s'.", module_name) + return None + synopsis = result.stdout.decode("utf-8").strip() + if synopsis == "None": + return None + return synopsis + + +def entry_points(name: str) -> Iterator[Tuple[str, str, str]]: + for group in ("console_scripts", "gui_scripts"): + for entry_point in pkg_resources.iter_entry_points(group, name): + yield entry_point.name, entry_point.module_name, entry_point.attrs[0] + + +def main(name: str, out_path: Path): + out_path.mkdir(parents=True, exist_ok=True) + + eps = tuple(entry_points(name)) + if not eps: + raise RuntimeError("No entry points found!") + + for target_name, module_name, function_name in eps: + logger.info(f"Build man for entry-point target '{target_name}'") + python_command = [ + sys.executable, + "-c", + f'"import {module_name}; {module_name}.{function_name}()"', + ] + + help2man_command = [ + "help2man", + "-N", + " ".join(python_command), + "-o", + str(out_path / f"{target_name}.1"), + ] + + synopsis = get_synopsis(module_name) + if synopsis: + help2man_command += ["-n", synopsis] + + result = subprocess.run(help2man_command) + if result.returncode != 0: + logger.error(f"Error while generating man file for target '{target_name}'.") + for argument in ("--help", "--version"): + test_command = python_command + [argument] + logger.info(f"Running: {test_command}") + result = subprocess.run(test_command) + logger.info(f"\tReturn code: {result.returncode}") + raise RuntimeError(f"Fail to generate '{target_name}' man documentation") + + +if __name__ == "__main__": + main(PROJECT, out_path=Path(__file__).parent.parent / "build" / "man")