Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/click/exceptions.py: 40%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

154 statements  

1from __future__ import annotations 

2 

3import collections.abc as cabc 

4import typing as t 

5from gettext import gettext as _ 

6from gettext import ngettext 

7 

8from ._compat import get_text_stderr 

9from .globals import resolve_color_default 

10from .utils import echo 

11from .utils import format_filename 

12 

13if t.TYPE_CHECKING: 

14 from .core import Command 

15 from .core import Context 

16 from .core import Parameter 

17 

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_hint 

24 

25 

26def _format_possibilities(possibilities: list[str]) -> str: 

27 possibility_str = ", ".join(repr(p) for p in sorted(possibilities)) 

28 return ngettext( 

29 "Did you mean {possibility}?", 

30 "(Did you mean one of: {possibilities}?)", 

31 len(possibilities), 

32 ).format(possibility=possibility_str, possibilities=possibility_str) 

33 

34 

35class ClickException(Exception): 

36 """An exception that Click can handle and show to the user.""" 

37 

38 #: The exit code for this exception. 

39 exit_code = 1 

40 

41 def __init__(self, message: str) -> None: 

42 super().__init__(message) 

43 # The context will be removed by the time we print the message, so cache 

44 # the color settings here to be used later on (in `show`) 

45 self.show_color: bool | None = resolve_color_default() 

46 self.message = message 

47 

48 def format_message(self) -> str: 

49 return self.message 

50 

51 def __str__(self) -> str: 

52 return self.message 

53 

54 def show(self, file: t.IO[t.Any] | None = None) -> None: 

55 if file is None: 

56 file = get_text_stderr() 

57 

58 echo( 

59 _("Error: {message}").format(message=self.format_message()), 

60 file=file, 

61 color=self.show_color, 

62 ) 

63 

64 

65class UsageError(ClickException): 

66 """An internal exception that signals a usage error. This typically 

67 aborts any further handling. 

68 

69 :param message: the error message to display. 

70 :param ctx: optionally the context that caused this error. Click will 

71 fill in the context automatically in some situations. 

72 """ 

73 

74 exit_code = 2 

75 

76 def __init__(self, message: str, ctx: Context | None = None) -> None: 

77 super().__init__(message) 

78 self.ctx = ctx 

79 self.cmd: Command | None = self.ctx.command if self.ctx else None 

80 

81 def show(self, file: t.IO[t.Any] | None = None) -> None: 

82 if file is None: 

83 file = get_text_stderr() 

84 color = None 

85 hint = "" 

86 if ( 

87 self.ctx is not None 

88 and self.ctx.command.get_help_option(self.ctx) is not None 

89 ): 

90 help_names = self.ctx.command.get_help_option_names(self.ctx) 

91 # Pick the longest name (like ``--help`` over ``-h``) for 

92 # readability in error messages. 

93 hint = _("Try '{command} {option}' for help.").format( 

94 command=self.ctx.command_path, 

95 option=max(help_names, key=len), 

96 ) 

97 hint = f"{hint}\n" 

98 if self.ctx is not None: 

99 color = self.ctx.color 

100 echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color) 

101 echo( 

102 _("Error: {message}").format(message=self.format_message()), 

103 file=file, 

104 color=color, 

105 ) 

106 

107 

108class BadParameter(UsageError): 

109 """An exception that formats out a standardized error message for a 

110 bad parameter. This is useful when thrown from a callback or type as 

111 Click will attach contextual information to it (for instance, which 

112 parameter it is). 

113 

114 .. versionadded:: 2.0 

115 

116 :param param: the parameter object that caused this error. This can 

117 be left out, and Click will attach this info itself 

118 if possible. 

119 :param param_hint: a string that shows up as parameter name. This 

120 can be used as alternative to `param` in cases 

121 where custom validation should happen. If it is 

122 a string it's used as such, if it's a list then 

123 each item is quoted and separated. 

124 """ 

125 

126 def __init__( 

127 self, 

128 message: str, 

129 ctx: Context | None = None, 

130 param: Parameter | None = None, 

131 param_hint: cabc.Sequence[str] | str | None = None, 

132 ) -> None: 

133 super().__init__(message, ctx) 

134 self.param = param 

135 self.param_hint = param_hint 

136 

137 def format_message(self) -> str: 

138 if self.param_hint is not None: 

139 param_hint = self.param_hint 

140 elif self.param is not None: 

141 param_hint = self.param.get_error_hint(self.ctx) 

142 else: 

143 return _("Invalid value: {message}").format(message=self.message) 

144 

145 return _("Invalid value for {param_hint}: {message}").format( 

146 param_hint=_join_param_hints(param_hint), message=self.message 

147 ) 

148 

149 

150class MissingParameter(BadParameter): 

151 """Raised if click required an option or argument but it was not 

152 provided when invoking the script. 

153 

154 .. versionadded:: 4.0 

155 

156 :param param_type: a string that indicates the type of the parameter. 

157 The default is to inherit the parameter type from 

158 the given `param`. Valid values are ``'parameter'``, 

159 ``'option'`` or ``'argument'``. 

160 """ 

161 

162 def __init__( 

163 self, 

164 message: str | None = None, 

165 ctx: Context | None = None, 

166 param: Parameter | None = None, 

167 param_hint: cabc.Sequence[str] | str | None = None, 

168 param_type: str | None = None, 

169 ) -> None: 

170 super().__init__(message or "", ctx, param, param_hint) 

171 self.param_type = param_type 

172 

173 def format_message(self) -> str: 

