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

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

171 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: t.ClassVar[int] = 1 

40 

41 show_color: t.Final[bool | None] 

42 message: t.Final[str] 

43 

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

45 super().__init__(message) 

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

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

48 self.show_color = resolve_color_default() 

49 self.message = message 

50 

51 def format_message(self) -> str: 

52 return self.message 

53 

54 def __str__(self) -> str: 

55 return self.message 

56 

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

58 if file is None: 

59 file = get_text_stderr() 

60 

61 echo( 

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

63 file=file, 

64 color=self.show_color, 

65 ) 

66 

67 

68class UsageError(ClickException): 

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

70 aborts any further handling. 

71 

72 :param message: the error message to display. 

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

74 fill in the context automatically in some situations. 

75 """ 

76 

77 exit_code: t.ClassVar[int] = 2 

78 

79 ctx: Context | None 

80 cmd: t.Final[Command | None] 

81 

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

83 super().__init__(message) 

84 self.ctx = ctx 

85 self.cmd = self.ctx.command if self.ctx else None 

86 

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

88 if file is None: 

89 file = get_text_stderr() 

90 color = None 

91 hint = "" 

92 if ( 

93 self.ctx is not None 

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

95 ): 

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

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

98 # readability in error messages. 

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

100 command=self.ctx.command_path, 

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

102 ) 

103 hint = f"{hint}\n" 

104 if self.ctx is not None: 

105 color = self.ctx.color 

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

107 echo( 

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

109 file=file, 

110 color=color, 

111 ) 

112 

113 

114class BadParameter(UsageError): 

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

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

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

118 parameter it is). 

119 

120 .. versionadded:: 2.0 

121 

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

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

124 if possible. 

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

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

127 where custom validation should happen. If it is 

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

129 each item is quoted and separated. 

130 """ 

131 

132 param: Parameter | None 

133 param_hint: cabc.Sequence[str] | str | None 

134 

135 def __init__( 

136 self, 

137 message: str, 

138 ctx: Context | None = None, 

139 param: Parameter | None = None, 

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

141 ) -> None: 

142 super().__init__(message, ctx) 

143 self.param = param 

144 self.param_hint = param_hint 

145 

146 def format_message(self) -> str: 

147 if self.param_hint is not None: 

148 param_hint = self.param_hint 

149 elif self.param is not None: 

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

151 else: 

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

153 

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

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

156 ) 

157 

158 

159class MissingParameter(BadParameter): 

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

161 provided when invoking the script. 

162 

163 .. versionadded:: 4.0 

164 

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

166 The default is to inherit the parameter type from 

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

168 ``'option'`` or ``'argument'``. 

169 """ 

170 

171 param_type: t.Final[str | None] 

172 

173 def __init__( 

174 self, 

175 message: str | None = None, 

176 ctx: Context | None = None, 

177 param: Parameter | None = None, 

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

179 param_type: str | None = None, 

180 ) -> None: 

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

182 self.param_type = param_type 

183 

184 def format_message(self) -> str: 

185 if self.param_hint is not None: 

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

187 elif self.param is not None: 

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

189 else: 

190 param_hint = None 

191 

192 param_hint = _join_param_hints(param_hint) 

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

194 

195 param_type = self.param_type 

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

197 param_type = self.param.param_type_name 

198 

199 msg = self.message 

200 if self.param is not None: 

201 msg_extra = self.param.type.get_missing_message( 

202 param=self.param, ctx=self.ctx 

203 ) 

204 if msg_extra: 

205 if msg: 

206 msg += f". {msg_extra}" 

207 else: 

208 msg = msg_extra 

209 

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

211 

212 # Translate param_type for known types. 

213 if param_type == "argument": 

214 missing = _("Missing argument") 

215 elif param_type == "option": 

216 missing = _("Missing option") 

217 elif param_type == "parameter": 

218 missing = _("Missing parameter") 

219 else: 

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

221 

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

223 

224 def __str__(self) -> str: 

225 if not self.message: 

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

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

228 else: 

229 return self.message 

230 

231 

232class NoSuchOption(UsageError): 

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

234 

235 .. versionadded:: 4.0 

236 """ 

237 

238 option_name: t.Final[str] 

239 possibilities: t.Final[list[str] | None] 

240 

241 def __init__( 

242 self, 

243 option_name: str, 

244 message: str | None = None, 

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

246 ctx: Context | None = None, 

247 ) -> None: 

248 if message is None: 

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

250 

251 super().__init__(message, ctx) 

252 self.option_name = option_name 

253 

254 if possibilities: 

255 from difflib import get_close_matches 

256 

257 possibilities_ = get_close_matches(option_name, possibilities) 

258 else: 

259 possibilities_ = None 

260 self.possibilities = possibilities_ 

261 

262 def format_message(self) -> str: 

263 if not self.possibilities: 

264 return self.message 

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

266 

267 

268class NoSuchCommand(UsageError): 

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

270 

271 .. versionadded:: 8.4.0 

272 """ 

273 

274 command_name: t.Final[str] 

275 possibilities: t.Final[list[str] | None] 

276 

277 def __init__( 

278 self, 

279 command_name: str, 

280 message: str | None = None, 

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

282 ctx: Context | None = None, 

283 ) -> None: 

284 if message is None: 

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

286 

287 super().__init__(message, ctx) 

288 self.command_name = command_name 

289 

290 if possibilities: 

291 from difflib import get_close_matches 

292 

293 possibilities_ = get_close_matches(command_name, possibilities) 

294 else: 

295 possibilities_ = None 

296 self.possibilities = possibilities_ 

297 

298 def format_message(self) -> str: 

299 if not self.possibilities: 

300 return self.message 

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

302 

303 

304class BadOptionUsage(UsageError): 

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

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

307 for an option is not correct. 

308 

309 .. versionadded:: 4.0 

310 

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

312 """ 

313 

314 option_name: t.Final[str] 

315 

316 def __init__( 

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

318 ) -> None: 

319 super().__init__(message, ctx) 

320 self.option_name = option_name 

321 

322 

323class BadArgumentUsage(UsageError): 

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

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

326 for an argument is not correct. 

327 

328 .. versionadded:: 6.0 

329 """ 

330 

331 

332class NoArgsIsHelpError(UsageError): 

333 ctx: Context 

334 

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

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

337 

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

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

340 

341 

342class FileError(ClickException): 

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

344 

345 ui_filename: t.Final[str] 

346 filename: t.Final[str] 

347 

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

349 if hint is None: 

350 hint = _("unknown error") 

351 

352 super().__init__(hint) 

353 self.ui_filename = format_filename(filename) 

354 self.filename = filename 

355 

356 def format_message(self) -> str: 

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

358 filename=self.ui_filename, message=self.message 

359 ) 

360 

361 

362class Abort(RuntimeError): 

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

364 

365 

366class Exit(RuntimeError): 

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

368 status code. 

369 

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

371 """ 

372 

373 __slots__ = ("exit_code",) 

374 

375 exit_code: t.Final[int] 

376 

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

378 self.exit_code = code