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

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

127 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 .utils import echo 

10from .utils import format_filename 

11 

12if t.TYPE_CHECKING: 

13 from .core import Command 

14 from .core import Context 

15 from .core import Parameter 

16 

17 

18def _join_param_hints(param_hint: cabc.Sequence[str] | str | None) -> str | None: 

19 if param_hint is not None and not isinstance(param_hint, str): 

20 return " / ".join(repr(x) for x in param_hint) 

21 

22 return param_hint 

23 

24 

25class ClickException(Exception): 

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

27 

28 #: The exit code for this exception. 

29 exit_code = 1 

30 

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

32 super().__init__(message) 

33 self.message = message 

34 

35 def format_message(self) -> str: 

36 return self.message 

37 

38 def __str__(self) -> str: 

39 return self.message 

40 

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

42 if file is None: 

43 file = get_text_stderr() 

44 

45 echo(_("Error: {message}").format(message=self.format_message()), file=file) 

46 

47 

48class UsageError(ClickException): 

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

50 aborts any further handling. 

51 

52 :param message: the error message to display. 

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

54 fill in the context automatically in some situations. 

55 """ 

56 

57 exit_code = 2 

58 

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

60 super().__init__(message) 

61 self.ctx = ctx 

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

63 

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

65 if file is None: 

66 file = get_text_stderr() 

67 color = None 

68 hint = "" 

69 if ( 

70 self.ctx is not None 

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

72 ): 

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

74 command=self.ctx.command_path, option=self.ctx.help_option_names[0] 

75 ) 

76 hint = f"{hint}\n" 

77 if self.ctx is not None: 

78 color = self.ctx.color 

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

80 echo( 

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

82 file=file, 

83 color=color, 

84 ) 

85 

86 

87class BadParameter(UsageError): 

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

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

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

91 parameter it is). 

92 

93 .. versionadded:: 2.0 

94 

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

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

97 if possible. 

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

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

100 where custom validation should happen. If it is 

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

102 each item is quoted and separated. 

103 """ 

104 

105 def __init__( 

106 self, 

107 message: str, 

108 ctx: Context | None = None, 

109 param: Parameter | None = None, 

110 param_hint: str | None = None, 

111 ) -> None: 

112 super().__init__(message, ctx) 

113 self.param = param 

114 self.param_hint = param_hint 

115 

116 def format_message(self) -> str: 

117 if self.param_hint is not None: 

118 param_hint = self.param_hint 

119 elif self.param is not None: 

120 param_hint = self.param.get_error_hint(self.ctx) # type: ignore 

121 else: 

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

123 

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

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

126 ) 

127 

128 

129class MissingParameter(BadParameter): 

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

131 provided when invoking the script. 

132 

133 .. versionadded:: 4.0 

134 

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

136 The default is to inherit the parameter type from 

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

138 ``'option'`` or ``'argument'``. 

139 """ 

140 

141 def __init__( 

142 self, 

143 message: str | None = None, 

144 ctx: Context | None = None, 

145 param: Parameter | None = None, 

146 param_hint: str | None = None, 

147 param_type: str | None = None, 

148 ) -> None: 

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

150 self.param_type = param_type 

151 

152 def format_message(self) -> str: 

153 if self.param_hint is not None: 

154 param_hint: str | None = self.param_hint 

155 elif self.param is not None: 

156 param_hint = self.param.get_error_hint(self.ctx) # type: ignore 

157 else: 

158 param_hint = None 

159 

160 param_hint = _join_param_hints(param_hint) 

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

162 

163 param_type = self.param_type 

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

165 param_type = self.param.param_type_name 

166 

167 msg = self.message 

168 if self.param is not None: 

169 msg_extra = self.param.type.get_missing_message(self.param) 

170 if msg_extra: 

171 if msg: 

172 msg += f". {msg_extra}" 

173 else: 

174 msg = msg_extra 

175 

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

177 

178 # Translate param_type for known types. 

179 if param_type == "argument": 

180 missing = _("Missing argument") 

181 elif param_type == "option": 

182 missing = _("Missing option") 

183 elif param_type == "parameter": 

184 missing = _("Missing parameter") 

185 else: 

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

187 

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

189 

190 def __str__(self) -> str: 

191 if not self.message: 

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

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

194 else: 

195 return self.message 

196 

197 

198class NoSuchOption(UsageError): 

199 """Raised if click attempted to handle an option that does not 

200 exist. 

201 

202 .. versionadded:: 4.0 

203 """ 

204 

205 def __init__( 

206 self, 

207 option_name: str, 

208 message: str | None = None, 

209 possibilities: cabc.Sequence[str] | None = None, 

210 ctx: Context | None = None, 

211 ) -> None: 

212 if message is None: 

213 message = _("No such option: {name}").format(name=option_name) 

214 

215 super().__init__(message, ctx) 

216 self.option_name = option_name 

217 self.possibilities = possibilities 

218 

219 def format_message(self) -> str: 

220 if not self.possibilities: 

221 return self.message 

222 

223 possibility_str = ", ".join(sorted(self.possibilities)) 

224 suggest = ngettext( 

225 "Did you mean {possibility}?", 

226 "(Possible options: {possibilities})", 

227 len(self.possibilities), 

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

229 return f"{self.message} {suggest}" 

230 

231 

232class BadOptionUsage(UsageError): 

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

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

235 for an option is not correct. 

236 

237 .. versionadded:: 4.0 

238 

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

240 """ 

241 

242 def __init__( 

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

244 ) -> None: 

245 super().__init__(message, ctx) 

246 self.option_name = option_name 

247 

248 

249class BadArgumentUsage(UsageError): 

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

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

252 for an argument is not correct. 

253 

254 .. versionadded:: 6.0 

255 """ 

256 

257 

258class FileError(ClickException): 

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

260 

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

262 if hint is None: 

263 hint = _("unknown error") 

264 

265 super().__init__(hint) 

266 self.ui_filename: str = format_filename(filename) 

267 self.filename = filename 

268 

269 def format_message(self) -> str: 

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

271 filename=self.ui_filename, message=self.message 

272 ) 

273 

274 

275class Abort(RuntimeError): 

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

277 

278 

279class Exit(RuntimeError): 

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

281 status code. 

282 

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

284 """ 

285 

286 __slots__ = ("exit_code",) 

287 

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

289 self.exit_code: int = code