174 if self.param_hint is not None: 

175 param_hint: cabc.Sequence[str] | str | None = self.param_hint 

176 elif self.param is not None: 

177 param_hint = self.param.get_error_hint(self.ctx) 

178 else: 

179 param_hint = None 

180 

181 param_hint = _join_param_hints(param_hint) 

182 param_hint = f" {param_hint}" if param_hint else "" 

183 

184 param_type = self.param_type 

185 if param_type is None and self.param is not None: 

186 param_type = self.param.param_type_name 

187 

188 msg = self.message 

189 if self.param is not None: 

190 msg_extra = self.param.type.get_missing_message( 

191 param=self.param, ctx=self.ctx 

192 ) 

193 if msg_extra: 

194 if msg: 

195 msg += f". {msg_extra}" 

196 else: 

197 msg = msg_extra 

198 

199 msg = f" {msg}" if msg else "" 

200 

201 # Translate param_type for known types. 

202 if param_type == "argument": 

203 missing = _("Missing argument") 

204 elif param_type == "option": 

205 missing = _("Missing option") 

206 elif param_type == "parameter": 

207 missing = _("Missing parameter") 

208 else: 

209 missing = _("Missing {param_type}").format(param_type=param_type) 

210 

211 return f"{missing}{param_hint}.{msg}" 

212 

213 def __str__(self) -> str: 

214 if not self.message: 

215 param_name = self.param.name if self.param else None 

216 return _("Missing parameter: {param_name}").format(param_name=param_name) 

217 else: 

218 return self.message 

219 

220 

221class NoSuchOption(UsageError): 

222 """Raised if Click attempted to handle an option that does not exist. 

223 

224 .. versionadded:: 4.0 

225 """ 

226 

227 def __init__( 

228 self, 

229 option_name: str, 

230 message: str | None = None, 

231 possibilities: cabc.Iterable[str] | None = None, 

232 ctx: Context | None = None, 

233 ) -> None: 

234 if message is None: 

235 message = _("No such option {name!r}.").format(name=option_name) 

236 

237 super().__init__(message, ctx) 

238 self.option_name = option_name 

239 self.possibilities: list[str] | None = None 

240 if possibilities: 

241 from difflib import get_close_matches 

242 

243 self.possibilities = get_close_matches(option_name, possibilities) 

244 

245 def format_message(self) -> str: 

246 if not self.possibilities: 

247 return self.message 

248 return f"{self.message} {_format_possibilities(self.possibilities)}" 

249 

250 

251class NoSuchCommand(UsageError): 

252 """Raised if Click attempted to handle a command that does not exist.""" 

253 

254 def __init__( 

255 self, 

256 command_name: str, 

257 message: str | None = None, 

258 possibilities: cabc.Iterable[str] | None = None, 

259 ctx: Context | None = None, 

260 ) -> None: 

261 if message is None: 

262 message = _("No such command {name!r}.").format(name=command_name) 

263 

264 super().__init__(message, ctx) 

265 self.command_name = command_name 

266 self.possibilities: list[str] | None = None 

267 if possibilities: 

268 from difflib import get_close_matches 

269 

270 self.possibilities = get_close_matches(command_name, possibilities) 

271 

272 def format_message(self) -> str: 

273 if not self.possibilities: 

274 return self.message 

275 return f"{self.message} {_format_possibilities(self.possibilities)}" 

276 

277 

278class BadOptionUsage(UsageError): 

279 """Raised if an option is generally supplied but the use of the option 

280 was incorrect. This is for instance raised if the number of arguments 

281 for an option is not correct. 

282 

283 .. versionadded:: 4.0 

284 

285 :param option_name: the name of the option being used incorrectly. 

286 """ 

287 

288 def __init__( 

289 self, option_name: str, message: str, ctx: Context | None = None 

290 ) -> None: 

291 super().__init__(message, ctx) 

292 self.option_name = option_name 

293 

294 

295class BadArgumentUsage(UsageError): 

296 """Raised if an argument is generally supplied but the use of the argument 

297 was incorrect. This is for instance raised if the number of values 

298 for an argument is not correct. 

299 

300 .. versionadded:: 6.0 

301 """ 

302 

303 

304class NoArgsIsHelpError(UsageError): 

305 def __init__(self, ctx: Context) -> None: 

306 self.ctx: Context 

307 super().__init__(ctx.get_help(), ctx=ctx) 

308 

309 def show(self, file: t.IO[t.Any] | None = None) -> None: 

310 echo(self.format_message(), file=file, err=True, color=self.ctx.color) 

311 

312 

313class FileError(ClickException): 

314 """Raised if a file cannot be opened.""" 

315 

316 def __init__(self, filename: str, hint: str | None = None) -> None: 

317 if hint is None: 

318 hint = _("unknown error") 

319 

320 super().__init__(hint) 

321 self.ui_filename: str = format_filename(filename) 

322 self.filename = filename 

323 

324 def format_message(self) -> str: 

325 return _("Could not open file {filename!r}: {message}").format( 

326 filename=self.ui_filename, message=self.message 

327 ) 

328 

329 

330class Abort(RuntimeError): 

331 """An internal signalling exception that signals Click to abort.""" 

332 

333 

334class Exit(RuntimeError): 

335 """An exception that indicates that the application should exit with some 

336 status code. 

337 

338 :param code: the status code to exit with. 

339 """ 

340 

341 __slots__ = ("exit_code",) 

342 

343 def __init__(self, code: int = 0) -> None: 

344 self.exit_code: int = code