%PDF- %PDF-
Direktori : /lib/python3/dist-packages/certbot/_internal/cli/ |
Current File : //lib/python3/dist-packages/certbot/_internal/cli/cli_utils.py |
"""Certbot command line util function""" import argparse import copy import inspect from acme import challenges from certbot import configuration from certbot import errors from certbot import util from certbot._internal import constants from certbot.compat import os class _Default: """A class to use as a default to detect if a value is set by a user""" def __bool__(self): return False def __eq__(self, other): return isinstance(other, _Default) def __hash__(self): return id(_Default) def __nonzero__(self): return self.__bool__() def read_file(filename, mode="rb"): """Returns the given file's contents. :param str filename: path to file :param str mode: open mode (see `open`) :returns: absolute path of filename and its contents :rtype: tuple :raises argparse.ArgumentTypeError: File does not exist or is not readable. """ try: filename = os.path.abspath(filename) with open(filename, mode) as the_file: contents = the_file.read() return filename, contents except IOError as exc: raise argparse.ArgumentTypeError(exc.strerror) def flag_default(name): """Default value for CLI flag.""" # XXX: this is an internal housekeeping notion of defaults before # argparse has been set up; it is not accurate for all flags. Call it # with caution. Plugin defaults are missing, and some things are using # defaults defined in this file, not in constants.py :( return copy.deepcopy(constants.CLI_DEFAULTS[name]) def config_help(name, hidden=False): """Extract the help message for a `configuration.NamespaceConfig` property docstring.""" if hidden: return argparse.SUPPRESS return inspect.getdoc(getattr(configuration.NamespaceConfig, name)) class HelpfulArgumentGroup: """Emulates an argparse group for use with HelpfulArgumentParser. This class is used in the add_group method of HelpfulArgumentParser. Command line arguments can be added to the group, but help suppression and default detection is applied by HelpfulArgumentParser when necessary. """ def __init__(self, helpful_arg_parser, topic): self._parser = helpful_arg_parser self._topic = topic def add_argument(self, *args, **kwargs): """Add a new command line argument to the argument group.""" self._parser.add(self._topic, *args, **kwargs) class CustomHelpFormatter(argparse.HelpFormatter): """This is a clone of ArgumentDefaultsHelpFormatter, with bugfixes. In particular we fix https://bugs.python.org/issue28742 """ def _get_help_string(self, action): helpstr = action.help if '%(default)' not in action.help and '(default:' not in action.help: if action.default != argparse.SUPPRESS: defaulting_nargs = [argparse.OPTIONAL, argparse.ZERO_OR_MORE] if action.option_strings or action.nargs in defaulting_nargs: helpstr += ' (default: %(default)s)' return helpstr class _DomainsAction(argparse.Action): """Action class for parsing domains.""" def __call__(self, parser, namespace, domain, option_string=None): """Just wrap add_domains in argparseese.""" add_domains(namespace, domain) def add_domains(args_or_config, domains): """Registers new domains to be used during the current client run. Domains are not added to the list of requested domains if they have already been registered. :param args_or_config: parsed command line arguments :type args_or_config: argparse.Namespace or configuration.NamespaceConfig :param str domain: one or more comma separated domains :returns: domains after they have been normalized and validated :rtype: `list` of `str` """ validated_domains = [] for domain in domains.split(","): domain = util.enforce_domain_sanity(domain.strip()) validated_domains.append(domain) if domain not in args_or_config.domains: args_or_config.domains.append(domain) return validated_domains class CaseInsensitiveList(list): """A list that will ignore case when searching. This class is passed to the `choices` argument of `argparse.add_arguments` through the `helpful` wrapper. It is necessary due to special handling of command line arguments by `set_by_cli` in which the `type_func` is not applied.""" def __contains__(self, element): return super().__contains__(element.lower()) def _user_agent_comment_type(value): if "(" in value or ")" in value: raise argparse.ArgumentTypeError("may not contain parentheses") return value class _EncodeReasonAction(argparse.Action): """Action class for parsing revocation reason.""" def __call__(self, parser, namespace, reason, option_string=None): """Encodes the reason for certificate revocation.""" code = constants.REVOCATION_REASONS[reason.lower()] setattr(namespace, self.dest, code) def parse_preferred_challenges(pref_challs): """Translate and validate preferred challenges. :param pref_challs: list of preferred challenge types :type pref_challs: `list` of `str` :returns: validated list of preferred challenge types :rtype: `list` of `str` :raises errors.Error: if pref_challs is invalid """ aliases = {"dns": "dns-01", "http": "http-01"} challs = [c.strip() for c in pref_challs] challs = [aliases.get(c, c) for c in challs] unrecognized = ", ".join(name for name in challs if name not in challenges.Challenge.TYPES) if unrecognized: raise errors.Error( "Unrecognized challenges: {0}".format(unrecognized)) return challs class _PrefChallAction(argparse.Action): """Action class for parsing preferred challenges.""" def __call__(self, parser, namespace, pref_challs, option_string=None): try: challs = parse_preferred_challenges(pref_challs.split(",")) except errors.Error as error: raise argparse.ArgumentError(self, str(error)) namespace.pref_challs.extend(challs) class _DeployHookAction(argparse.Action): """Action class for parsing deploy hooks.""" def __call__(self, parser, namespace, values, option_string=None): renew_hook_set = namespace.deploy_hook != namespace.renew_hook if renew_hook_set and namespace.renew_hook != values: raise argparse.ArgumentError( self, "conflicts with --renew-hook value") namespace.deploy_hook = namespace.renew_hook = values class _RenewHookAction(argparse.Action): """Action class for parsing renew hooks.""" def __call__(self, parser, namespace, values, option_string=None): deploy_hook_set = namespace.deploy_hook is not None if deploy_hook_set and namespace.deploy_hook != values: raise argparse.ArgumentError( self, "conflicts with --deploy-hook value") namespace.renew_hook = values def nonnegative_int(value): """Converts value to an int and checks that it is not negative. This function should used as the type parameter for argparse arguments. :param str value: value provided on the command line :returns: integer representation of value :rtype: int :raises argparse.ArgumentTypeError: if value isn't a non-negative integer """ try: int_value = int(value) except ValueError: raise argparse.ArgumentTypeError("value must be an integer") if int_value < 0: raise argparse.ArgumentTypeError("value must be non-negative") return int_value