codekingpro/portable-devtools
115k
1from __future__ import annotations2 3import collections.abc as cabc4import typing as t5from gettext import gettext as _6from gettext import ngettext7 8from ._compat import get_text_stderr9from .globals import resolve_color_default10from .utils import echo11from .utils import format_filename12 13if t.TYPE_CHECKING:14 from .core import Command15 from .core import Context16 from .core import Parameter17 18 19def _join_param_hints(param_hint: cabc.Sequence[str] | str | None) -> str | None:20 if param_hint is not None and not isinstance(param_hint, str):21 return " / ".join(repr(x) for x in param_hint)22 23 return param_hint24 25 26class ClickException(Exception):27 """An exception that Click can handle and show to the user."""28 29 #: The exit code for this exception.30 exit_code = 131 32 def __init__(self, message: str) -> None:33 super().__init__(message)34 # The context will be removed by the time we print the message, so cache35 # the color settings here to be used later on (in `show`)36 self.show_color: bool | None = resolve_color_default()37 self.message = message38 39 def format_message(self) -> str:40 return self.message41 42 def __str__(self) -> str:43 return self.message44 45 def show(self, file: t.IO[t.Any] | None = None) -> None:46 if file is None:47 file = get_text_stderr()48 49 echo(50 _("Error: {message}").format(message=self.format_message()),51 file=file,52 color=self.show_color,53 )54 55 56class UsageError(ClickException):57 """An internal exception that signals a usage error. This typically58 aborts any further handling.59 60 :param message: the error message to display.61 :param ctx: optionally the context that caused this error. Click will62 fill in the context automatically in some situations.63 """64 65 exit_code = 266 67 def __init__(self, message: str, ctx: Context | None = None) -> None:68 super().__init__(message)69 self.ctx = ctx70 self.cmd: Command | None = self.ctx.command if self.ctx else None71 72 def show(self, file: t.IO[t.Any] | None = None) -> None:73 if file is None:74 file = get_text_stderr()75 color = None76 hint = ""77 if (78 self.ctx is not None79 and self.ctx.command.get_help_option(self.ctx) is not None80 ):81 hint = _("Try '{command} {option}' for help.").format(82 command=self.ctx.command_path, option=self.ctx.help_option_names[0]83 )84 hint = f"{hint}\n"85 if self.ctx is not None:86 color = self.ctx.color87 echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color)88 echo(89 _("Error: {message}").format(message=self.format_message()),90 file=file,91 color=color,92 )93 94 95class BadParameter(UsageError):96 """An exception that formats out a standardized error message for a97 bad parameter. This is useful when thrown from a callback or type as98 Click will attach contextual information to it (for instance, which99 parameter it is).100 101 .. versionadded:: 2.0102 103 :param param: the parameter object that caused this error. This can104 be left out, and Click will attach this info itself105 if possible.106 :param param_hint: a string that shows up as parameter name. This107 can be used as alternative to `param` in cases108 where custom validation should happen. If it is109 a string it's used as such, if it's a list then110 each item is quoted and separated.111 """112 113 def __init__(114 self,115 message: str,116 ctx: Context | None = None,117 param: Parameter | None = None,118 param_hint: cabc.Sequence[str] | str | None = None,119 ) -> None:120 super().__init__(message, ctx)121 self.param = param122 self.param_hint = param_hint123 124 def format_message(self) -> str:125 if self.param_hint is not None:126 param_hint = self.param_hint127 elif self.param is not None:128 param_hint = self.param.get_error_hint(self.ctx) # type: ignore129 else:130 return _("Invalid value: {message}").format(message=self.message)131 132 return _("Invalid value for {param_hint}: {message}").format(133 param_hint=_join_param_hints(param_hint), message=self.message134 )135 136 137class MissingParameter(BadParameter):138 """Raised if click required an option or argument but it was not139 provided when invoking the script.140 141 .. versionadded:: 4.0142 143 :param param_type: a string that indicates the type of the parameter.144 The default is to inherit the parameter type from145 the given `param`. Valid values are ``'parameter'``,146 ``'option'`` or ``'argument'``.147 """148 149 def __init__(150 self,151 message: str | None = None,152 ctx: Context | None = None,153 param: Parameter | None = None,154 param_hint: cabc.Sequence[str] | str | None = None,155 param_type: str | None = None,156 ) -> None:157 super().__init__(message or "", ctx, param, param_hint)158 self.param_type = param_type159 160 def format_message(self) -> str:161 if self.param_hint is not None:162 param_hint: cabc.Sequence[str] | str | None = self.param_hint163 elif self.param is not None:164 param_hint = self.param.get_error_hint(self.ctx) # type: ignore165 else:166 param_hint = None167 168 param_hint = _join_param_hints(param_hint)169 param_hint = f" {param_hint}" if param_hint else ""170 171 param_type = self.param_type172 if param_type is None and self.param is not None:173 param_type = self.param.param_type_name174 175 msg = self.message176 if self.param is not None:177 msg_extra = self.param.type.get_missing_message(178 param=self.param, ctx=self.ctx179 )180 if msg_extra:181 if msg:182 msg += f". {msg_extra}"183 else:184 msg = msg_extra185 186 msg = f" {msg}" if msg else ""187 188 # Translate param_type for known types.189 if param_type == "argument":190 missing = _("Missing argument")191 elif param_type == "option":192 missing = _("Missing option")193 elif param_type == "parameter":194 missing = _("Missing parameter")195 else:196 missing = _("Missing {param_type}").format(param_type=param_type)197 198 return f"{missing}{param_hint}.{msg}"199 200 def __str__(self) -> str:201 if not self.message:202 param_name = self.param.name if self.param else None203 return _("Missing parameter: {param_name}").format(param_name=param_name)204 else:205 return self.message206 207 208class NoSuchOption(UsageError):209 """Raised if click attempted to handle an option that does not210 exist.211 212 .. versionadded:: 4.0213 """214 215 def __init__(216 self,217 option_name: str,218 message: str | None = None,219 possibilities: cabc.Sequence[str] | None = None,220 ctx: Context | None = None,221 ) -> None:222 if message is None:223 message = _("No such option: {name}").format(name=option_name)224 225 super().__init__(message, ctx)226 self.option_name = option_name227 self.possibilities = possibilities228 229 def format_message(self) -> str:230 if not self.possibilities:231 return self.message232 233 possibility_str = ", ".join(sorted(self.possibilities))234 suggest = ngettext(235 "Did you mean {possibility}?",236 "(Possible options: {possibilities})",237 len(self.possibilities),238 ).format(possibility=possibility_str, possibilities=possibility_str)239 return f"{self.message} {suggest}"240 241 242class BadOptionUsage(UsageError):243 """Raised if an option is generally supplied but the use of the option244 was incorrect. This is for instance raised if the number of arguments245 for an option is not correct.246 247 .. versionadded:: 4.0248 249 :param option_name: the name of the option being used incorrectly.250 """251 252 def __init__(253 self, option_name: str, message: str, ctx: Context | None = None254 ) -> None:255 super().__init__(message, ctx)256 self.option_name = option_name257 258 259class BadArgumentUsage(UsageError):260 """Raised if an argument is generally supplied but the use of the argument261 was incorrect. This is for instance raised if the number of values262 for an argument is not correct.263 264 .. versionadded:: 6.0265 """266 267 268class NoArgsIsHelpError(UsageError):269 def __init__(self, ctx: Context) -> None:270 self.ctx: Context271 super().__init__(ctx.get_help(), ctx=ctx)272 273 def show(self, file: t.IO[t.Any] | None = None) -> None:274 echo(self.format_message(), file=file, err=True, color=self.ctx.color)275 276 277class FileError(ClickException):278 """Raised if a file cannot be opened."""279 280 def __init__(self, filename: str, hint: str | None = None) -> None:281 if hint is None:282 hint = _("unknown error")283 284 super().__init__(hint)285 self.ui_filename: str = format_filename(filename)286 self.filename = filename287 288 def format_message(self) -> str:289 return _("Could not open file {filename!r}: {message}").format(290 filename=self.ui_filename, message=self.message291 )292 293 294class Abort(RuntimeError):295 """An internal signalling exception that signals Click to abort."""296 297 298class Exit(RuntimeError):299 """An exception that indicates that the application should exit with some300 status code.301 302 :param code: the status code to exit with.303 """304 305 __slots__ = ("exit_code",)306 307 def __init__(self, code: int = 0) -> None:308 self.exit_code: int = code309 