#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Utility class to format help text.
:module: hformatter
:author: Le Bars, Yoann
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version. See file `LICENSE` or go to:
https://www.gnu.org/licenses/gpl-3.0.html
"""
# Defines the public names of this module.
__all__ = ['InvalidCommandLine', 'create_parser']
from typing import Final, Iterable
import argparse
from sys import stderr
from functools import partial
# Title for usage section.
USAGE_STRING: str = 'Usage: '
# Title for default value description.
DEFAULT_STRING: str = 'default'
[docs]
class InvalidCommandLine(Exception):
"""
Exception raised when the command line is invalid.
:param str message: Error message.
"""
message: str
def __init__(self, message: str, *args: object) -> None:
"""
Class constructor.
:param str message: Error message.
:param object args: Optional arguments list.
"""
self.message: Final[str] = message
super().__init__(*args)
class HelpFormatter(argparse.ArgumentDefaultsHelpFormatter):
"""
Class to format help output.
:param str __usage_string: Title for usage section.
:param str __default_string: Title for default value description.
"""
__usage_string: str
__default_string: str
def __init__(self, prog: str, indent_increment: int = 2, max_help_position: int = 24, # pylint: disable=too-many-arguments, too-many-positional-arguments
width: int | None = None, usage_str: str | None = None,
default_str: str | None = None) -> None:
"""
Class constructor.
:param str prog: Program name.
:param int indent_increment: Number of blank space when incrementing the text.
:param int max_help_position: Maximum text indent.
:param int width: Line width in help message.
:param str usage_str: Title for usage section.
:param str default_str: Title for default value description.
"""
self.__usage_string = usage_str if usage_str is not None else USAGE_STRING
self.__default_string = default_str if default_str is not None else DEFAULT_STRING
super().__init__(prog, indent_increment, max_help_position, width)
def _get_help_string(self, action: argparse.Action) -> str | None:
"""
Create the help message for a given action.
:param argparse.Action action: Action descriptor.
"""
help_string: str | None = action.help
if '%(default)' not in action.help:
if action.default is not argparse.SUPPRESS:
defaulting_nargs = [argparse.OPTIONAL, argparse.ZERO_OR_MORE]
if action.option_strings or action.nargs in defaulting_nargs:
help_string += f' ({self.__default_string}: %(default)s)'
return help_string
def add_usage(self, usage: str, actions: Iterable[argparse.Action],
groups: Iterable[argparse._ArgumentGroup],
prefix: str | None = None) -> None:
"""
Reformat usage message.
:param str usage: Program command line description.
:param Iterable[argparse.Action] actions: Action identifier.
:param Iterable[argparse._ArgumentGroup] groups: Groups identifier.
:param str prefix: Prefix to usage explanation.
:returns: Object describing usage string.
:rtype: None
"""
if prefix is None:
prefix = self.__usage_string
return super().add_usage(usage, actions, groups, prefix)
class PersonalizedArgumentParser(argparse.ArgumentParser):
"""
Argument parser with translated error messages.
"""
def error(self, message: str) -> None:
"""
Prints a usage message incorporating the message to stderr and raises an exception.
:param str message: Error message.
:raises InvalidCommandLine: If the command line is invalid.
"""
self.print_usage(stderr)
raise InvalidCommandLine(message)
[docs]
def create_parser(program_description: str, positional_name: str, optional_name: str, # pylint: disable=too-many-arguments, too-many-positional-arguments
program_version: str, version_message: str,
help_message: str, usage_message: str = USAGE_STRING,
default_message: str = DEFAULT_STRING) -> PersonalizedArgumentParser:
"""
Generates an argument parser.
:param str program_description: String describing the aims of the program.
:param str positional_name: String for positional arguments title.
:param str optional_name: String for optional arguments title.
:param str program_version: String describing program version.
:param str version_message: String describing the version program option.
:param str help_message: String describing the help program option.
:param str usage_message: Title for usage section. Defaults to “Usage: ”.
:param str default_message: Title for default value description. Defaults to “default”.
:returns: An argument parser.
:rtype: PersonalizedArgumentParser
"""
# Command line parser.
formatter = partial(HelpFormatter, usage_str=usage_message, default_str=default_message)
parser = PersonalizedArgumentParser(add_help=False,
formatter_class=formatter,
description=program_description)
parser._positionals.title = positional_name # pylint: disable=protected-access
parser._optionals.title = optional_name # pylint: disable=protected-access
parser.add_argument('-V', '--version', action='version', version='%(prog)s ' + program_version,
help=version_message)
parser.add_argument('-h', '--help', action='help', default=argparse.SUPPRESS,
help=help_message)
return parser