| from __future__ import annotations
|
|
|
| import collections.abc as cabc
|
| import typing as t
|
| from gettext import gettext as _
|
| from gettext import ngettext
|
|
|
| from ._compat import get_text_stderr
|
| from .globals import resolve_color_default
|
| from .utils import echo
|
| from .utils import format_filename
|
|
|
| if t.TYPE_CHECKING:
|
| from .core import Command
|
| from .core import Context
|
| from .core import Parameter
|
|
|
|
|
| def _join_param_hints(param_hint: cabc.Sequence[str] | str | None) -> str | None:
|
| if param_hint is not None and not isinstance(param_hint, str):
|
| return " / ".join(repr(x) for x in param_hint)
|
|
|
| return param_hint
|
|
|
|
|
| class ClickException(Exception):
|
| """An exception that Click can handle and show to the user."""
|
|
|
|
|
| exit_code = 1
|
|
|
| def __init__(self, message: str) -> None:
|
| super().__init__(message)
|
|
|
|
|
| self.show_color: bool | None = resolve_color_default()
|
| self.message = message
|
|
|
| def format_message(self) -> str:
|
| return self.message
|
|
|
| def __str__(self) -> str:
|
| return self.message
|
|
|
| def show(self, file: t.IO[t.Any] | None = None) -> None:
|
| if file is None:
|
| file = get_text_stderr()
|
|
|
| echo(
|
| _("Error: {message}").format(message=self.format_message()),
|
| file=file,
|
| color=self.show_color,
|
| )
|
|
|
|
|
| class UsageError(ClickException):
|
| """An internal exception that signals a usage error. This typically
|
| aborts any further handling.
|
|
|
| :param message: the error message to display.
|
| :param ctx: optionally the context that caused this error. Click will
|
| fill in the context automatically in some situations.
|
| """
|
|
|
| exit_code = 2
|
|
|
| def __init__(self, message: str, ctx: Context | None = None) -> None:
|
| super().__init__(message)
|
| self.ctx = ctx
|
| self.cmd: Command | None = self.ctx.command if self.ctx else None
|
|
|
| def show(self, file: t.IO[t.Any] | None = None) -> None:
|
| if file is None:
|
| file = get_text_stderr()
|
| color = None
|
| hint = ""
|
| if (
|
| self.ctx is not None
|
| and self.ctx.command.get_help_option(self.ctx) is not None
|
| ):
|
| hint = _("Try '{command} {option}' for help.").format(
|
| command=self.ctx.command_path, option=self.ctx.help_option_names[0]
|
| )
|
| hint = f"{hint}\n"
|
| if self.ctx is not None:
|
| color = self.ctx.color
|
| echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color)
|
| echo(
|
| _("Error: {message}").format(message=self.format_message()),
|
| file=file,
|
| color=color,
|
| )
|
|
|
|
|
| class BadParameter(UsageError):
|
| """An exception that formats out a standardized error message for a
|
| bad parameter. This is useful when thrown from a callback or type as
|
| Click will attach contextual information to it (for instance, which
|
| parameter it is).
|
|
|
| .. versionadded:: 2.0
|
|
|
| :param param: the parameter object that caused this error. This can
|
| be left out, and Click will attach this info itself
|
| if possible.
|
| :param param_hint: a string that shows up as parameter name. This
|
| can be used as alternative to `param` in cases
|
| where custom validation should happen. If it is
|
| a string it's used as such, if it's a list then
|
| each item is quoted and separated.
|
| """
|
|
|
| def __init__(
|
| self,
|
| message: str,
|
| ctx: Context | None = None,
|
| param: Parameter | None = None,
|
| param_hint: cabc.Sequence[str] | str | None = None,
|
| ) -> None:
|
| super().__init__(message, ctx)
|
| self.param = param
|
| self.param_hint = param_hint
|
|
|
| def format_message(self) -> str:
|
| if self.param_hint is not None:
|
| param_hint = self.param_hint
|
| elif self.param is not None:
|
| param_hint = self.param.get_error_hint(self.ctx)
|
| else:
|
| return _("Invalid value: {message}").format(message=self.message)
|
|
|
| return _("Invalid value for {param_hint}: {message}").format(
|
| param_hint=_join_param_hints(param_hint), message=self.message
|
| )
|
|
|
|
|
| class MissingParameter(BadParameter):
|
| """Raised if click required an option or argument but it was not
|
| provided when invoking the script.
|
|
|
| .. versionadded:: 4.0
|
|
|
| :param param_type: a string that indicates the type of the parameter.
|
| The default is to inherit the parameter type from
|
| the given `param`. Valid values are ``'parameter'``,
|
| ``'option'`` or ``'argument'``.
|
| """
|
|
|
| def __init__(
|
| self,
|
| message: str | None = None,
|
| ctx: Context | None = None,
|
| param: Parameter | None = None,
|
| param_hint: cabc.Sequence[str] | str | None = None,
|
| param_type: str | None = None,
|
| ) -> None:
|
| super().__init__(message or "", ctx, param, param_hint)
|
| self.param_type = param_type
|
|
|
| def format_message(self) -> str:
|
| if self.param_hint is not None:
|
| param_hint: cabc.Sequence[str] | str | None = self.param_hint
|
| elif self.param is not None:
|
| param_hint = self.param.get_error_hint(self.ctx)
|
| else:
|
| param_hint = None
|
|
|
| param_hint = _join_param_hints(param_hint)
|
| param_hint = f" {param_hint}" if param_hint else ""
|
|
|
| param_type = self.param_type
|
| if param_type is None and self.param is not None:
|
| param_type = self.param.param_type_name
|
|
|
| msg = self.message
|
| if self.param is not None:
|
| msg_extra = self.param.type.get_missing_message(
|
| param=self.param, ctx=self.ctx
|
| )
|
| if msg_extra:
|
| if msg:
|
| msg += f". {msg_extra}"
|
| else:
|
| msg = msg_extra
|
|
|
| msg = f" {msg}" if msg else ""
|
|
|
|
|
| if param_type == "argument":
|
| missing = _("Missing argument")
|
| elif param_type == "option":
|
| missing = _("Missing option")
|
| elif param_type == "parameter":
|
| missing = _("Missing parameter")
|
| else:
|
| missing = _("Missing {param_type}").format(param_type=param_type)
|
|
|
| return f"{missing}{param_hint}.{msg}"
|
|
|
| def __str__(self) -> str:
|
| if not self.message:
|
| param_name = self.param.name if self.param else None
|
| return _("Missing parameter: {param_name}").format(param_name=param_name)
|
| else:
|
| return self.message
|
|
|
|
|
| class NoSuchOption(UsageError):
|
| """Raised if click attempted to handle an option that does not
|
| exist.
|
|
|
| .. versionadded:: 4.0
|
| """
|
|
|
| def __init__(
|
| self,
|
| option_name: str,
|
| message: str | None = None,
|
| possibilities: cabc.Sequence[str] | None = None,
|
| ctx: Context | None = None,
|
| ) -> None:
|
| if message is None:
|
| message = _("No such option: {name}").format(name=option_name)
|
|
|
| super().__init__(message, ctx)
|
| self.option_name = option_name
|
| self.possibilities = possibilities
|
|
|
| def format_message(self) -> str:
|
| if not self.possibilities:
|
| return self.message
|
|
|
| possibility_str = ", ".join(sorted(self.possibilities))
|
| suggest = ngettext(
|
| "Did you mean {possibility}?",
|
| "(Possible options: {possibilities})",
|
| len(self.possibilities),
|
| ).format(possibility=possibility_str, possibilities=possibility_str)
|
| return f"{self.message} {suggest}"
|
|
|
|
|
| class BadOptionUsage(UsageError):
|
| """Raised if an option is generally supplied but the use of the option
|
| was incorrect. This is for instance raised if the number of arguments
|
| for an option is not correct.
|
|
|
| .. versionadded:: 4.0
|
|
|
| :param option_name: the name of the option being used incorrectly.
|
| """
|
|
|
| def __init__(
|
| self, option_name: str, message: str, ctx: Context | None = None
|
| ) -> None:
|
| super().__init__(message, ctx)
|
| self.option_name = option_name
|
|
|
|
|
| class BadArgumentUsage(UsageError):
|
| """Raised if an argument is generally supplied but the use of the argument
|
| was incorrect. This is for instance raised if the number of values
|
| for an argument is not correct.
|
|
|
| .. versionadded:: 6.0
|
| """
|
|
|
|
|
| class NoArgsIsHelpError(UsageError):
|
| def __init__(self, ctx: Context) -> None:
|
| self.ctx: Context
|
| super().__init__(ctx.get_help(), ctx=ctx)
|
|
|
| def show(self, file: t.IO[t.Any] | None = None) -> None:
|
| echo(self.format_message(), file=file, err=True, color=self.ctx.color)
|
|
|
|
|
| class FileError(ClickException):
|
| """Raised if a file cannot be opened."""
|
|
|
| def __init__(self, filename: str, hint: str | None = None) -> None:
|
| if hint is None:
|
| hint = _("unknown error")
|
|
|
| super().__init__(hint)
|
| self.ui_filename: str = format_filename(filename)
|
| self.filename = filename
|
|
|
| def format_message(self) -> str:
|
| return _("Could not open file {filename!r}: {message}").format(
|
| filename=self.ui_filename, message=self.message
|
| )
|
|
|
|
|
| class Abort(RuntimeError):
|
| """An internal signalling exception that signals Click to abort."""
|
|
|
|
|
| class Exit(RuntimeError):
|
| """An exception that indicates that the application should exit with some
|
| status code.
|
|
|
| :param code: the status code to exit with.
|
| """
|
|
|
| __slots__ = ("exit_code",)
|
|
|
| def __init__(self, code: int = 0) -> None:
|
| self.exit_code: int = code
|
|
|