Coverage for typer / _click / exceptions.py: 100%
119 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-11-09 00:15 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-11-09 00:15 +0000
1from collections.abc import Sequence 1abcdefg
2from typing import IO, TYPE_CHECKING, Any, Union 1abcdefg
4from ..exceptions import TyperException 1abcdefg
5from ._compat import get_text_stderr 1abcdefg
6from .globals import resolve_color_default 1abcdefg
7from .utils import _escape_control_characters, echo, format_filename 1abcdefg
9if TYPE_CHECKING: 1abcdefg
10 from .core import Command, Context, Parameter
13def _join_param_hints(param_hint: Sequence[str] | str | None) -> str | None: 1abcdefg
14 if param_hint is not None and not isinstance(param_hint, str): 1abcdefg
15 return " / ".join(repr(x) for x in param_hint) 1abcdefg
17 return param_hint 1abcdefg
20class ClickException(TyperException): 1abcdefg
21 """An exception that Click can handle and show to the user."""
23 def __init__(self, message: str) -> None: 1abcdefg
24 super().__init__(message) 1abcdefg
25 # The context will be removed by the time we print the message, so cache
26 # the color settings here to be used later on (in `show`)
27 self.show_color: bool | None = resolve_color_default() 1abcdefg
29 def show(self, file: IO[Any] | None = None) -> None: 1abcdefg
30 if file is None: 1abcdefg
31 file = get_text_stderr() 1abcdefg
33 echo( 1abcdefg
34 f"Error: {self.format_message()}",
35 file=file,
36 color=self.show_color,
37 )
40class UsageError(ClickException): 1abcdefg
41 """An internal exception that signals a usage error. This typically
42 aborts any further handling.
43 """
45 exit_code = 2 1abcdefg
47 def __init__(self, message: str, ctx: Union["Context", None] = None) -> None: 1abcdefg
48 super().__init__(message) 1abcdefg
49 self.ctx = ctx 1abcdefg
50 self.cmd: Command | None = self.ctx.command if self.ctx else None 1abcdefg
52 def show(self, file: IO[Any] | None = None) -> None: 1abcdefg
53 if file is None: 1abcdefg
54 file = get_text_stderr() 1abcdefg
55 color = None 1abcdefg
56 hint = "" 1abcdefg
57 if (
58 self.ctx is not None
59 and self.ctx.command.get_help_option(self.ctx) is not None
60 ):
61 command = self.ctx.command_path 1abcdefg
62 option = self.ctx.help_option_names[0] 1abcdefg
63 hint = f"Try '{command} {option}' for help.\n" 1abcdefg
64 if self.ctx is not None: 1abcdefg
65 color = self.ctx.color 1abcdefg
66 echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color) 1abcdefg
67 echo( 1abcdefg
68 f"Error: {self.format_message()}",
69 file=file,
70 color=color,
71 )
74class BadParameter(UsageError): 1abcdefg
75 """An exception that formats out a standardized error message for a
76 bad parameter. This is useful when thrown from a callback or type as
77 Click will attach contextual information to it (for instance, which
78 parameter it is).
79 """
81 def __init__( 1abcdefg
82 self,
83 message: str,
84 ctx: Union["Context", None] = None,
85 param: Union["Parameter", None] = None,
86 param_hint: Sequence[str] | str | None = None,
87 ) -> None:
88 super().__init__(message, ctx) 1abcdefg
89 self.param = param 1abcdefg
90 self.param_hint = param_hint 1abcdefg
92 def format_message(self) -> str: 1abcdefg
93 if self.param_hint is not None: 1abcdefg
94 param_hint = self.param_hint 1abcdefg
95 elif self.param is not None: 1abcdefg
96 param_hint = self.param.get_error_hint(self.ctx) # type: ignore 1abcdefg
97 else:
98 return f"Invalid value: {self.message}" 1abcdefg
100 hint = _join_param_hints(param_hint) 1abcdefg
101 return f"Invalid value for {hint}: {self.message}" 1abcdefg
104class MissingParameter(BadParameter): 1abcdefg
105 """Raised if click required an option or argument but it was not
106 provided when invoking the script.
107 """
109 def __init__( 1abcdefg
110 self,
111 message: str | None = None,
112 ctx: Union["Context", None] = None,
113 param: Union["Parameter", None] = None,
114 param_hint: Sequence[str] | str | None = None,
115 param_type: str | None = None,
116 ) -> None:
117 super().__init__(message or "", ctx, param, param_hint) 1abcdefg
118 self.param_type = param_type 1abcdefg
120 def format_message(self) -> str: 1abcdefg
121 if self.param_hint is not None: 1abcdefg
122 param_hint: Sequence[str] | str | None = self.param_hint 1abcdefg
123 elif self.param is not None: 1abcdefg
124 param_hint = self.param.get_error_hint(self.ctx) # type: ignore 1abcdefg
125 else:
126 param_hint = None 1abcdefg
128 param_hint = _join_param_hints(param_hint) 1abcdefg
129 param_hint = f" {param_hint}" if param_hint else "" 1abcdefg
131 param_type = self.param_type 1abcdefg
132 if param_type is None and self.param is not None: 1abcdefg
133 param_type = self.param.param_type_name 1abcdefg
135 msg = self.message 1abcdefg
136 if self.param is not None: 1abcdefg
137 msg_extra = self.param.type.get_missing_message( 1abcdefg
138 param=self.param, ctx=self.ctx
139 )
140 if msg_extra: 1abcdefg
141 if msg: 1abcdefg
142 msg += f". {msg_extra}" 1abcdefg
143 else:
144 msg = msg_extra 1abcdefg
146 msg = f" {msg}" if msg else "" 1abcdefg
148 # Translate param_type for known types.
149 if param_type == "argument": 1abcdefg
150 missing = "Missing argument" 1abcdefg
151 elif param_type == "option": 1abcdefg
152 missing = "Missing option" 1abcdefg
153 elif param_type == "parameter": 1abcdefg
154 missing = "Missing parameter" 1abcdefg
155 else:
156 missing = f"Missing {param_type}" 1abcdefg
158 return f"{missing}{param_hint}.{msg}" 1abcdefg
160 def __str__(self) -> str: 1abcdefg
161 if not self.message: 1abcdefg
162 param_name = self.param.name if self.param else None 1abcdefg
163 return f"Missing parameter: {param_name}" 1abcdefg
164 else:
165 return self.message 1abcdefg
168class NoSuchOption(UsageError): 1abcdefg
169 """Raised if click attempted to handle an option that does not
170 exist.
171 """
173 def __init__( 1abcdefg
174 self,
175 option_name: str,
176 message: str | None = None,
177 possibilities: Sequence[str] | None = None,
178 ctx: Union["Context", None] = None,
179 ) -> None:
180 if message is None: 1abcdefg
181 message = f"No such option: {_escape_control_characters(option_name)}" 1abcdefg
183 super().__init__(message, ctx) 1abcdefg
184 self.option_name = option_name 1abcdefg
185 self.possibilities = possibilities 1abcdefg
187 def format_message(self) -> str: 1abcdefg
188 if not self.possibilities: 1abcdefg
189 return self.message 1abcdefg
191 possibility_str = ", ".join(sorted(self.possibilities)) 1abcdefg
192 suggest = f"(Possible options: {possibility_str})" 1abcdefg
193 return f"{self.message} {suggest}" 1abcdefg
196class BadOptionUsage(UsageError): 1abcdefg
197 """Raised if an option is generally supplied but the use of the option
198 was incorrect. This is for instance raised if the number of arguments
199 for an option is not correct.
200 """
202 def __init__( 1abcdefg
203 self, option_name: str, message: str, ctx: Union["Context", None] = None
204 ) -> None:
205 super().__init__(message, ctx) 1abcdefg
206 self.option_name = option_name 1abcdefg
209class BadArgumentUsage(UsageError): 1abcdefg
210 """Raised if an argument is generally supplied but the use of the argument
211 was incorrect. This is for instance raised if the number of values
212 for an argument is not correct.
213 """
216class NoArgsIsHelpError(UsageError): 1abcdefg
217 def __init__(self, ctx: "Context") -> None: 1abcdefg
218 self.ctx: Context 1abcdefg
219 super().__init__(ctx.get_help(), ctx=ctx) 1abcdefg
221 def show(self, file: IO[Any] | None = None) -> None: 1abcdefg
222 echo(self.format_message(), file=file, err=True, color=self.ctx.color) 1abcdefg
225class FileError(ClickException): 1abcdefg
226 """Raised if a file cannot be opened."""
228 def __init__(self, filename: str, hint: str | None = None) -> None: 1abcdefg
229 if hint is None: 1abcdefg
230 hint = "unknown error" 1abcdefg
232 super().__init__(hint) 1abcdefg
233 self.ui_filename: str = format_filename(filename) 1abcdefg
234 self.filename = filename 1abcdefg
236 def format_message(self) -> str: 1abcdefg
237 return f"Could not open file {self.ui_filename!r}: {self.message}" 1abcdefg